diff options
| author | 2024-05-03 06:30:44 +0000 | |
|---|---|---|
| committer | 2024-05-03 06:30:44 +0000 | |
| commit | 2cea58c19b064fef424cf39755c4cbf2a3898c99 (patch) | |
| tree | c808cccf405e85548bc55eb9c0eeb367723b19dd | |
| parent | d0c90f574e7afb316fbd2d59db979cd94452b607 (diff) | |
| parent | 7476c8afa7649fe552bbe52253e4f9552422651c (diff) | |
Merge "Check if apps are currently in pip before launching split screen" into 24D1-dev
4 files changed, 194 insertions, 24 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java index 32442f740a52..5584f238e131 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java @@ -266,9 +266,9 @@ public abstract class PipTransitionController implements Transitions.TransitionH } /** Whether a particular package is same as current pip package. */ - public boolean isInPipPackage(String packageName) { + public boolean isPackageActiveInPip(String packageName) { final TaskInfo inPipTask = mPipOrganizer.getTaskInfo(); - return packageName != null && inPipTask != null + return packageName != null && inPipTask != null && mPipOrganizer.isInPip() && packageName.equals(SplitScreenUtils.getPackageName(inPipTask.baseIntent)); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index 9dd4c193a006..68c59ee3adf9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -199,7 +199,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private final DisplayImeController mDisplayImeController; private final DisplayInsetsController mDisplayInsetsController; private final TransactionPool mTransactionPool; - private final SplitScreenTransitions mSplitTransitions; + private SplitScreenTransitions mSplitTransitions; private final SplitscreenEventLogger mLogger; private final ShellExecutor mMainExecutor; // Cache live tile tasks while entering recents, evict them from stages in finish transaction @@ -397,6 +397,11 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, return mSplitTransitions; } + @VisibleForTesting + void setSplitTransitions(SplitScreenTransitions splitScreenTransitions) { + mSplitTransitions = splitScreenTransitions; + } + public boolean isSplitScreenVisible() { return mSideStageListener.mVisible && mMainStageListener.mVisible; } @@ -581,7 +586,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, options = resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, null /* wct */); wct.startTask(taskId, options); // If this should be mixed, send the task to avoid split handle transition directly. - if (mMixedHandler != null && mMixedHandler.shouldSplitEnterMixed(taskId, mTaskOrganizer)) { + if (mMixedHandler != null && mMixedHandler.isTaskInPip(taskId, mTaskOrganizer)) { mTaskOrganizer.applyTransaction(wct); return; } @@ -620,7 +625,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, wct.sendPendingIntent(intent, fillInIntent, options); // If this should be mixed, just send the intent to avoid split handle transition directly. - if (mMixedHandler != null && mMixedHandler.shouldSplitEnterMixed(intent)) { + if (mMixedHandler != null && mMixedHandler.isIntentInPip(intent)) { mTaskOrganizer.applyTransaction(wct); return; } @@ -709,16 +714,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, taskId1, taskId2, splitPosition, snapPosition); final WindowContainerTransaction wct = new WindowContainerTransaction(); if (taskId2 == INVALID_TASK_ID) { - if (mMainStage.containsTask(taskId1) || mSideStage.containsTask(taskId1)) { - prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct); - } - if (mRecentTasks.isPresent()) { - mRecentTasks.get().removeSplitPair(taskId1); - } - options1 = options1 != null ? options1 : new Bundle(); - addActivityOptions(options1, null); - wct.startTask(taskId1, options1); - mSplitTransitions.startFullscreenTransition(wct, remoteTransition); + startSingleTask(taskId1, options1, wct, remoteTransition); return; } @@ -739,11 +735,15 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, "startIntentAndTask: intent=%s task1=%d position=%d snapPosition=%d", pendingIntent.getIntent(), taskId, splitPosition, snapPosition); final WindowContainerTransaction wct = new WindowContainerTransaction(); - if (taskId == INVALID_TASK_ID) { - options1 = options1 != null ? options1 : new Bundle(); - addActivityOptions(options1, null); - wct.sendPendingIntent(pendingIntent, fillInIntent, options1); - mSplitTransitions.startFullscreenTransition(wct, remoteTransition); + boolean firstIntentPipped = mMixedHandler.isIntentInPip(pendingIntent); + boolean secondTaskPipped = mMixedHandler.isTaskInPip(taskId, mTaskOrganizer); + if (taskId == INVALID_TASK_ID || secondTaskPipped) { + startSingleIntent(pendingIntent, fillInIntent, options1, wct, remoteTransition); + return; + } + + if (firstIntentPipped) { + startSingleTask(taskId, options2, wct, remoteTransition); return; } @@ -755,6 +755,24 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, startWithTask(wct, taskId, options2, snapPosition, remoteTransition, instanceId); } + /** + * @param taskId Starts this task in fullscreen, removing it from existing pairs if it was part + * of one. + */ + private void startSingleTask(int taskId, Bundle options, WindowContainerTransaction wct, + RemoteTransition remoteTransition) { + if (mMainStage.containsTask(taskId) || mSideStage.containsTask(taskId)) { + prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct); + } + if (mRecentTasks.isPresent()) { + mRecentTasks.get().removeSplitPair(taskId); + } + options = options != null ? options : new Bundle(); + addActivityOptions(options, null); + wct.startTask(taskId, options); + mSplitTransitions.startFullscreenTransition(wct, remoteTransition); + } + /** Starts a shortcut and a task to a split pair in one transition. */ void startShortcutAndTask(ShortcutInfo shortcutInfo, @Nullable Bundle options1, int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition, @@ -842,6 +860,21 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, return; } + boolean handledForPipSplitLaunch = handlePippedSplitIntentsLaunch( + pendingIntent1, + pendingIntent2, + options1, + options2, + shortcutInfo1, + shortcutInfo2, + wct, + fillInIntent1, + fillInIntent2, + remoteTransition); + if (handledForPipSplitLaunch) { + return; + } + if (!mMainStage.isActive()) { // Build a request WCT that will launch both apps such that task 0 is on the main stage // while task 1 is on the side stage. @@ -876,6 +909,46 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, setEnterInstanceId(instanceId); } + /** + * Checks if either of the apps in the desired split launch is currently in Pip. If so, it will + * launch the non-pipped app as a fullscreen app, otherwise no-op. + */ + private boolean handlePippedSplitIntentsLaunch(PendingIntent pendingIntent1, + PendingIntent pendingIntent2, Bundle options1, Bundle options2, + ShortcutInfo shortcutInfo1, ShortcutInfo shortcutInfo2, WindowContainerTransaction wct, + Intent fillInIntent1, Intent fillInIntent2, RemoteTransition remoteTransition) { + // If one of the split apps to start is in Pip, only launch the non-pip app in fullscreen + boolean firstIntentPipped = mMixedHandler.isIntentInPip(pendingIntent1); + boolean secondIntentPipped = mMixedHandler.isIntentInPip(pendingIntent2); + if (firstIntentPipped || secondIntentPipped) { + Bundle options = secondIntentPipped ? options1 : options2; + options = options == null ? new Bundle() : options; + addActivityOptions(options, null); + if (shortcutInfo1 != null || shortcutInfo2 != null) { + ShortcutInfo infoToLaunch = secondIntentPipped ? shortcutInfo1 : shortcutInfo2; + wct.startShortcut(mContext.getPackageName(), infoToLaunch, options); + mSplitTransitions.startFullscreenTransition(wct, remoteTransition); + } else { + PendingIntent intentToLaunch = secondIntentPipped ? pendingIntent1 : pendingIntent2; + Intent fillInIntentToLaunch = secondIntentPipped ? fillInIntent1 : fillInIntent2; + startSingleIntent(intentToLaunch, fillInIntentToLaunch, options, wct, + remoteTransition); + } + return true; + } + return false; + } + + /** @param pendingIntent Starts this intent in fullscreen */ + private void startSingleIntent(PendingIntent pendingIntent, Intent fillInIntent, Bundle options, + WindowContainerTransaction wct, + RemoteTransition remoteTransition) { + Bundle optionsToLaunch = options != null ? options : new Bundle(); + addActivityOptions(optionsToLaunch, null); + wct.sendPendingIntent(pendingIntent, fillInIntent, optionsToLaunch); + mSplitTransitions.startFullscreenTransition(wct, remoteTransition); + } + /** Starts a pair of tasks using legacy transition. */ void startTasksWithLegacyTransition(int taskId1, @Nullable Bundle options1, int taskId2, @Nullable Bundle options2, @SplitPosition int splitPosition, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java index 8746b8c8d55c..99133f79c85c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java @@ -562,22 +562,23 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, /** Use to when split use intent to enter, check if this enter transition should be mixed or * not.*/ - public boolean shouldSplitEnterMixed(PendingIntent intent) { + public boolean isIntentInPip(PendingIntent intent) { // Check if this intent package is same as pip one or not, if true we want let the pip // task enter split. if (mPipHandler != null) { - return mPipHandler.isInPipPackage(SplitScreenUtils.getPackageName(intent.getIntent())); + return mPipHandler + .isPackageActiveInPip(SplitScreenUtils.getPackageName(intent.getIntent())); } return false; } /** Use to when split use taskId to enter, check if this enter transition should be mixed or * not.*/ - public boolean shouldSplitEnterMixed(int taskId, ShellTaskOrganizer shellTaskOrganizer) { + public boolean isTaskInPip(int taskId, ShellTaskOrganizer shellTaskOrganizer) { // Check if this intent package is same as pip one or not, if true we want let the pip // task enter split. if (mPipHandler != null) { - return mPipHandler.isInPipPackage( + return mPipHandler.isPackageActiveInPip( SplitScreenUtils.getPackageName(taskId, shellTaskOrganizer)); } return false; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java index d819261ecba2..d7c383523a6f 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java @@ -40,10 +40,12 @@ import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.ActivityManager; +import android.app.PendingIntent; import android.content.res.Configuration; import android.graphics.Rect; import android.os.Bundle; @@ -51,6 +53,7 @@ import android.os.Handler; import android.os.Looper; import android.view.SurfaceControl; import android.view.SurfaceSession; +import android.window.RemoteTransition; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; @@ -74,6 +77,7 @@ import com.android.wm.shell.common.split.SplitLayout; import com.android.wm.shell.splitscreen.SplitScreen.SplitScreenListener; import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; +import com.android.wm.shell.transition.DefaultMixedHandler; import com.android.wm.shell.transition.HomeTransitionObserver; import com.android.wm.shell.transition.Transitions; @@ -111,6 +115,8 @@ public class StageCoordinatorTests extends ShellTestCase { private TransactionPool mTransactionPool; @Mock private LaunchAdjacentController mLaunchAdjacentController; + @Mock + private DefaultMixedHandler mDefaultMixedHandler; private final Rect mBounds1 = new Rect(10, 20, 30, 40); private final Rect mBounds2 = new Rect(5, 10, 15, 20); @@ -370,6 +376,96 @@ public class StageCoordinatorTests extends ShellTestCase { } } + @Test + public void testSplitIntentAndTaskWithPippedApp_launchFullscreen() { + int taskId = 9; + SplitScreenTransitions splitScreenTransitions = + spy(mStageCoordinator.getSplitTransitions()); + mStageCoordinator.setSplitTransitions(splitScreenTransitions); + mStageCoordinator.setMixedHandler(mDefaultMixedHandler); + PendingIntent pendingIntent = mock(PendingIntent.class); + RemoteTransition remoteTransition = mock(RemoteTransition.class); + when(remoteTransition.getDebugName()).thenReturn(""); + // Test launching second task full screen + when(mDefaultMixedHandler.isIntentInPip(pendingIntent)).thenReturn(true); + mStageCoordinator.startIntentAndTask( + pendingIntent, + null /*fillInIntent*/, + null /*option1*/, + taskId, + null /*option2*/, + 0 /*splitPosition*/, + 1 /*snapPosition*/, + remoteTransition /*remoteTransition*/, + null /*instanceId*/); + verify(splitScreenTransitions, times(1)) + .startFullscreenTransition(any(), any()); + + // Test launching first intent fullscreen + when(mDefaultMixedHandler.isIntentInPip(pendingIntent)).thenReturn(false); + when(mDefaultMixedHandler.isTaskInPip(taskId, mTaskOrganizer)).thenReturn(true); + mStageCoordinator.startIntentAndTask( + pendingIntent, + null /*fillInIntent*/, + null /*option1*/, + taskId, + null /*option2*/, + 0 /*splitPosition*/, + 1 /*snapPosition*/, + remoteTransition /*remoteTransition*/, + null /*instanceId*/); + verify(splitScreenTransitions, times(2)) + .startFullscreenTransition(any(), any()); + } + + @Test + public void testSplitIntentsWithPippedApp_launchFullscreen() { + SplitScreenTransitions splitScreenTransitions = + spy(mStageCoordinator.getSplitTransitions()); + mStageCoordinator.setSplitTransitions(splitScreenTransitions); + mStageCoordinator.setMixedHandler(mDefaultMixedHandler); + PendingIntent pendingIntent = mock(PendingIntent.class); + PendingIntent pendingIntent2 = mock(PendingIntent.class); + RemoteTransition remoteTransition = mock(RemoteTransition.class); + when(remoteTransition.getDebugName()).thenReturn(""); + // Test launching second task full screen + when(mDefaultMixedHandler.isIntentInPip(pendingIntent)).thenReturn(true); + mStageCoordinator.startIntents( + pendingIntent, + null /*fillInIntent*/, + null /*shortcutInfo1*/, + new Bundle(), + pendingIntent2, + null /*fillInIntent2*/, + null /*shortcutInfo1*/, + new Bundle(), + 0 /*splitPosition*/, + 1 /*snapPosition*/, + remoteTransition /*remoteTransition*/, + null /*instanceId*/); + verify(splitScreenTransitions, times(1)) + .startFullscreenTransition(any(), any()); + + // Test launching first intent fullscreen + when(mDefaultMixedHandler.isIntentInPip(pendingIntent)).thenReturn(false); + when(mDefaultMixedHandler.isIntentInPip(pendingIntent2)).thenReturn(true); + mStageCoordinator.startIntents( + pendingIntent, + null /*fillInIntent*/, + null /*shortcutInfo1*/, + new Bundle(), + pendingIntent2, + null /*fillInIntent2*/, + null /*shortcutInfo1*/, + new Bundle(), + 0 /*splitPosition*/, + 1 /*snapPosition*/, + remoteTransition /*remoteTransition*/, + null /*instanceId*/); + verify(splitScreenTransitions, times(2)) + .startFullscreenTransition(any(), any()); + } + private Transitions createTestTransitions() { ShellInit shellInit = new ShellInit(mMainExecutor); final Transitions t = new Transitions(mContext, shellInit, mock(ShellController.class), |