diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java index b8204d0131051..0bcafe513b4f0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java @@ -59,4 +59,17 @@ public class SplitScreenConstants { /** Flag applied to a transition change to identify it as a divider bar for animation. */ public static final int FLAG_IS_DIVIDER_BAR = FLAG_FIRST_CUSTOM; + + public static final String splitPositionToString(@SplitPosition int pos) { + switch (pos) { + case SPLIT_POSITION_UNDEFINED: + return "SPLIT_POSITION_UNDEFINED"; + case SPLIT_POSITION_TOP_OR_LEFT: + return "SPLIT_POSITION_TOP_OR_LEFT"; + case SPLIT_POSITION_BOTTOM_OR_RIGHT: + return "SPLIT_POSITION_BOTTOM_OR_RIGHT"; + default: + return "UNKNOWN"; + } + } } 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 0ef26fcff2840..e5ae10c097a55 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 @@ -39,6 +39,7 @@ import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIV import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED; +import static com.android.wm.shell.common.split.SplitScreenConstants.splitPositionToString; import static com.android.wm.shell.common.split.SplitScreenUtils.reverseSplitPosition; import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN; import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE; @@ -378,40 +379,18 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, boolean moveToStage(ActivityManager.RunningTaskInfo task, @SplitPosition int stagePosition, WindowContainerTransaction wct) { - StageTaskListener targetStage; - int sideStagePosition; - if (isSplitScreenVisible()) { - // If the split screen is foreground, retrieves target stage based on position. - targetStage = stagePosition == mSideStagePosition ? mSideStage : mMainStage; - sideStagePosition = mSideStagePosition; + prepareEnterSplitScreen(wct, task, stagePosition); + if (ENABLE_SHELL_TRANSITIONS) { + mSplitTransitions.startEnterTransition(TRANSIT_TO_FRONT, wct, + null, this, null /* consumedCallback */, null /* finishedCallback */, + isSplitScreenVisible() + ? TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE : TRANSIT_SPLIT_SCREEN_PAIR_OPEN); } else { - targetStage = mSideStage; - sideStagePosition = stagePosition; - } - - if (!isSplitActive()) { - mSplitLayout.init(); - prepareEnterSplitScreen(wct, task, stagePosition); mSyncQueue.queue(wct); mSyncQueue.runInSync(t -> { updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */); - setDividerVisibility(true, t); }); - } else { - setSideStagePosition(sideStagePosition, wct); - targetStage.addTask(task, wct); - targetStage.evictAllChildren(wct); - if (!isSplitScreenVisible()) { - final StageTaskListener anotherStage = targetStage == mMainStage - ? mSideStage : mMainStage; - anotherStage.reparentTopTask(wct); - anotherStage.evictAllChildren(wct); - wct.reorder(mRootTaskInfo.token, true); - } - setRootForceTranslucent(false, wct); - mSyncQueue.queue(wct); } - // Due to drag already pip task entering split by this method so need to reset flag here. mIsDropEntering = false; return true; @@ -1509,9 +1488,51 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, */ void prepareEnterSplitScreen(WindowContainerTransaction wct, @Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition) { - if (mMainStage.isActive()) return; - onSplitScreenEnter(); + if (isSplitActive()) { + prepareBringSplit(wct, taskInfo, startPosition); + } else { + prepareActiveSplit(wct, taskInfo, startPosition); + } + } + + private void prepareBringSplit(WindowContainerTransaction wct, + @Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition) { + StageTaskListener targetStage; + if (isSplitScreenVisible()) { + // If the split screen is foreground, retrieves target stage based on position. + targetStage = startPosition == mSideStagePosition ? mSideStage : mMainStage; + } else { + targetStage = mSideStage; + } + + if (taskInfo != null) { + wct.startTask(taskInfo.taskId, + resolveStartStage(STAGE_TYPE_UNDEFINED, startPosition, null, wct)); + targetStage.evictAllChildren(wct); + } + // If running background, we need to reparent current top visible task to another stage + // and evict all tasks current under its. + if (!isSplitScreenVisible()) { + // Recreate so we need to reset position rather than keep position of background split. + mSplitLayout.resetDividerPosition(); + updateWindowBounds(mSplitLayout, wct); + final StageTaskListener anotherStage = targetStage == mMainStage + ? mSideStage : mMainStage; + anotherStage.evictAllChildren(wct); + anotherStage.reparentTopTask(wct); + wct.reorder(mRootTaskInfo.token, true); + setRootForceTranslucent(false, wct); + } + } + + private void prepareActiveSplit(WindowContainerTransaction wct, + @Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition) { + if (!ENABLE_SHELL_TRANSITIONS) { + // Legacy transition we need to create divider here, shell transition case we will + // create it on #finishEnterSplitScreen + mSplitLayout.init(); + } if (taskInfo != null) { setSideStagePosition(startPosition, wct); mSideStage.addTask(taskInfo, wct); @@ -2383,7 +2404,17 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); - if (taskInfo == null || !taskInfo.hasParentTask()) continue; + if (taskInfo == null) continue; + if (taskInfo.token.equals(mRootTaskInfo.token)) { + if (isOpeningType(change.getMode())) { + // Split is opened by someone so set it as visible. + setSplitsVisible(true); + } else if (isClosingType(change.getMode())) { + // Split is closed by someone so set it as invisible. + setSplitsVisible(false); + } + continue; + } final StageTaskListener stage = getStageOfTask(taskInfo); if (stage == null) continue; if (isOpeningType(change.getMode())) { @@ -2823,12 +2854,18 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, final String childPrefix = innerPrefix + " "; pw.println(prefix + TAG + " mDisplayId=" + mDisplayId); pw.println(innerPrefix + "mDividerVisible=" + mDividerVisible); + pw.println(innerPrefix + "isSplitActive=" + isSplitActive()); + pw.println(innerPrefix + "isSplitVisible=" + isSplitScreenVisible()); pw.println(innerPrefix + "MainStage"); - pw.println(childPrefix + "stagePosition=" + getMainStagePosition()); + pw.println(childPrefix + "stagePosition=" + splitPositionToString(getMainStagePosition())); pw.println(childPrefix + "isActive=" + mMainStage.isActive()); + mMainStage.dump(pw, childPrefix); + pw.println(innerPrefix + "MainStageListener"); mMainStageListener.dump(pw, childPrefix); pw.println(innerPrefix + "SideStage"); - pw.println(childPrefix + "stagePosition=" + getSideStagePosition()); + pw.println(childPrefix + "stagePosition=" + splitPositionToString(getSideStagePosition())); + mSideStage.dump(pw, childPrefix); + pw.println(innerPrefix + "SideStageListener"); mSideStageListener.dump(pw, childPrefix); if (mMainStage.isActive()) { pw.println(innerPrefix + "SplitLayout"); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java index a841b7f96d3c5..18b09b0907943 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java @@ -421,6 +421,13 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { public void dump(@NonNull PrintWriter pw, String prefix) { final String innerPrefix = prefix + " "; final String childPrefix = innerPrefix + " "; - pw.println(prefix + this); + if (mChildrenTaskInfo.size() > 0) { + pw.println(prefix + "Children list:"); + for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) { + final ActivityManager.RunningTaskInfo taskInfo = mChildrenTaskInfo.valueAt(i); + pw.println(childPrefix + "Task#" + i + " taskID=" + taskInfo.taskId + + " baseActivity=" + taskInfo.baseActivity); + } + } } } 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 7c729a46b6799..49429327572e2 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 @@ -73,7 +73,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, /** Pip was entered while handling an intent with its own remoteTransition. */ static final int TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE = 3; - /** Recents transition while split-screen active. */ + /** Recents transition while split-screen foreground. */ static final int TYPE_RECENTS_DURING_SPLIT = 4; /** The default animation for this mixed transition. */ @@ -152,7 +152,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, @NonNull TransitionRequestInfo request) { if (mPipHandler.requestHasPipEnter(request) && mSplitHandler.isSplitScreenVisible()) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a PiP-enter request while " - + "Split-Screen is active, so treat it as Mixed."); + + "Split-Screen is foreground, so treat it as Mixed."); if (request.getRemoteTransition() != null) { throw new IllegalStateException("Unexpected remote transition in" + "pip-enter-from-split request"); @@ -183,13 +183,13 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, mixed.mLeftoversHandler = handler.first; mActiveTransitions.add(mixed); return handler.second; - } else if (mSplitHandler.isSplitActive() + } else if (mSplitHandler.isSplitScreenVisible() && isOpeningType(request.getType()) && request.getTriggerTask() != null && request.getTriggerTask().getWindowingMode() == WINDOWING_MODE_FULLSCREEN && request.getTriggerTask().getActivityType() == ACTIVITY_TYPE_HOME) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a going-home request while " - + "Split-Screen is active, so treat it as Mixed."); + + "Split-Screen is foreground, so treat it as Mixed."); Pair handler = mPlayer.dispatchRequest(transition, request, this); if (handler == null) { @@ -211,7 +211,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, @Override public Transitions.TransitionHandler handleRecentsRequest(WindowContainerTransaction outWCT) { - if (mRecentsHandler != null && mSplitHandler.isSplitActive()) { + if (mRecentsHandler != null && mSplitHandler.isSplitScreenVisible()) { return this; } return null; @@ -219,9 +219,9 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, @Override public void setRecentsTransition(IBinder transition) { - if (mSplitHandler.isSplitActive()) { + if (mSplitHandler.isSplitScreenVisible()) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a recents request while " - + "Split-Screen is active, so treat it as Mixed."); + + "Split-Screen is foreground, so treat it as Mixed."); final MixedTransition mixed = new MixedTransition( MixedTransition.TYPE_RECENTS_DURING_SPLIT, transition); mixed.mLeftoversHandler = mRecentsHandler; @@ -351,7 +351,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animating a mixed transition for " - + "entering PIP while Split-Screen is active."); + + "entering PIP while Split-Screen is foreground."); TransitionInfo.Change pipChange = null; TransitionInfo.Change wallpaper = null; final TransitionInfo everythingElse = subCopy(info, TRANSIT_TO_BACK, true /* changes */); 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 e6219d1aa7921..d27064d1b4dad 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 @@ -152,10 +152,15 @@ public class StageCoordinatorTests extends ShellTestCase { when(mStageCoordinator.isSplitActive()).thenReturn(true); final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build(); - final WindowContainerTransaction wct = new WindowContainerTransaction(); + final WindowContainerTransaction wct = spy(new WindowContainerTransaction()); mStageCoordinator.moveToStage(task, SPLIT_POSITION_BOTTOM_OR_RIGHT, wct); - verify(mSideStage).addTask(eq(task), eq(wct)); + verify(mStageCoordinator).prepareEnterSplitScreen(eq(wct), eq(task), + eq(SPLIT_POSITION_BOTTOM_OR_RIGHT)); + verify(mMainStage).reparentTopTask(eq(wct)); + verify(mMainStage).evictAllChildren(eq(wct)); + verify(mSideStage).evictAllChildren(eq(wct)); + verify(mSplitLayout).resetDividerPosition(); assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, mStageCoordinator.getSideStagePosition()); assertEquals(SPLIT_POSITION_TOP_OR_LEFT, mStageCoordinator.getMainStagePosition()); } @@ -171,14 +176,11 @@ public class StageCoordinatorTests extends ShellTestCase { final WindowContainerTransaction wct = new WindowContainerTransaction(); mStageCoordinator.moveToStage(task, SPLIT_POSITION_BOTTOM_OR_RIGHT, wct); - verify(mMainStage).addTask(eq(task), eq(wct)); + verify(mStageCoordinator).prepareEnterSplitScreen(eq(wct), eq(task), + eq(SPLIT_POSITION_BOTTOM_OR_RIGHT)); + verify(mMainStage).evictAllChildren(eq(wct)); assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, mStageCoordinator.getMainStagePosition()); assertEquals(SPLIT_POSITION_TOP_OR_LEFT, mStageCoordinator.getSideStagePosition()); - - mStageCoordinator.moveToStage(task, SPLIT_POSITION_TOP_OR_LEFT, wct); - verify(mSideStage).addTask(eq(task), eq(wct)); - assertEquals(SPLIT_POSITION_TOP_OR_LEFT, mStageCoordinator.getSideStagePosition()); - assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, mStageCoordinator.getMainStagePosition()); } @Test