Integrate RecetsTaskController with shell-transition
Hook up RecentsTaskController with split screen when enabled shell-transition. Bundle exit reason and dismiss transition together in order to log and determine whether the dismiss should break recorded app-pair in recents or not. When dismissing split screen with shell-transition, it'll reparent child tasks out of split screen before they committed invisible, so rely on fullscreen task opening signal to update app-pair information instead of relying on visible task appeared or changed in FullscreenTaskListener. Bug: 206487881 Test: atest SplitTransitionTests Test: dump and observe recorded split pair in RecentsTaskController Change-Id: I109d7b3b2eab2b4f691e85a2afd052f56b1a9bca
This commit is contained in:
@@ -23,6 +23,10 @@ import static android.view.WindowManager.TRANSIT_TO_BACK;
|
||||
import static android.view.WindowManager.TRANSIT_TO_FRONT;
|
||||
import static android.window.TransitionInfo.FLAG_FIRST_CUSTOM;
|
||||
|
||||
import static com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
|
||||
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;
|
||||
|
||||
@@ -40,7 +44,9 @@ import android.window.TransitionInfo;
|
||||
import android.window.WindowContainerToken;
|
||||
import android.window.WindowContainerTransaction;
|
||||
|
||||
import com.android.internal.protolog.common.ProtoLog;
|
||||
import com.android.wm.shell.common.TransactionPool;
|
||||
import com.android.wm.shell.protolog.ShellProtoLogGroup;
|
||||
import com.android.wm.shell.transition.OneShotRemoteHandler;
|
||||
import com.android.wm.shell.transition.Transitions;
|
||||
|
||||
@@ -57,7 +63,7 @@ class SplitScreenTransitions {
|
||||
private final Transitions mTransitions;
|
||||
private final Runnable mOnFinish;
|
||||
|
||||
IBinder mPendingDismiss = null;
|
||||
DismissTransition mPendingDismiss = null;
|
||||
IBinder mPendingEnter = null;
|
||||
|
||||
private IBinder mAnimatingTransition = null;
|
||||
@@ -127,12 +133,6 @@ class SplitScreenTransitions {
|
||||
}
|
||||
// TODO(shell-transitions): screenshot here
|
||||
final Rect startBounds = new Rect(change.getStartAbsBounds());
|
||||
if (info.getType() == TRANSIT_SPLIT_DISMISS_SNAP) {
|
||||
// Dismissing split via snap which means the still-visible task has been
|
||||
// dragged to its end position at animation start so reflect that here.
|
||||
startBounds.offsetTo(change.getEndAbsBounds().left,
|
||||
change.getEndAbsBounds().top);
|
||||
}
|
||||
final Rect endBounds = new Rect(change.getEndAbsBounds());
|
||||
startBounds.offset(-info.getRootOffset().x, -info.getRootOffset().y);
|
||||
endBounds.offset(-info.getRootOffset().x, -info.getRootOffset().y);
|
||||
@@ -184,12 +184,20 @@ class SplitScreenTransitions {
|
||||
return transition;
|
||||
}
|
||||
|
||||
/** Starts a transition for dismissing split after dragging the divider to a screen edge */
|
||||
IBinder startSnapToDismiss(@NonNull WindowContainerTransaction wct,
|
||||
@NonNull Transitions.TransitionHandler handler) {
|
||||
final IBinder transition = mTransitions.startTransition(
|
||||
TRANSIT_SPLIT_DISMISS_SNAP, wct, handler);
|
||||
mPendingDismiss = transition;
|
||||
/** Starts a transition to dismiss split. */
|
||||
IBinder startDismissTransition(@Nullable IBinder transition, WindowContainerTransaction wct,
|
||||
Transitions.TransitionHandler handler, @SplitScreen.StageType int dismissTop,
|
||||
@SplitScreenController.ExitReason int reason) {
|
||||
final int type = reason == EXIT_REASON_DRAG_DIVIDER
|
||||
? TRANSIT_SPLIT_DISMISS_SNAP : TRANSIT_SPLIT_DISMISS;
|
||||
if (transition == null) {
|
||||
transition = mTransitions.startTransition(type, wct, handler);
|
||||
}
|
||||
mPendingDismiss = new DismissTransition(transition, reason, dismissTop);
|
||||
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition "
|
||||
+ " deduced Dismiss due to %s. toTop=%s",
|
||||
exitReasonToString(reason), stageTypeToString(dismissTop));
|
||||
return transition;
|
||||
}
|
||||
|
||||
@@ -206,7 +214,7 @@ class SplitScreenTransitions {
|
||||
if (mAnimatingTransition == mPendingEnter) {
|
||||
mPendingEnter = null;
|
||||
}
|
||||
if (mAnimatingTransition == mPendingDismiss) {
|
||||
if (mPendingDismiss != null && mPendingDismiss.mTransition == mAnimatingTransition) {
|
||||
mPendingDismiss = null;
|
||||
}
|
||||
mAnimatingTransition = null;
|
||||
@@ -295,4 +303,20 @@ class SplitScreenTransitions {
|
||||
mAnimations.add(va);
|
||||
mTransitions.getAnimExecutor().execute(va::start);
|
||||
}
|
||||
|
||||
/** Bundled information of dismiss transition. */
|
||||
static class DismissTransition {
|
||||
IBinder mTransition;
|
||||
|
||||
int mReason;
|
||||
|
||||
@SplitScreen.StageType
|
||||
int mDismissTop;
|
||||
|
||||
DismissTransition(IBinder transition, int reason, int dismissTop) {
|
||||
this.mTransition = transition;
|
||||
this.mReason = reason;
|
||||
this.mDismissTop = dismissTop;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,9 +18,10 @@ package com.android.wm.shell.splitscreen;
|
||||
|
||||
import static android.app.ActivityOptions.KEY_LAUNCH_ROOT_TASK_TOKEN;
|
||||
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
|
||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
|
||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
|
||||
import static android.view.WindowManager.TRANSIT_OPEN;
|
||||
import static android.view.WindowManager.TRANSIT_TO_BACK;
|
||||
import static android.view.WindowManager.TRANSIT_TO_FRONT;
|
||||
import static android.view.WindowManager.transitTypeToString;
|
||||
@@ -32,7 +33,6 @@ import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSIT
|
||||
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_FINISHED;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP;
|
||||
@@ -40,6 +40,7 @@ import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON
|
||||
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_RETURN_HOME;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_UNKNOWN;
|
||||
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;
|
||||
@@ -121,9 +122,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
|
||||
private static final String TAG = StageCoordinator.class.getSimpleName();
|
||||
|
||||
/** internal value for mDismissTop that represents no dismiss */
|
||||
private static final int NO_DISMISS = -2;
|
||||
|
||||
private final SurfaceSession mSurfaceSession = new SurfaceSession();
|
||||
|
||||
private final MainStage mMainStage;
|
||||
@@ -156,9 +154,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
private boolean mKeyguardOccluded;
|
||||
private boolean mDeviceSleep;
|
||||
|
||||
@StageType
|
||||
private int mDismissTop = NO_DISMISS;
|
||||
|
||||
/** The target stage to dismiss to when unlock after folded. */
|
||||
@StageType
|
||||
private int mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
|
||||
@@ -171,7 +166,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
setDividerVisibility(false);
|
||||
mSplitLayout.resetDividerPosition();
|
||||
}
|
||||
mDismissTop = NO_DISMISS;
|
||||
};
|
||||
|
||||
private final SplitWindowManager.ParentContainerCallbacks mParentContainerCallbacks =
|
||||
@@ -461,8 +455,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
options = resolveStartStage(STAGE_TYPE_SIDE, position, options, wct);
|
||||
}
|
||||
} else {
|
||||
// Exit split-screen and launch fullscreen since stage wasn't specified.
|
||||
prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct);
|
||||
Slog.w(TAG,
|
||||
"No stage type nor split position specified to resolve start stage");
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -914,23 +908,22 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
IBinder onSnappedToDismissTransition(boolean mainStageToTop) {
|
||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
prepareExitSplitScreen(mainStageToTop ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE, wct);
|
||||
return mSplitTransitions.startSnapToDismiss(wct, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSnappedToDismiss(boolean bottomOrRight) {
|
||||
final boolean mainStageToTop =
|
||||
bottomOrRight ? mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT
|
||||
: mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT;
|
||||
if (ENABLE_SHELL_TRANSITIONS) {
|
||||
onSnappedToDismissTransition(mainStageToTop);
|
||||
|
||||
if (!ENABLE_SHELL_TRANSITIONS) {
|
||||
exitSplitScreen(mainStageToTop ? mMainStage : mSideStage, EXIT_REASON_DRAG_DIVIDER);
|
||||
return;
|
||||
}
|
||||
exitSplitScreen(mainStageToTop ? mMainStage : mSideStage, EXIT_REASON_DRAG_DIVIDER);
|
||||
|
||||
final int dismissTop = mainStageToTop ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE;
|
||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
prepareExitSplitScreen(dismissTop, wct);
|
||||
mSplitTransitions.startDismissTransition(
|
||||
null /* transition */, wct, this, dismissTop, EXIT_REASON_DRAG_DIVIDER);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1109,14 +1102,25 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
@Nullable TransitionRequestInfo request) {
|
||||
final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask();
|
||||
if (triggerTask == null) {
|
||||
// still want to monitor everything while in split-screen, so return non-null.
|
||||
// Still want to monitor everything while in split-screen, so return non-null.
|
||||
return isSplitScreenVisible() ? new WindowContainerTransaction() : null;
|
||||
} else if (triggerTask.displayId != mDisplayId) {
|
||||
// Skip handling task on the other display.
|
||||
return null;
|
||||
}
|
||||
|
||||
WindowContainerTransaction out = null;
|
||||
final @WindowManager.TransitionType int type = request.getType();
|
||||
final boolean isOpening = isOpeningType(type);
|
||||
final boolean inFullscreen = triggerTask.getWindowingMode() == WINDOWING_MODE_FULLSCREEN;
|
||||
|
||||
if (isOpening && inFullscreen) {
|
||||
// One task is opening into fullscreen mode, remove the corresponding split record.
|
||||
mRecentTasks.ifPresent(recentTasks -> recentTasks.removeSplitPair(triggerTask.taskId));
|
||||
}
|
||||
|
||||
if (isSplitScreenVisible()) {
|
||||
// try to handle everything while in split-screen, so return a WCT even if it's empty.
|
||||
// Try to handle everything while in split-screen, so return a WCT even if it's empty.
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " split is active so using split"
|
||||
+ "Transition to handle request. triggerTask=%d type=%s mainChildren=%d"
|
||||
+ " sideChildren=%d", triggerTask.taskId, transitTypeToString(type),
|
||||
@@ -1124,35 +1128,35 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
out = new WindowContainerTransaction();
|
||||
final StageTaskListener stage = getStageOfTask(triggerTask);
|
||||
if (stage != null) {
|
||||
// dismiss split if the last task in one of the stages is going away
|
||||
// Dismiss split if the last task in one of the stages is going away
|
||||
if (isClosingType(type) && stage.getChildCount() == 1) {
|
||||
// The top should be the opposite side that is closing:
|
||||
mDismissTop = getStageType(stage) == STAGE_TYPE_MAIN
|
||||
? STAGE_TYPE_SIDE : STAGE_TYPE_MAIN;
|
||||
int dismissTop = getStageType(stage) == STAGE_TYPE_MAIN ? STAGE_TYPE_SIDE
|
||||
: STAGE_TYPE_MAIN;
|
||||
prepareExitSplitScreen(dismissTop, out);
|
||||
mSplitTransitions.startDismissTransition(transition, out, this, dismissTop,
|
||||
EXIT_REASON_APP_FINISHED);
|
||||
}
|
||||
} else {
|
||||
if (triggerTask.getActivityType() == ACTIVITY_TYPE_HOME && isOpeningType(type)) {
|
||||
// Going home so dismiss both.
|
||||
mDismissTop = STAGE_TYPE_UNDEFINED;
|
||||
} else if (isOpening && inFullscreen) {
|
||||
final int activityType = triggerTask.getActivityType();
|
||||
if (activityType == ACTIVITY_TYPE_ASSISTANT) {
|
||||
// We don't want assistant panel to dismiss split screen, so do nothing.
|
||||
} else {
|
||||
// Going home or occluded by the other fullscreen task, so dismiss both.
|
||||
prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, out);
|
||||
final int exitReason = activityType == ACTIVITY_TYPE_HOME
|
||||
? EXIT_REASON_RETURN_HOME : EXIT_REASON_UNKNOWN;
|
||||
mSplitTransitions.startDismissTransition(transition, out, this,
|
||||
STAGE_TYPE_UNDEFINED, exitReason);
|
||||
}
|
||||
}
|
||||
if (mDismissTop != NO_DISMISS) {
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition "
|
||||
+ " deduced Dismiss from request. toTop=%s",
|
||||
stageTypeToString(mDismissTop));
|
||||
prepareExitSplitScreen(mDismissTop, out);
|
||||
mSplitTransitions.mPendingDismiss = transition;
|
||||
}
|
||||
} else {
|
||||
// Not in split mode, so look for an open into a split stage to active split screen.
|
||||
if ((type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT)) {
|
||||
if (getStageOfTask(triggerTask) != null) {
|
||||
// One task is appearing in split, prepare to enter split screen.
|
||||
out = new WindowContainerTransaction();
|
||||
mSplitTransitions.mPendingEnter = transition;
|
||||
mMainStage.activate(getMainStageBounds(), out, true /* includingTopTask */);
|
||||
mSideStage.moveToTop(getSideStageBounds(), out);
|
||||
}
|
||||
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);
|
||||
mSplitTransitions.mPendingEnter = transition;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
@@ -1164,8 +1168,9 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
@NonNull SurfaceControl.Transaction startTransaction,
|
||||
@NonNull SurfaceControl.Transaction finishTransaction,
|
||||
@NonNull Transitions.TransitionFinishCallback finishCallback) {
|
||||
if (transition != mSplitTransitions.mPendingDismiss
|
||||
&& transition != mSplitTransitions.mPendingEnter) {
|
||||
if (transition != mSplitTransitions.mPendingEnter && (
|
||||
mSplitTransitions.mPendingDismiss == null
|
||||
|| mSplitTransitions.mPendingDismiss.mTransition != transition)) {
|
||||
// Not entering or exiting, so just do some house-keeping and validation.
|
||||
|
||||
// If we're not in split-mode, just abort so something else can handle it.
|
||||
@@ -1208,8 +1213,10 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
boolean shouldAnimate = true;
|
||||
if (mSplitTransitions.mPendingEnter == transition) {
|
||||
shouldAnimate = startPendingEnterAnimation(transition, info, startTransaction);
|
||||
} else if (mSplitTransitions.mPendingDismiss == transition) {
|
||||
shouldAnimate = startPendingDismissAnimation(transition, info, startTransaction);
|
||||
} else if (mSplitTransitions.mPendingDismiss != null
|
||||
&& mSplitTransitions.mPendingDismiss.mTransition == transition) {
|
||||
shouldAnimate = startPendingDismissAnimation(
|
||||
mSplitTransitions.mPendingDismiss, info, startTransaction);
|
||||
}
|
||||
if (!shouldAnimate) return false;
|
||||
|
||||
@@ -1263,10 +1270,22 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
+ " 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());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean startPendingDismissAnimation(@NonNull IBinder transition,
|
||||
private boolean startPendingDismissAnimation(
|
||||
@NonNull SplitScreenTransitions.DismissTransition dismissTransition,
|
||||
@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t) {
|
||||
// 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
|
||||
@@ -1295,6 +1314,21 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
+ "] before startAnimation().");
|
||||
}
|
||||
|
||||
mRecentTasks.ifPresent(recentTasks -> {
|
||||
// Notify recents if we are exiting in a way that breaks the pair, and disable further
|
||||
// updates to splits in the recents until we enter split again
|
||||
if (shouldBreakPairedTaskInRecents(dismissTransition.mReason) && mShouldUpdateRecents) {
|
||||
for (TransitionInfo.Change change : info.getChanges()) {
|
||||
final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
|
||||
if (taskInfo != null
|
||||
&& taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
|
||||
recentTasks.removeSplitPair(taskInfo.taskId);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
mShouldUpdateRecents = false;
|
||||
|
||||
// Update local states.
|
||||
setSplitsVisible(false);
|
||||
// Wait until after animation to update divider
|
||||
@@ -1305,15 +1339,17 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
t.setWindowCrop(mSideStage.mRootLeash, null);
|
||||
}
|
||||
|
||||
if (mDismissTop == STAGE_TYPE_UNDEFINED) {
|
||||
// Going home (dismissing both splits)
|
||||
|
||||
if (dismissTransition.mDismissTop == STAGE_TYPE_UNDEFINED) {
|
||||
logExit(dismissTransition.mReason);
|
||||
// TODO: Have a proper remote for this. Until then, though, reset state and use the
|
||||
// normal animation stuff (which falls back to the normal launcher remote).
|
||||
t.hide(mSplitLayout.getDividerLeash());
|
||||
setDividerVisibility(false);
|
||||
mSplitTransitions.mPendingDismiss = null;
|
||||
return false;
|
||||
} else {
|
||||
logExitToStage(dismissTransition.mReason,
|
||||
dismissTransition.mDismissTop == STAGE_TYPE_MAIN);
|
||||
}
|
||||
|
||||
addDividerBarToTransition(info, t, false /* show */);
|
||||
@@ -1457,7 +1493,15 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
@Override
|
||||
public void onNoLongerSupportMultiWindow() {
|
||||
if (mMainStage.isActive()) {
|
||||
StageCoordinator.this.exitSplitScreen(null /* childrenToTop */,
|
||||
if (!ENABLE_SHELL_TRANSITIONS) {
|
||||
StageCoordinator.this.exitSplitScreen(null /* childrenToTop */,
|
||||
EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW);
|
||||
}
|
||||
|
||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct);
|
||||
mSplitTransitions.startDismissTransition(null /* transition */, wct,
|
||||
StageCoordinator.this, STAGE_TYPE_UNDEFINED,
|
||||
EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,12 +127,6 @@ class SplitScreenTransitions {
|
||||
}
|
||||
// TODO(shell-transitions): screenshot here
|
||||
final Rect startBounds = new Rect(change.getStartAbsBounds());
|
||||
if (info.getType() == TRANSIT_SPLIT_DISMISS_SNAP) {
|
||||
// Dismissing split via snap which means the still-visible task has been
|
||||
// dragged to its end position at animation start so reflect that here.
|
||||
startBounds.offsetTo(change.getEndAbsBounds().left,
|
||||
change.getEndAbsBounds().top);
|
||||
}
|
||||
final Rect endBounds = new Rect(change.getEndAbsBounds());
|
||||
startBounds.offset(-info.getRootOffset().x, -info.getRootOffset().y);
|
||||
endBounds.offset(-info.getRootOffset().x, -info.getRootOffset().y);
|
||||
|
||||
@@ -89,6 +89,9 @@ public class Transitions implements RemoteCallable<Transitions> {
|
||||
/** 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. */
|
||||
public static final int TRANSIT_SPLIT_DISMISS = TRANSIT_FIRST_CUSTOM + 6;
|
||||
|
||||
private final WindowOrganizer mOrganizer;
|
||||
private final Context mContext;
|
||||
private final ShellExecutor mMainExecutor;
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.wm.shell;
|
||||
|
||||
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
|
||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
|
||||
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
@@ -36,6 +37,7 @@ public final class TestRunningTaskInfoBuilder {
|
||||
private WindowContainerToken mToken = createMockWCToken();
|
||||
private int mParentTaskId = INVALID_TASK_ID;
|
||||
private @WindowConfiguration.ActivityType int mActivityType = ACTIVITY_TYPE_STANDARD;
|
||||
private @WindowConfiguration.WindowingMode int mWindowingMode = WINDOWING_MODE_UNDEFINED;
|
||||
|
||||
public static WindowContainerToken createMockWCToken() {
|
||||
final IWindowContainerToken itoken = mock(IWindowContainerToken.class);
|
||||
@@ -60,6 +62,12 @@ public final class TestRunningTaskInfoBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public TestRunningTaskInfoBuilder setWindowingMode(
|
||||
@WindowConfiguration.WindowingMode int windowingMode) {
|
||||
mWindowingMode = windowingMode;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ActivityManager.RunningTaskInfo build() {
|
||||
final ActivityManager.RunningTaskInfo info = new ActivityManager.RunningTaskInfo();
|
||||
info.parentTaskId = INVALID_TASK_ID;
|
||||
@@ -67,6 +75,7 @@ public final class TestRunningTaskInfoBuilder {
|
||||
info.parentTaskId = mParentTaskId;
|
||||
info.configuration.windowConfiguration.setBounds(mBounds);
|
||||
info.configuration.windowConfiguration.setActivityType(mActivityType);
|
||||
info.configuration.windowConfiguration.setWindowingMode(mWindowingMode);
|
||||
info.token = mToken;
|
||||
info.isResizeable = true;
|
||||
info.supportsMultiWindow = true;
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.wm.shell.splitscreen;
|
||||
|
||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
import static android.view.WindowManager.TRANSIT_CHANGE;
|
||||
import static android.view.WindowManager.TRANSIT_CLOSE;
|
||||
@@ -26,6 +27,9 @@ import static android.view.WindowManager.TRANSIT_TO_FRONT;
|
||||
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT;
|
||||
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER;
|
||||
|
||||
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
|
||||
import static com.android.wm.shell.splitscreen.SplitTestUtils.createMockSurface;
|
||||
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
|
||||
|
||||
@@ -249,7 +253,9 @@ public class SplitTransitionTests extends ShellTestCase {
|
||||
enterSplit();
|
||||
|
||||
ActivityManager.RunningTaskInfo homeTask = new TestRunningTaskInfoBuilder()
|
||||
.setActivityType(ACTIVITY_TYPE_HOME).build();
|
||||
.setWindowingMode(WINDOWING_MODE_FULLSCREEN)
|
||||
.setActivityType(ACTIVITY_TYPE_HOME)
|
||||
.build();
|
||||
|
||||
// Create a request to bring home forward
|
||||
TransitionRequestInfo request = new TransitionRequestInfo(TRANSIT_TO_FRONT, homeTask, null);
|
||||
@@ -279,6 +285,64 @@ public class SplitTransitionTests extends ShellTestCase {
|
||||
assertFalse(mStageCoordinator.isSplitScreenVisible());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDismissFromBeingOccluded() {
|
||||
enterSplit();
|
||||
|
||||
ActivityManager.RunningTaskInfo normalTask = new TestRunningTaskInfoBuilder()
|
||||
.setWindowingMode(WINDOWING_MODE_FULLSCREEN)
|
||||
.build();
|
||||
|
||||
// Create a request to bring a normal task forward
|
||||
TransitionRequestInfo request =
|
||||
new TransitionRequestInfo(TRANSIT_TO_FRONT, normalTask, null);
|
||||
IBinder transition = mock(IBinder.class);
|
||||
WindowContainerTransaction result = mStageCoordinator.handleRequest(transition, request);
|
||||
|
||||
assertTrue(containsSplitExit(result));
|
||||
|
||||
// make sure we haven't made any local changes yet (need to wait until transition is ready)
|
||||
assertTrue(mStageCoordinator.isSplitScreenVisible());
|
||||
|
||||
// simulate the transition
|
||||
TransitionInfo.Change normalChange = createChange(TRANSIT_TO_FRONT, normalTask);
|
||||
TransitionInfo.Change mainChange = createChange(TRANSIT_TO_BACK, mMainChild);
|
||||
TransitionInfo.Change sideChange = createChange(TRANSIT_TO_BACK, mSideChild);
|
||||
|
||||
TransitionInfo info = new TransitionInfo(TRANSIT_TO_FRONT, 0);
|
||||
info.addChange(normalChange);
|
||||
info.addChange(mainChange);
|
||||
info.addChange(sideChange);
|
||||
mMainStage.onTaskVanished(mMainChild);
|
||||
mSideStage.onTaskVanished(mSideChild);
|
||||
mStageCoordinator.startAnimation(transition, info,
|
||||
mock(SurfaceControl.Transaction.class),
|
||||
mock(SurfaceControl.Transaction.class),
|
||||
mock(Transitions.TransitionFinishCallback.class));
|
||||
assertFalse(mStageCoordinator.isSplitScreenVisible());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDismissFromMultiWindowSupport() {
|
||||
enterSplit();
|
||||
|
||||
// simulate the transition
|
||||
TransitionInfo.Change mainChange = createChange(TRANSIT_TO_BACK, mMainChild);
|
||||
TransitionInfo.Change sideChange = createChange(TRANSIT_TO_BACK, mSideChild);
|
||||
TransitionInfo info = new TransitionInfo(TRANSIT_TO_BACK, 0);
|
||||
info.addChange(mainChange);
|
||||
info.addChange(sideChange);
|
||||
IBinder transition = mSplitScreenTransitions.startDismissTransition(null,
|
||||
new WindowContainerTransaction(), mStageCoordinator,
|
||||
EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW, STAGE_TYPE_SIDE);
|
||||
boolean accepted = mStageCoordinator.startAnimation(transition, info,
|
||||
mock(SurfaceControl.Transaction.class),
|
||||
mock(SurfaceControl.Transaction.class),
|
||||
mock(Transitions.TransitionFinishCallback.class));
|
||||
assertTrue(accepted);
|
||||
assertFalse(mStageCoordinator.isSplitScreenVisible());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDismissSnap() {
|
||||
enterSplit();
|
||||
@@ -290,8 +354,9 @@ public class SplitTransitionTests extends ShellTestCase {
|
||||
TransitionInfo info = new TransitionInfo(TRANSIT_TO_BACK, 0);
|
||||
info.addChange(mainChange);
|
||||
info.addChange(sideChange);
|
||||
IBinder transition = mStageCoordinator.onSnappedToDismissTransition(
|
||||
false /* mainStageToTop */);
|
||||
IBinder transition = mSplitScreenTransitions.startDismissTransition(null,
|
||||
new WindowContainerTransaction(), mStageCoordinator, EXIT_REASON_DRAG_DIVIDER,
|
||||
STAGE_TYPE_SIDE);
|
||||
mMainStage.onTaskVanished(mMainChild);
|
||||
mSideStage.onTaskVanished(mSideChild);
|
||||
boolean accepted = mStageCoordinator.startAnimation(transition, info,
|
||||
|
||||
Reference in New Issue
Block a user