From e5d8c8069db3d35b8cdbc057a0a979a7f8614e3b Mon Sep 17 00:00:00 2001 From: Jerry Chang Date: Tue, 21 Dec 2021 15:22:44 +0000 Subject: [PATCH] Integrate pip to split flow with shell-transition Integrate moveToStage API with open-to-side shell-transition. Also sketch exit pip to split transition to merge exit pip and enter split transitions as one. Bug: 206487881 Test: atest WMShellUnitTests Test: moveToSideStage command works with shell-transition Test: tap "enter split" on PIP window, verified it enters split screen as expectedly. Change-Id: Ibccc08b277a0c38bfb31411cbe5e31941a6bacf1 --- .../wm/shell/dagger/WMShellModule.java | 5 +- .../wm/shell/pip/PipTaskOrganizer.java | 26 ++++- .../android/wm/shell/pip/PipTransition.java | 66 +++++++++-- .../wm/shell/pip/PipTransitionController.java | 6 +- .../splitscreen/SplitScreenController.java | 9 ++ .../splitscreen/SplitScreenTransitions.java | 11 +- .../shell/splitscreen/StageCoordinator.java | 107 +++++++++++------- .../shell/splitscreen/StageTaskListener.java | 5 + .../wm/shell/transition/Transitions.java | 20 ++-- 9 files changed, 185 insertions(+), 70 deletions(-) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java index d70857a4b2217..4f01dc60452e5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java @@ -309,10 +309,11 @@ public class WMShellModule { PipAnimationController pipAnimationController, PipBoundsAlgorithm pipBoundsAlgorithm, PipBoundsState pipBoundsState, PipTransitionState pipTransitionState, PhonePipMenuController pipMenuController, - PipSurfaceTransactionHelper pipSurfaceTransactionHelper) { + PipSurfaceTransactionHelper pipSurfaceTransactionHelper, + Optional splitScreenOptional) { return new PipTransition(context, pipBoundsState, pipTransitionState, pipMenuController, pipBoundsAlgorithm, pipAnimationController, transitions, shellTaskOrganizer, - pipSurfaceTransactionHelper); + pipSurfaceTransactionHelper, splitScreenOptional); } @WMSingleton diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index 667d16636e6c1..a8d4d1c281073 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -25,6 +25,8 @@ import static android.util.RotationUtils.rotateBounds; import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_PIP; import static com.android.wm.shell.ShellTaskOrganizer.taskListenerTypeToString; +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.pip.PipAnimationController.ANIM_TYPE_ALPHA; import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_BOUNDS; import static com.android.wm.shell.pip.PipAnimationController.FRACTION_START; @@ -40,6 +42,10 @@ import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTI import static com.android.wm.shell.pip.PipAnimationController.isInPipDirection; import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection; import static com.android.wm.shell.pip.PipAnimationController.isRemovePipDirection; +import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS; +import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP; +import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP_TO_SPLIT; +import static com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -394,6 +400,18 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mPipUiEventLoggerLogger.log( PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN); final WindowContainerTransaction wct = new WindowContainerTransaction(); + + if (ENABLE_SHELL_TRANSITIONS) { + if (requestEnterSplit && mSplitScreenOptional.isPresent()) { + mSplitScreenOptional.get().prepareEnterSplitScreen(wct, mTaskInfo, + isPipTopLeft() + ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT); + mPipTransitionController.startExitTransition( + TRANSIT_EXIT_PIP_TO_SPLIT, wct, null /* destinationBounds */); + return; + } + } + final Rect destinationBounds = mPipBoundsState.getDisplayBounds(); final int direction = syncWithSplitScreenBounds(destinationBounds, requestEnterSplit) ? TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN @@ -414,7 +432,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mPipTransitionState.setTransitionState(PipTransitionState.EXITING_PIP); if (Transitions.ENABLE_SHELL_TRANSITIONS) { - mPipTransitionController.startTransition(destinationBounds, wct); + mPipTransitionController.startExitTransition(TRANSIT_EXIT_PIP, wct, destinationBounds); return; } mSyncTransactionQueue.queue(wct); @@ -479,7 +497,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, wct.setBounds(mToken, null); wct.setWindowingMode(mToken, WINDOWING_MODE_UNDEFINED); wct.reorder(mToken, false); - mPipTransitionController.startTransition(null, wct); + mPipTransitionController.startExitTransition(TRANSIT_REMOVE_PIP, wct, + null /* destinationBounds */); return; } @@ -1280,7 +1299,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, public void applyFinishBoundsResize(@NonNull WindowContainerTransaction wct, @PipAnimationController.TransitionDirection int direction, boolean wasPipTopLeft) { if (direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN) { - mSplitScreenOptional.get().enterSplitScreen(mTaskInfo.taskId, wasPipTopLeft, wct); + mSplitScreenOptional.ifPresent(splitScreenController -> + splitScreenController.enterSplitScreen(mTaskInfo.taskId, wasPipTopLeft, wct)); } else { mTaskOrganizer.applyTransaction(wct); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java index 0fcfced2dcfc6..3039f9c57431a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java @@ -30,8 +30,9 @@ import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTI import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP; import static com.android.wm.shell.pip.PipAnimationController.isInPipDirection; import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection; -import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP; +import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP_TO_SPLIT; import static com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP; +import static com.android.wm.shell.transition.Transitions.isOpeningType; import android.app.ActivityManager; import android.app.TaskInfo; @@ -51,8 +52,11 @@ import androidx.annotation.Nullable; import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.splitscreen.SplitScreenController; import com.android.wm.shell.transition.Transitions; +import java.util.Optional; + /** * Implementation of transitions for PiP on phone. Responsible for enter (alpha, bounds) and * exit animation. @@ -64,6 +68,7 @@ public class PipTransition extends PipTransitionController { private final PipTransitionState mPipTransitionState; private final int mEnterExitAnimationDuration; private final PipSurfaceTransactionHelper mSurfaceTransactionHelper; + private final Optional mSplitScreenOptional; private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS; private Transitions.TransitionFinishCallback mFinishCallback; private Rect mExitDestinationBounds = new Rect(); @@ -77,13 +82,15 @@ public class PipTransition extends PipTransitionController { PipAnimationController pipAnimationController, Transitions transitions, @NonNull ShellTaskOrganizer shellTaskOrganizer, - PipSurfaceTransactionHelper pipSurfaceTransactionHelper) { + PipSurfaceTransactionHelper pipSurfaceTransactionHelper, + Optional splitScreenOptional) { super(pipBoundsState, pipMenuController, pipBoundsAlgorithm, pipAnimationController, transitions, shellTaskOrganizer); mPipTransitionState = pipTransitionState; mEnterExitAnimationDuration = context.getResources() .getInteger(R.integer.config_pipResizeAnimationDuration); mSurfaceTransactionHelper = pipSurfaceTransactionHelper; + mSplitScreenOptional = splitScreenOptional; } @Override @@ -101,13 +108,12 @@ public class PipTransition extends PipTransitionController { } @Override - public void startTransition(Rect destinationBounds, WindowContainerTransaction out) { + public void startExitTransition(int type, WindowContainerTransaction out, + @Nullable Rect destinationBounds) { if (destinationBounds != null) { mExitDestinationBounds.set(destinationBounds); - mExitTransition = mTransitions.startTransition(TRANSIT_EXIT_PIP, out, this); - } else { - mTransitions.startTransition(TRANSIT_REMOVE_PIP, out, this); } + mExitTransition = mTransitions.startTransition(type, out, this); } @Override @@ -116,9 +122,15 @@ public class PipTransition extends PipTransitionController { @android.annotation.NonNull SurfaceControl.Transaction startTransaction, @android.annotation.NonNull SurfaceControl.Transaction finishTransaction, @android.annotation.NonNull Transitions.TransitionFinishCallback finishCallback) { - - if (mExitTransition == transition || info.getType() == TRANSIT_EXIT_PIP) { + final int type = info.getType(); + if (mExitTransition == transition) { mExitTransition = null; + + if (type == TRANSIT_EXIT_PIP_TO_SPLIT) { + return startExitToSplitAnimation( + info, startTransaction, finishTransaction, finishCallback); + } + if (info.getChanges().size() == 1) { if (mFinishCallback != null) { mFinishCallback.onTransitionFinished(null, null); @@ -138,7 +150,7 @@ public class PipTransition extends PipTransitionController { } } - if (info.getType() == TRANSIT_REMOVE_PIP) { + if (type == TRANSIT_REMOVE_PIP) { if (mFinishCallback != null) { mFinishCallback.onTransitionFinished(null /* wct */, null /* callback */); mFinishCallback = null; @@ -154,7 +166,7 @@ public class PipTransition extends PipTransitionController { // We only support TRANSIT_PIP type (from RootWindowContainer) or TRANSIT_OPEN (from apps // that enter PiP instantly on opening, mostly from CTS/Flicker tests) - if (info.getType() != TRANSIT_PIP && info.getType() != TRANSIT_OPEN) { + if (type != TRANSIT_PIP && type != TRANSIT_OPEN) { // In case the PIP window is part of rotation transition, reset the bounds and rounded // corner. for (int i = info.getChanges().size() - 1; i >= 0; --i) { @@ -369,6 +381,40 @@ public class PipTransition extends PipTransitionController { return true; } + private boolean startExitToSplitAnimation(TransitionInfo info, + SurfaceControl.Transaction startTransaction, + SurfaceControl.Transaction finishTransaction, + Transitions.TransitionFinishCallback finishCallback) { + final int changeSize = info.getChanges().size(); + if (changeSize < 4) { + throw new RuntimeException( + "Got an exit-pip-to-split transition with unexpected change-list"); + } + for (int i = changeSize - 1; i >= 0; i--) { + final TransitionInfo.Change change = info.getChanges().get(i); + final int mode = change.getMode(); + + if (mode == TRANSIT_CHANGE && change.getParent() != null) { + // TODO: perform resize/expand animation for reparented child task. + continue; + } + + if (isOpeningType(mode) && change.getParent() == null) { + final SurfaceControl leash = change.getLeash(); + final Rect endBounds = change.getEndAbsBounds(); + startTransaction + .show(leash) + .setAlpha(leash, 1f) + .setPosition(leash, endBounds.left, endBounds.top) + .setWindowCrop(leash, endBounds.width(), endBounds.height()); + } + } + mSplitScreenOptional.get().finishEnterSplitScreen(startTransaction); + startTransaction.apply(); + finishCallback.onTransitionFinished(null, null); + return true; + } + private void finishResizeForMenu(Rect destinationBounds) { mPipMenuController.movePipMenu(null, null, destinationBounds); mPipMenuController.updateMenuBounds(destinationBounds); 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 376f3298a83c9..1c8b9bc21ae11 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 @@ -20,6 +20,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_REMOVE_STACK; +import android.annotation.Nullable; import android.app.PictureInPictureParams; import android.app.TaskInfo; import android.content.ComponentName; @@ -98,9 +99,10 @@ public abstract class PipTransitionController implements Transitions.TransitionH } /** - * Called when the Shell wants to starts a transition/animation. + * Called when the Shell wants to start an exit Pip transition/animation. */ - public void startTransition(Rect destinationBounds, WindowContainerTransaction out) { + public void startExitTransition(int type, WindowContainerTransaction out, + @Nullable Rect destinationBounds) { // Default implementation does nothing. } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index 1f49a4cc3ab90..0b374c9d1ec40 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -238,6 +238,15 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, enterSplitScreen(taskId, leftOrTop, new WindowContainerTransaction()); } + public void prepareEnterSplitScreen(WindowContainerTransaction wct, + ActivityManager.RunningTaskInfo taskInfo, int startPosition) { + mStageCoordinator.prepareEnterSplitScreen(wct, taskInfo, startPosition); + } + + public void finishEnterSplitScreen(SurfaceControl.Transaction t) { + mStageCoordinator.finishEnterSplitScreen(t); + } + public void enterSplitScreen(int taskId, boolean leftOrTop, WindowContainerTransaction wct) { final int stageType = isSplitScreenVisible() ? STAGE_TYPE_UNDEFINED : STAGE_TYPE_SIDE; final int stagePosition = diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java index 54d8ece26c1ac..0aa8d7e2f1a35 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java @@ -28,7 +28,8 @@ import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON import static com.android.wm.shell.splitscreen.SplitScreenController.exitReasonToString; import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_DISMISS; import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_DISMISS_SNAP; -import static com.android.wm.shell.transition.Transitions.isOpeningType; +import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE; +import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_PAIR_OPEN; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -148,7 +149,7 @@ class SplitScreenTransitions { t.setWindowCrop(leash, change.getEndAbsBounds().width(), change.getEndAbsBounds().height()); } - boolean isOpening = isOpeningType(info.getType()); + boolean isOpening = isOpeningTransition(info); if (isOpening && (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT)) { // fade in startExampleAnimation(leash, true /* show */); @@ -305,6 +306,12 @@ class SplitScreenTransitions { mTransitions.getAnimExecutor().execute(va::start); } + private boolean isOpeningTransition(TransitionInfo info) { + return Transitions.isOpeningType(info.getType()) + || info.getType() == TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE + || info.getType() == TRANSIT_SPLIT_SCREEN_PAIR_OPEN; + } + /** Bundled information of dismiss transition. */ static class DismissTransition { IBinder mTransition; 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 c87f0bcb469c8..8f5a560facd36 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 @@ -46,6 +46,7 @@ import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON import static com.android.wm.shell.splitscreen.SplitScreenController.exitReasonToString; import static com.android.wm.shell.splitscreen.SplitScreenTransitions.FLAG_IS_DIVIDER_BAR; import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS; +import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE; import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_PAIR_OPEN; import static com.android.wm.shell.transition.Transitions.isClosingType; import static com.android.wm.shell.transition.Transitions.isOpeningType; @@ -318,7 +319,14 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (!evictWct.isEmpty()) { wct.merge(evictWct, true /* transfer */); } - mTaskOrganizer.applyTransaction(wct); + + if (ENABLE_SHELL_TRANSITIONS) { + prepareEnterSplitScreen(wct); + mSplitTransitions.startEnterTransition(TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, + wct, null, this); + } else { + mTaskOrganizer.applyTransaction(wct); + } return true; } @@ -694,12 +702,47 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, * an existing WindowContainerTransaction (rather than applying immediately). This is intended * to be used when exiting split might be bundled with other window operations. */ - void prepareExitSplitScreen(@StageType int stageToTop, + private void prepareExitSplitScreen(@StageType int stageToTop, @NonNull WindowContainerTransaction wct) { + if (!mMainStage.isActive()) return; mSideStage.removeAllTasks(wct, stageToTop == STAGE_TYPE_SIDE); mMainStage.deactivate(wct, stageToTop == STAGE_TYPE_MAIN); } + private void prepareEnterSplitScreen(WindowContainerTransaction wct) { + prepareEnterSplitScreen(wct, null /* taskInfo */, SPLIT_POSITION_UNDEFINED); + } + + /** + * Prepare transaction to active split screen. If there's a task indicated, the task will be put + * into side stage. + */ + void prepareEnterSplitScreen(WindowContainerTransaction wct, + @Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition) { + if (mMainStage.isActive()) return; + + if (taskInfo != null) { + setSideStagePosition(startPosition, wct); + mSideStage.addTask(taskInfo, wct); + } + mMainStage.activate(getMainStageBounds(), wct, true /* includingTopTask */); + mSideStage.moveToTop(getSideStageBounds(), wct); + } + + void finishEnterSplitScreen(SurfaceControl.Transaction t) { + mSplitLayout.init(); + setDividerVisibility(true, t); + setSplitsVisible(true); + mShouldUpdateRecents = true; + updateRecentTasksSplitPair(); + if (!mLogger.hasStartedSession()) { + mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(), + getMainStagePosition(), mMainStage.getTopChildTaskUid(), + getSideStagePosition(), mSideStage.getTopChildTaskUid(), + mSplitLayout.isLandscape()); + } + } + void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds) { outTopOrLeftBounds.set(mSplitLayout.getBounds1()); outBottomOrRightBounds.set(mSplitLayout.getBounds2()); @@ -906,8 +949,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, } else if (isSideStage) { final WindowContainerTransaction wct = new WindowContainerTransaction(); // Make sure the main stage is active. - mMainStage.activate(getMainStageBounds(), wct, true /* reparent */); - mSideStage.moveToTop(getSideStageBounds(), wct); + prepareEnterSplitScreen(wct); mSyncQueue.queue(wct); mSyncQueue.runInSync(t -> updateSurfaceBounds(mSplitLayout, t)); } @@ -1199,8 +1241,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (isOpening && getStageOfTask(triggerTask) != null) { // One task is appearing into split, prepare to enter split screen. out = new WindowContainerTransaction(); - mMainStage.activate(getMainStageBounds(), out, true /* includingTopTask */); - mSideStage.moveToTop(getSideStageBounds(), out); + prepareEnterSplitScreen(out); mSplitTransitions.mPendingEnter = transition; } } @@ -1212,18 +1253,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, // Once the pending enter transition got merged, make sure to bring divider bar visible and // clear the pending transition from cache to prevent mess-up the following state. if (transition == mSplitTransitions.mPendingEnter) { - mSplitLayout.init(); - setDividerVisibility(true, null /* transaction */); - setSplitsVisible(true); - mShouldUpdateRecents = true; - updateRecentTasksSplitPair(); - - if (!mLogger.hasStartedSession()) { - mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(), - getMainStagePosition(), mMainStage.getTopChildTaskUid(), - getSideStagePosition(), mSideStage.getTopChildTaskUid(), - mSplitLayout.isLandscape()); - } + finishEnterSplitScreen(null); mSplitTransitions.mPendingEnter = null; } } @@ -1311,47 +1341,40 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, sideChild = change; } } - if (mainChild == null || sideChild == null) { - throw new IllegalStateException("Launched 2 tasks in split, but didn't receive" - + " 2 tasks in transition. Possibly one of them failed to launch"); - // TODO: fallback logic. Probably start a new transition to exit split before - // applying anything here. Ideally consolidate with transition-merging. + + // TODO: fallback logic. Probably start a new transition to exit split before applying + // anything here. Ideally consolidate with transition-merging. + if (info.getType() == TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE) { + if (mainChild == null && sideChild == null) { + throw new IllegalStateException("Launched a task in split, but didn't receive any" + + " task in transition."); + } + } else { + if (mainChild == null || sideChild == null) { + throw new IllegalStateException("Launched 2 tasks in split, but didn't receive" + + " 2 tasks in transition. Possibly one of them failed to launch"); + } } - // Update local states (before animating). - mSplitLayout.init(); - setDividerVisibility(true, t); - setSplitsVisible(true); - - addDividerBarToTransition(info, t, true /* show */); - // Make some noise if things aren't totally expected. These states shouldn't effect // transitions locally, but remotes (like Launcher) may get confused if they were // depending on listener callbacks. This can happen because task-organizer callbacks // aren't serialized with transition callbacks. // TODO(b/184679596): Find a way to either include task-org information in // the transition, or synchronize task-org callbacks. - if (!mMainStage.containsTask(mainChild.getTaskInfo().taskId)) { + if (mainChild != null && !mMainStage.containsTask(mainChild.getTaskInfo().taskId)) { Log.w(TAG, "Expected onTaskAppeared on " + mMainStage + " to have been called with " + mainChild.getTaskInfo().taskId + " before startAnimation()."); } - if (!mSideStage.containsTask(sideChild.getTaskInfo().taskId)) { + if (sideChild != null && !mSideStage.containsTask(sideChild.getTaskInfo().taskId)) { Log.w(TAG, "Expected onTaskAppeared on " + mSideStage + " to have been called with " + sideChild.getTaskInfo().taskId + " before startAnimation()."); } - mShouldUpdateRecents = true; - updateRecentTasksSplitPair(); - - if (!mLogger.hasStartedSession()) { - mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(), - getMainStagePosition(), mMainStage.getTopChildTaskUid(), - getSideStagePosition(), mSideStage.getTopChildTaskUid(), - mSplitLayout.isLandscape()); - } - + finishEnterSplitScreen(t); + addDividerBarToTransition(info, t, true /* show */); return true; } 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 2c853c1c474c6..1f4bbe617dbec 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 @@ -301,6 +301,11 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { } void addTask(ActivityManager.RunningTaskInfo task, WindowContainerTransaction wct) { + // Clear overridden bounds and windowing mode to make sure the child task can inherit + // windowing mode and bounds from split root. + wct.setWindowingMode(task.token, WINDOWING_MODE_UNDEFINED) + .setBounds(task.token, null); + wct.reparent(task.token, mRootTaskInfo.token, true /* onTop*/); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index b8cbfd9680fd0..711510d9c1330 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -74,23 +74,25 @@ public class Transitions implements RemoteCallable { public static final boolean ENABLE_SHELL_TRANSITIONS = SystemProperties.getBoolean("persist.debug.shell_transit", false); - /** Transition type for dismissing split-screen via dragging the divider off the screen. */ - public static final int TRANSIT_SPLIT_DISMISS_SNAP = TRANSIT_FIRST_CUSTOM + 1; - - /** Transition type for launching 2 tasks simultaneously. */ - public static final int TRANSIT_SPLIT_SCREEN_PAIR_OPEN = TRANSIT_FIRST_CUSTOM + 2; - /** Transition type for exiting PIP via the Shell, via pressing the expand button. */ - public static final int TRANSIT_EXIT_PIP = TRANSIT_FIRST_CUSTOM + 3; + public static final int TRANSIT_EXIT_PIP = TRANSIT_FIRST_CUSTOM + 1; + + public static final int TRANSIT_EXIT_PIP_TO_SPLIT = TRANSIT_FIRST_CUSTOM + 2; /** Transition type for removing PIP via the Shell, either via Dismiss bubble or Close. */ - public static final int TRANSIT_REMOVE_PIP = TRANSIT_FIRST_CUSTOM + 4; + public static final int TRANSIT_REMOVE_PIP = TRANSIT_FIRST_CUSTOM + 3; + + /** Transition type for launching 2 tasks simultaneously. */ + public static final int TRANSIT_SPLIT_SCREEN_PAIR_OPEN = TRANSIT_FIRST_CUSTOM + 4; /** Transition type for entering split by opening an app into side-stage. */ public static final int TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE = TRANSIT_FIRST_CUSTOM + 5; + /** Transition type for dismissing split-screen via dragging the divider off the screen. */ + public static final int TRANSIT_SPLIT_DISMISS_SNAP = TRANSIT_FIRST_CUSTOM + 6; + /** Transition type for dismissing split-screen. */ - public static final int TRANSIT_SPLIT_DISMISS = TRANSIT_FIRST_CUSTOM + 6; + public static final int TRANSIT_SPLIT_DISMISS = TRANSIT_FIRST_CUSTOM + 7; private final WindowOrganizer mOrganizer; private final Context mContext;