From ce778be8958848b6b2a9424def02c8e030701d41 Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Mon, 11 Mar 2024 15:39:47 -0400 Subject: [PATCH] Resume call PIP on app foreground. --- .../securesms/WebRtcCallActivity.java | 26 +++++++-- .../securesms/events/WebRtcViewModel.kt | 5 +- .../service/webrtc/SignalCallManager.java | 57 ++++++++++++++++--- 3 files changed, 76 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/WebRtcCallActivity.java b/app/src/main/java/org/thoughtcrime/securesms/WebRtcCallActivity.java index f201a748a5..e5c34a8f92 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/WebRtcCallActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/WebRtcCallActivity.java @@ -137,6 +137,7 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan public static final String EXTRA_ENABLE_VIDEO_IF_AVAILABLE = WebRtcCallActivity.class.getCanonicalName() + ".ENABLE_VIDEO_IF_AVAILABLE"; public static final String EXTRA_STARTED_FROM_FULLSCREEN = WebRtcCallActivity.class.getCanonicalName() + ".STARTED_FROM_FULLSCREEN"; public static final String EXTRA_STARTED_FROM_CALL_LINK = WebRtcCallActivity.class.getCanonicalName() + ".STARTED_FROM_CALL_LINK"; + public static final String EXTRA_LAUNCH_IN_PIP = WebRtcCallActivity.class.getCanonicalName() + ".STARTED_FROM_CALL_LINK"; private CallParticipantsListUpdatePopupWindow participantUpdateWindow; private CallStateUpdatePopupWindow callStateUpdatePopupWindow; @@ -159,6 +160,8 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan private LifecycleDisposable lifecycleDisposable; private long lastCallLinkDisconnectDialogShowTime; private ControlsAndInfoController controlsAndInfo; + private boolean enterPipOnResume; + private long lastProcessedIntentTimestamp; private Disposable ephemeralStateDisposable = Disposable.empty(); @@ -264,6 +267,11 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan } }, TimeUnit.SECONDS.toMillis(1)); } + + if (enterPipOnResume) { + enterPipOnResume = false; + enterPipModeIfPossible(); + } } @Override @@ -303,8 +311,12 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan if (!viewModel.isCallStarting()) { CallParticipantsState state = viewModel.getCallParticipantsStateSnapshot(); - if (state != null && state.getCallState().isPreJoinOrNetworkUnavailable()) { - ApplicationDependencies.getSignalCallManager().cancelPreJoin(); + if (state != null) { + if (state.getCallState().isPreJoinOrNetworkUnavailable()) { + ApplicationDependencies.getSignalCallManager().cancelPreJoin(); + } else if (state.getCallState().getInOngoingCall() && isInPipMode()) { + ApplicationDependencies.getSignalCallManager().relaunchPipOnForeground(); + } } } } @@ -363,6 +375,7 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan Log.d(TAG, "Intent: Action: " + intent.getAction()); Log.d(TAG, "Intent: EXTRA_STARTED_FROM_FULLSCREEN: " + intent.getBooleanExtra(EXTRA_STARTED_FROM_FULLSCREEN, false)); Log.d(TAG, "Intent: EXTRA_ENABLE_VIDEO_IF_AVAILABLE: " + intent.getBooleanExtra(EXTRA_ENABLE_VIDEO_IF_AVAILABLE, false)); + Log.d(TAG, "Intent: EXTRA_LAUNCH_IN_PIP: " + intent.getBooleanExtra(EXTRA_LAUNCH_IN_PIP, false)); } private void processIntent(@NonNull Intent intent) { @@ -375,6 +388,12 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan } else if (END_CALL_ACTION.equals(intent.getAction())) { handleEndCall(); } + + if (System.currentTimeMillis() - lastProcessedIntentTimestamp > TimeUnit.SECONDS.toMillis(1)) { + enterPipOnResume = intent.getBooleanExtra(EXTRA_LAUNCH_IN_PIP, false); + } + + lastProcessedIntentTimestamp = System.currentTimeMillis(); } private void initializePendingParticipantFragmentListener() { @@ -853,8 +872,7 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan } private boolean isSystemPipEnabledAndAvailable() { - return Build.VERSION.SDK_INT >= 26 && - getPackageManager().hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE); + return Build.VERSION.SDK_INT >= 26 && getPackageManager().hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE); } private void delayedFinish() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/events/WebRtcViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/events/WebRtcViewModel.kt index 9e9180db2f..058714ee55 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/events/WebRtcViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/events/WebRtcViewModel.kt @@ -47,7 +47,10 @@ class WebRtcViewModel(state: WebRtcServiceState) { get() = this == CALL_PRE_JOIN || this == NETWORK_FAILURE val isPassedPreJoin: Boolean - get() = ordinal > ordinal + get() = ordinal > CALL_PRE_JOIN.ordinal + + val inOngoingCall: Boolean + get() = this == CALL_INCOMING || this == CALL_OUTGOING || this == CALL_CONNECTED || this == CALL_RINGING || this == CALL_RECONNECTING } enum class GroupCallState { diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/SignalCallManager.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/SignalCallManager.java index a64adee61a..c02dcb40f1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/SignalCallManager.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/SignalCallManager.java @@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.service.webrtc; import android.app.Application; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; import android.os.Build; import android.os.ResultReceiver; @@ -125,14 +126,14 @@ public final class SignalCallManager implements CallManager.Observer, GroupCall. @Nullable private final CallManager callManager; - private final Context context; - private final ExecutorService serviceExecutor; - private final Executor networkExecutor; - private final LockManager lockManager; + private final Context context; + private final ExecutorService serviceExecutor; + private final Executor networkExecutor; + private final LockManager lockManager; private WebRtcServiceState serviceState; private RxStore ephemeralStateStore; - private boolean needsToSetSelfUuid = true; + private boolean needsToSetSelfUuid = true; private RxStore> linkPeekInfoStore; @@ -182,8 +183,8 @@ public final class SignalCallManager implements CallManager.Observer, GroupCall. } private void process(@NonNull ProcessAction action) { - Throwable t = new Throwable(); - String caller = t.getStackTrace().length > 1 ? t.getStackTrace()[1].getMethodName() : "unknown"; + Throwable t = new Throwable(); + String caller = t.getStackTrace().length > 1 ? t.getStackTrace()[1].getMethodName() : "unknown"; if (callManager == null) { Log.w(TAG, "Unable to process action, call manager is not initialized"); @@ -1151,6 +1152,10 @@ public final class SignalCallManager implements CallManager.Observer, GroupCall. return new SignalCallLinkManager(Objects.requireNonNull(callManager)); } + public void relaunchPipOnForeground() { + ApplicationDependencies.getAppForegroundObserver().addListener(new RelaunchListener(ApplicationDependencies.getAppForegroundObserver().isForegrounded())); + } + private void processSendMessageFailureWithChangeDetection(@NonNull RemotePeer remotePeer, @NonNull ProcessAction failureProcessAction) { @@ -1169,6 +1174,44 @@ public final class SignalCallManager implements CallManager.Observer, GroupCall. }); } + private class RelaunchListener implements AppForegroundObserver.Listener { + private boolean canRelaunch; + + public RelaunchListener(boolean isForegrounded) { + canRelaunch = !isForegrounded; + } + + @Override + public void onForeground() { + if (canRelaunch) { + if (isSystemPipEnabledAndAvailable()) { + process((s, p) -> { + WebRtcViewModel.State callState = s.getCallInfoState().getCallState(); + + if (callState.getInOngoingCall()) { + Intent intent = new Intent(context, WebRtcCallActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(WebRtcCallActivity.EXTRA_LAUNCH_IN_PIP, true); + context.startActivity(intent); + } + + return s; + }); + } + ApplicationDependencies.getAppForegroundObserver().removeListener(this); + } + } + + @Override + public void onBackground() { + canRelaunch = true; + } + + private boolean isSystemPipEnabledAndAvailable() { + return Build.VERSION.SDK_INT >= 26 && context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE); + } + } + interface ProcessAction { @NonNull WebRtcServiceState process(@NonNull WebRtcServiceState currentState, @NonNull WebRtcActionProcessor processor); }