diff --git a/app/src/main/java/org/thoughtcrime/securesms/WebRtcCallActivity.java b/app/src/main/java/org/thoughtcrime/securesms/WebRtcCallActivity.java index acc4b5bbe4..ad288be4b6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/WebRtcCallActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/WebRtcCallActivity.java @@ -141,9 +141,9 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan EventBus.getDefault().unregister(this); } - if (!viewModel.isCallingStarted()) { + if (!viewModel.isCallStarting()) { CallParticipantsState state = viewModel.getCallParticipantsState().getValue(); - if (state != null && state.getCallState() == WebRtcViewModel.State.CALL_PRE_JOIN) { + if (state != null && state.getCallState().isPreJoinOrNetworkUnavailable()) { finish(); } } @@ -156,9 +156,9 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan EventBus.getDefault().unregister(this); - if (!viewModel.isCallingStarted()) { + if (!viewModel.isCallStarting()) { CallParticipantsState state = viewModel.getCallParticipantsState().getValue(); - if (state != null && state.getCallState() == WebRtcViewModel.State.CALL_PRE_JOIN) { + if (state != null && state.getCallState().isPreJoinOrNetworkUnavailable()) { Intent intent = new Intent(this, WebRtcCallService.class); intent.setAction(WebRtcCallService.ACTION_CANCEL_PRE_JOIN_CALL); startService(intent); @@ -471,7 +471,6 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan private void handleServerFailure() { EventBus.getDefault().removeStickyEvent(WebRtcViewModel.class); callScreen.setStatus(getString(R.string.RedPhone_network_failed)); - delayedFinish(); } private void handleNoSuchUser(final @NonNull WebRtcViewModel event) { @@ -529,7 +528,7 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan .putExtra(WebRtcCallService.EXTRA_RECIPIENT_IDS, RecipientId.toSerializedList(changedRecipients)); startService(intent); } else { - startCall(state.getLocalParticipant().isVideoEnabled()); + viewModel.startCall(state.getLocalParticipant().isVideoEnabled()); } } @@ -540,7 +539,7 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan public void onCanceled() { CallParticipantsState state = viewModel.getCallParticipantsState().getValue(); if (state != null && state.getGroupCallState().isNotIdle()) { - if (state.getCallState() == WebRtcViewModel.State.CALL_PRE_JOIN) { + if (state.getCallState().isPreJoinOrNetworkUnavailable()) { Intent intent = new Intent(this, WebRtcCallService.class); intent.setAction(WebRtcCallService.ACTION_CANCEL_PRE_JOIN_CALL); startService(intent); diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallView.java b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallView.java index f5fc5c520d..60f8dd1604 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallView.java @@ -96,6 +96,7 @@ public class WebRtcCallView extends FrameLayout { private TextView participantCount; private Stub groupCallSpeakerHint; private Stub groupCallFullStub; + private View errorButton; private int pagerBottomMarginDp; private boolean controlsVisible = true; @@ -151,6 +152,7 @@ public class WebRtcCallView extends FrameLayout { callParticipantsRecycler = findViewById(R.id.call_screen_participants_recycler); toolbar = findViewById(R.id.call_screen_toolbar); startCall = findViewById(R.id.call_screen_start_call_start_call); + errorButton = findViewById(R.id.call_screen_error_cancel); groupCallSpeakerHint = new Stub<>(findViewById(R.id.call_screen_group_call_speaker_hint)); groupCallFullStub = new Stub<>(findViewById(R.id.group_call_call_full_view)); @@ -227,6 +229,12 @@ public class WebRtcCallView extends FrameLayout { int statusBarHeight = ViewUtil.getStatusBarHeight(this); statusBarGuideline.setGuidelineBegin(statusBarHeight); + + errorButton.setOnClickListener(v -> { + if (controlsListener != null) { + controlsListener.onCancelStartCall(); + } + }); } @Override @@ -426,6 +434,11 @@ public class WebRtcCallView extends FrameLayout { startCall.setEnabled(webRtcControls.isStartCallEnabled()); } + if (webRtcControls.displayErrorControls()) { + visibleViewSet.add(footerGradient); + visibleViewSet.add(errorButton); + } + if (webRtcControls.displayGroupCallFull()) { groupCallFullStub.get().setVisibility(View.VISIBLE); ((TextView) groupCallFullStub.get().findViewById(R.id.group_call_call_full_message)).setText(webRtcControls.getGroupCallFullMessage(getContext())); diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallViewModel.java index c46144be84..ccf6523dad 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallViewModel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallViewModel.java @@ -56,8 +56,8 @@ public class WebRtcCallViewModel extends ViewModel { private boolean answerWithVideoAvailable = false; private Runnable elapsedTimeRunnable = this::handleTick; private boolean canEnterPipMode = false; - private List previousParticipantsList = Collections.emptyList(); - private boolean callingStarted = false; + private List previousParticipantsList = Collections.emptyList(); + private boolean callStarting = false; private final WebRtcCallRepository repository = new WebRtcCallRepository(ApplicationDependencies.getApplication()); @@ -113,8 +113,8 @@ public class WebRtcCallViewModel extends ViewModel { return answerWithVideoAvailable; } - public boolean isCallingStarted() { - return callingStarted; + public boolean isCallStarting() { + return callStarting; } @MainThread @@ -141,7 +141,10 @@ public class WebRtcCallViewModel extends ViewModel { @MainThread public void updateFromWebRtcViewModel(@NonNull WebRtcViewModel webRtcViewModel, boolean enableVideo) { - canEnterPipMode = webRtcViewModel.getState() != WebRtcViewModel.State.CALL_PRE_JOIN; + canEnterPipMode = !webRtcViewModel.getState().isPreJoinOrNetworkUnavailable(); + if (callStarting && webRtcViewModel.getState().isPassedPreJoin()) { + callStarting = false; + } CallParticipant localParticipant = webRtcViewModel.getLocalParticipant(); @@ -232,6 +235,9 @@ public class WebRtcCallViewModel extends ViewModel { case CALL_DISCONNECTED: callState = WebRtcControls.CallState.ENDING; break; + case NETWORK_FAILURE: + callState = WebRtcControls.CallState.ERROR; + break; default: callState = WebRtcControls.CallState.ONGOING; } @@ -309,7 +315,7 @@ public class WebRtcCallViewModel extends ViewModel { } public void startCall(boolean isVideoCall) { - callingStarted = true; + callStarting = true; Recipient recipient = getRecipient().get(); if (recipient.isGroup()) { repository.getIdentityRecords(recipient, identityRecords -> { diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcControls.java b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcControls.java index 2fc4bc0fba..79478ac8fd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcControls.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcControls.java @@ -51,6 +51,10 @@ public final class WebRtcControls { this.participantLimit = participantLimit; } + boolean displayErrorControls() { + return isError(); + } + boolean displayStartCallControls() { return isPreJoin(); } @@ -145,6 +149,10 @@ public final class WebRtcControls { return audioOutput; } + private boolean isError() { + return callState == CallState.ERROR; + } + private boolean isPreJoin() { return callState == CallState.PRE_JOIN; } @@ -167,6 +175,7 @@ public final class WebRtcControls { public enum CallState { NONE, + ERROR, PRE_JOIN, INCOMING, OUTGOING, diff --git a/app/src/main/java/org/thoughtcrime/securesms/events/WebRtcViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/events/WebRtcViewModel.java index 9792765875..00d8f35f1a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/events/WebRtcViewModel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/events/WebRtcViewModel.java @@ -46,6 +46,14 @@ public class WebRtcViewModel { this == NO_SUCH_USER || this == UNTRUSTED_IDENTITY; } + + public boolean isPreJoinOrNetworkUnavailable() { + return this == CALL_PRE_JOIN || this == NETWORK_FAILURE; + } + + public boolean isPassedPreJoin() { + return this.ordinal() > CALL_PRE_JOIN.ordinal(); + } } public enum GroupCallState { diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.java b/app/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.java index 8d21a723b9..f82d6e61f9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.java @@ -6,6 +6,8 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.media.AudioManager; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; import android.os.Build; import android.os.IBinder; import android.os.ResultReceiver; @@ -41,6 +43,7 @@ import org.thoughtcrime.securesms.events.GroupCallPeekEvent; import org.thoughtcrime.securesms.events.WebRtcViewModel; import org.thoughtcrime.securesms.groups.GroupId; import org.thoughtcrime.securesms.groups.GroupManager; +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; import org.thoughtcrime.securesms.jobs.GroupCallUpdateSendJob; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientId; @@ -148,6 +151,7 @@ public class WebRtcCallService extends Service implements CallManager.Observer, public static final String ACTION_SET_MUTE_AUDIO = "SET_MUTE_AUDIO"; public static final String ACTION_FLIP_CAMERA = "FLIP_CAMERA"; public static final String ACTION_BLUETOOTH_CHANGE = "BLUETOOTH_CHANGE"; + public static final String ACTION_NETWORK_CHANGE = "NETWORK_CHANGE"; public static final String ACTION_WIRED_HEADSET_CHANGE = "WIRED_HEADSET_CHANGE"; public static final String ACTION_SCREEN_OFF = "SCREEN_OFF"; public static final String ACTION_IS_IN_CALL_QUERY = "IS_IN_CALL"; @@ -212,6 +216,7 @@ public class WebRtcCallService extends Service implements CallManager.Observer, private SignalServiceAccountManager accountManager; private BluetoothStateManager bluetoothStateManager; private WiredHeadsetStateReceiver wiredHeadsetStateReceiver; + private NetworkReceiver networkReceiver; private PowerButtonReceiver powerButtonReceiver; private LockManager lockManager; private UncaughtExceptionHandlerManager uncaughtExceptionHandlerManager; @@ -241,6 +246,7 @@ public class WebRtcCallService extends Service implements CallManager.Observer, registerUncaughtExceptionHandler(); registerWiredHeadsetStateReceiver(); + registerNetworkReceiver(); TelephonyUtil.getManager(this) .listen(hangUpRtcOnDeviceCallAnswered, PhoneStateListener.LISTEN_CALL_STATE); @@ -328,6 +334,8 @@ public class WebRtcCallService extends Service implements CallManager.Observer, powerButtonReceiver = null; } + unregisterNetworkReceiver(); + TelephonyUtil.getManager(this) .listen(hangUpRtcOnDeviceCallAnswered, PhoneStateListener.LISTEN_NONE); } @@ -371,6 +379,22 @@ public class WebRtcCallService extends Service implements CallManager.Observer, registerReceiver(wiredHeadsetStateReceiver, new IntentFilter(action)); } + private void registerNetworkReceiver() { + if (networkReceiver == null) { + networkReceiver = new NetworkReceiver(); + + registerReceiver(networkReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)); + } + } + + private void unregisterNetworkReceiver() { + if (networkReceiver != null) { + unregisterReceiver(networkReceiver); + + networkReceiver = null; + } + } + public void registerPowerButtonReceiver() { if (powerButtonReceiver == null) { powerButtonReceiver = new PowerButtonReceiver(); @@ -502,6 +526,19 @@ public class WebRtcCallService extends Service implements CallManager.Observer, } } + private static class NetworkReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo(); + Intent serviceIntent = new Intent(context, WebRtcCallService.class); + + serviceIntent.setAction(ACTION_NETWORK_CHANGE); + serviceIntent.putExtra(EXTRA_AVAILABLE, activeNetworkInfo != null && activeNetworkInfo.isConnected()); + context.startService(serviceIntent); + } + } + private static class PowerButtonReceiver extends BroadcastReceiver { @Override public void onReceive(@NonNull Context context, @NonNull Intent intent) { @@ -1057,8 +1094,11 @@ public class WebRtcCallService extends Service implements CallManager.Observer, .putExtra(EXTRA_GROUP_CALL_HASH, groupCall.hashCode()); startService(intent); - } catch (IOException | VerificationFailedException e) { - Log.w(TAG, "Unable to fetch group membership proof", e); + } catch (IOException e) { + Log.w(TAG, "Unable to get group membership proof from service", e); + onEnded(groupCall, GroupCall.GroupCallEndReason.SFU_CLIENT_FAILED_TO_JOIN); + } catch (VerificationFailedException e) { + Log.w(TAG, "Unable to verify group membership proof", e); onEnded(groupCall, GroupCall.GroupCallEndReason.DEVICE_EXPLICITLY_DISCONNECTED); } }); diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupActionProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupActionProcessor.java index 68bab739ac..dabecee8ad 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupActionProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupActionProcessor.java @@ -18,6 +18,7 @@ import org.thoughtcrime.securesms.groups.GroupManager; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.ringrtc.RemotePeer; +import org.thoughtcrime.securesms.service.webrtc.state.VideoState; import org.thoughtcrime.securesms.service.webrtc.state.WebRtcServiceState; import org.thoughtcrime.securesms.service.webrtc.state.WebRtcServiceStateBuilder; import org.thoughtcrime.securesms.webrtc.locks.LockManager; @@ -271,6 +272,28 @@ public class GroupActionProcessor extends DeviceAwareActionProcessor { return groupCallFailure(currentState, "Unable to disconnect from group call", e); } + if (groupCallEndReason != GroupCall.GroupCallEndReason.DEVICE_EXPLICITLY_DISCONNECTED) { + Log.i(tag, "Group call ended unexpectedly, reinitializing and dropping back to lobby"); + Recipient currentRecipient = currentState.getCallInfoState().getCallRecipient(); + VideoState videoState = currentState.getVideoState(); + + currentState = terminateGroupCall(currentState, false).builder() + .actionProcessor(new GroupNetworkUnavailableActionProcessor(webRtcInteractor)) + .changeVideoState() + .eglBase(videoState.getEglBase()) + .camera(videoState.getCamera()) + .localSink(videoState.getLocalSink()) + .commit() + .changeCallInfoState() + .callState(WebRtcViewModel.State.CALL_PRE_JOIN) + .callRecipient(currentRecipient) + .build(); + + currentState = WebRtcVideoUtil.initializeVanityCamera(WebRtcVideoUtil.reinitializeCamera(context, webRtcInteractor.getCameraEventListener(), currentState)); + + return currentState.getActionProcessor().handlePreJoinCall(currentState, new RemotePeer(currentRecipient.getId())); + } + currentState = currentState.builder() .changeCallInfoState() .callState(WebRtcViewModel.State.CALL_DISCONNECTED) @@ -313,6 +336,10 @@ public class GroupActionProcessor extends DeviceAwareActionProcessor { } public synchronized @NonNull WebRtcServiceState terminateGroupCall(@NonNull WebRtcServiceState currentState) { + return terminateGroupCall(currentState, true); + } + + public synchronized @NonNull WebRtcServiceState terminateGroupCall(@NonNull WebRtcServiceState currentState, boolean terminateVideo) { webRtcInteractor.updatePhoneState(LockManager.PhoneState.PROCESSING); webRtcInteractor.stopForegroundService(); boolean playDisconnectSound = currentState.getCallInfoState().getCallState() == WebRtcViewModel.State.CALL_DISCONNECTED; @@ -321,7 +348,9 @@ public class GroupActionProcessor extends DeviceAwareActionProcessor { webRtcInteractor.updatePhoneState(LockManager.PhoneState.IDLE); - WebRtcVideoUtil.deinitializeVideo(currentState); + if (terminateVideo) { + WebRtcVideoUtil.deinitializeVideo(currentState); + } GroupCallSafetyNumberChangeNotificationUtil.cancelNotification(context, currentState.getCallInfoState().getCallRecipient()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupNetworkUnavailableActionProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupNetworkUnavailableActionProcessor.java new file mode 100644 index 0000000000..de15731370 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupNetworkUnavailableActionProcessor.java @@ -0,0 +1,80 @@ +package org.thoughtcrime.securesms.service.webrtc; + +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; + +import androidx.annotation.NonNull; + +import org.signal.core.util.logging.Log; +import org.signal.ringrtc.GroupCall; +import org.thoughtcrime.securesms.BuildConfig; +import org.thoughtcrime.securesms.events.WebRtcViewModel; +import org.thoughtcrime.securesms.keyvalue.SignalStore; +import org.thoughtcrime.securesms.ringrtc.RemotePeer; +import org.thoughtcrime.securesms.service.webrtc.state.WebRtcServiceState; + +/** + * Processor which is utilized when the network becomes unavailable during a group call. In general, + * this is triggered whenever there is a call ended, and the ending was not the result of direct user + * action. + * + * This class will check the network status when handlePreJoinCall is invoked, and transition to + * GroupPreJoinActionProcessor as network becomes available again. + */ +class GroupNetworkUnavailableActionProcessor extends WebRtcActionProcessor { + + private static final String TAG = Log.tag(GroupNetworkUnavailableActionProcessor.class); + + public GroupNetworkUnavailableActionProcessor(@NonNull WebRtcInteractor webRtcInteractor) { + super(webRtcInteractor, TAG); + } + + @Override + protected @NonNull WebRtcServiceState handlePreJoinCall(@NonNull WebRtcServiceState currentState, @NonNull RemotePeer remotePeer) { + Log.i(TAG, "handlePreJoinCall():"); + + ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo(); + + if (activeNetworkInfo != null && activeNetworkInfo.isConnected()) { + GroupPreJoinActionProcessor processor = new GroupPreJoinActionProcessor(webRtcInteractor); + return processor.handlePreJoinCall(currentState.builder().actionProcessor(processor).build(), remotePeer); + } + + byte[] groupId = currentState.getCallInfoState().getCallRecipient().requireGroupId().getDecodedId(); + GroupCall groupCall = webRtcInteractor.getCallManager().createGroupCall(groupId, + BuildConfig.SIGNAL_SFU_URL, + currentState.getVideoState().requireEglBase(), + webRtcInteractor.getGroupCallObserver()); + + return currentState.builder() + .changeCallInfoState() + .callState(WebRtcViewModel.State.NETWORK_FAILURE) + .groupCall(groupCall) + .groupCallState(WebRtcViewModel.GroupCallState.DISCONNECTED) + .build(); + } + + @Override + protected @NonNull WebRtcServiceState handleCancelPreJoinCall(@NonNull WebRtcServiceState currentState) { + Log.i(TAG, "handleCancelPreJoinCall():"); + + WebRtcVideoUtil.deinitializeVideo(currentState); + + return new WebRtcServiceState(new IdleActionProcessor(webRtcInteractor)); + } + + @Override + public @NonNull WebRtcServiceState handleNetworkChanged(@NonNull WebRtcServiceState currentState, boolean available) { + if (available) { + return currentState.builder() + .actionProcessor(new GroupPreJoinActionProcessor(webRtcInteractor)) + .changeCallInfoState() + .callState(WebRtcViewModel.State.CALL_PRE_JOIN) + .build(); + } else { + return currentState; + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupPreJoinActionProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupPreJoinActionProcessor.java index 7bcde5a6b1..3ded6bbf92 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupPreJoinActionProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupPreJoinActionProcessor.java @@ -187,4 +187,17 @@ public class GroupPreJoinActionProcessor extends GroupActionProcessor { .isMicrophoneEnabled(!muted) .build(); } + + @Override + public @NonNull WebRtcServiceState handleNetworkChanged(@NonNull WebRtcServiceState currentState, boolean available) { + if (!available) { + return currentState.builder() + .actionProcessor(new GroupNetworkUnavailableActionProcessor(webRtcInteractor)) + .changeCallInfoState() + .callState(WebRtcViewModel.State.NETWORK_FAILURE) + .build(); + } else { + return currentState; + } + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/IdleActionProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/IdleActionProcessor.java index 61c0b18470..69e70e559e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/IdleActionProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/IdleActionProcessor.java @@ -54,7 +54,7 @@ public class IdleActionProcessor extends WebRtcActionProcessor { WebRtcActionProcessor processor = isGroupCall ? new GroupPreJoinActionProcessor(webRtcInteractor) : new PreJoinActionProcessor(webRtcInteractor); - currentState = initializeVanityCamera(WebRtcVideoUtil.initializeVideo(context, webRtcInteractor.getCameraEventListener(), currentState)); + currentState = WebRtcVideoUtil.initializeVanityCamera(WebRtcVideoUtil.initializeVideo(context, webRtcInteractor.getCameraEventListener(), currentState)); currentState = currentState.builder() .actionProcessor(processor) @@ -66,30 +66,4 @@ public class IdleActionProcessor extends WebRtcActionProcessor { return isGroupCall ? currentState.getActionProcessor().handlePreJoinCall(currentState, remotePeer) : currentState; } - - private @NonNull WebRtcServiceState initializeVanityCamera(@NonNull WebRtcServiceState currentState) { - Camera camera = currentState.getVideoState().requireCamera(); - BroadcastVideoSink sink = currentState.getVideoState().requireLocalSink(); - - if (camera.hasCapturer()) { - camera.initCapturer(new CapturerObserver() { - @Override - public void onFrameCaptured(VideoFrame videoFrame) { - sink.onFrame(videoFrame); - } - - @Override - public void onCapturerStarted(boolean success) {} - - @Override - public void onCapturerStopped() {} - }); - camera.setEnabled(true); - } - - return currentState.builder() - .changeLocalDeviceState() - .cameraState(camera.getCameraState()) - .build(); - } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcActionProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcActionProcessor.java index 58c48ec2cc..6a0dd02489 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcActionProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcActionProcessor.java @@ -77,6 +77,7 @@ import static org.thoughtcrime.securesms.service.WebRtcCallService.ACTION_LOCAL_ import static org.thoughtcrime.securesms.service.WebRtcCallService.ACTION_LOCAL_RINGING; import static org.thoughtcrime.securesms.service.WebRtcCallService.ACTION_MESSAGE_SENT_ERROR; import static org.thoughtcrime.securesms.service.WebRtcCallService.ACTION_MESSAGE_SENT_SUCCESS; +import static org.thoughtcrime.securesms.service.WebRtcCallService.ACTION_NETWORK_CHANGE; import static org.thoughtcrime.securesms.service.WebRtcCallService.ACTION_OUTGOING_CALL; import static org.thoughtcrime.securesms.service.WebRtcCallService.ACTION_PRE_JOIN_CALL; import static org.thoughtcrime.securesms.service.WebRtcCallService.ACTION_RECEIVED_OFFER_EXPIRED; @@ -213,6 +214,7 @@ public abstract class WebRtcActionProcessor { case ACTION_SET_AUDIO_BLUETOOTH: return handleSetBluetoothAudio(currentState, intent.getBooleanExtra(EXTRA_BLUETOOTH, false)); case ACTION_BLUETOOTH_CHANGE: return handleBluetoothChange(currentState, getAvailable(intent)); case ACTION_CAMERA_SWITCH_COMPLETED: return handleCameraSwitchCompleted(currentState, getCameraState(intent)); + case ACTION_NETWORK_CHANGE: return handleNetworkChanged(currentState, getAvailable(intent)); // End Call Actions case ACTION_ENDED_REMOTE_HANGUP: @@ -598,6 +600,11 @@ public abstract class WebRtcActionProcessor { return currentState; } + public @NonNull WebRtcServiceState handleNetworkChanged(@NonNull WebRtcServiceState currentState, boolean available) { + Log.i(tag, "handleNetworkChanged not processed"); + return currentState; + } + //endregion Local device //region End call diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcVideoUtil.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcVideoUtil.java index 8b3352ba7b..7a2fbbb5d5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcVideoUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcVideoUtil.java @@ -11,7 +11,9 @@ import org.thoughtcrime.securesms.ringrtc.CameraState; import org.thoughtcrime.securesms.service.webrtc.state.WebRtcServiceState; import org.thoughtcrime.securesms.service.webrtc.state.WebRtcServiceStateBuilder; import org.thoughtcrime.securesms.util.Util; +import org.webrtc.CapturerObserver; import org.webrtc.EglBase; +import org.webrtc.VideoFrame; /** * Helper for initializing, reinitializing, and deinitializing the camera and it's related @@ -93,4 +95,30 @@ public final class WebRtcVideoUtil { .cameraState(CameraState.UNKNOWN) .build(); } + + public static @NonNull WebRtcServiceState initializeVanityCamera(@NonNull WebRtcServiceState currentState) { + Camera camera = currentState.getVideoState().requireCamera(); + BroadcastVideoSink sink = currentState.getVideoState().requireLocalSink(); + + if (camera.hasCapturer()) { + camera.initCapturer(new CapturerObserver() { + @Override + public void onFrameCaptured(VideoFrame videoFrame) { + sink.onFrame(videoFrame); + } + + @Override + public void onCapturerStarted(boolean success) {} + + @Override + public void onCapturerStopped() {} + }); + camera.setEnabled(true); + } + + return currentState.builder() + .changeLocalDeviceState() + .cameraState(camera.getCameraState()) + .build(); + } } diff --git a/app/src/main/res/layout/webrtc_call_view.xml b/app/src/main/res/layout/webrtc_call_view.xml index a659f1513f..8c6ae8a849 100644 --- a/app/src/main/res/layout/webrtc_call_view.xml +++ b/app/src/main/res/layout/webrtc_call_view.xml @@ -322,6 +322,25 @@ + +