Move recents-during-split handling to MixedHandler

This is a mixed operation so it shouldn't be handled
internally to split. Also, this will be needed once
recents are directly requested vs. coming from Core.

Bug: 269691848
Test: moving code around, so existing tests pass.
Change-Id: Ia8aeaace6cb3fca04b641c6c1c1890d06f1bdbd0
This commit is contained in:
Evan Rosky
2023-03-09 21:12:03 -08:00
parent ccd9936912
commit 6358dddd6a
4 changed files with 156 additions and 91 deletions

View File

@@ -67,7 +67,6 @@ class SplitScreenTransitions {
DismissTransition mPendingDismiss = null;
TransitSession mPendingEnter = null;
TransitSession mPendingRecent = null;
TransitSession mPendingResize = null;
private IBinder mAnimatingTransition = null;
@@ -257,10 +256,6 @@ class SplitScreenTransitions {
return mPendingEnter != null && mPendingEnter.mTransition == transition;
}
boolean isPendingRecent(IBinder transition) {
return mPendingRecent != null && mPendingRecent.mTransition == transition;
}
boolean isPendingDismiss(IBinder transition) {
return mPendingDismiss != null && mPendingDismiss.mTransition == transition;
}
@@ -273,8 +268,6 @@ class SplitScreenTransitions {
private TransitSession getPendingTransition(IBinder transition) {
if (isPendingEnter(transition)) {
return mPendingEnter;
} else if (isPendingRecent(transition)) {
return mPendingRecent;
} else if (isPendingDismiss(transition)) {
return mPendingDismiss;
} else if (isPendingResize(transition)) {
@@ -358,32 +351,10 @@ class SplitScreenTransitions {
+ " deduced Resize split screen");
}
void setRecentTransition(@NonNull IBinder transition,
@Nullable RemoteTransition remoteTransition,
@Nullable TransitionFinishedCallback finishCallback) {
mPendingRecent = new TransitSession(transition, null /* consumedCb */, finishCallback);
if (remoteTransition != null) {
// Wrapping it for ease-of-use (OneShot handles all the binder linking/death stuff)
mPendingRemoteHandler = new OneShotRemoteHandler(
mTransitions.getMainExecutor(), remoteTransition);
mPendingRemoteHandler.setTransition(transition);
}
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition "
+ " deduced Enter recent panel");
}
void mergeAnimation(IBinder transition, TransitionInfo info, SurfaceControl.Transaction t,
IBinder mergeTarget, Transitions.TransitionFinishCallback finishCallback) {
if (mergeTarget != mAnimatingTransition) return;
if (isPendingEnter(transition) && isPendingRecent(mergeTarget)) {
// Since there's an entering transition merged, recent transition no longer
// need to handle entering split screen after the transition finished.
mPendingRecent.setFinishedCallback(null);
}
if (mActiveRemoteHandler != null) {
mActiveRemoteHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback);
} else {
@@ -420,10 +391,6 @@ class SplitScreenTransitions {
} else if (isPendingDismiss(transition)) {
mPendingDismiss.onConsumed(aborted);
mPendingDismiss = null;
} else if (isPendingRecent(transition)) {
mPendingRecent.onConsumed(aborted);
mPendingRecent = null;
mPendingRemoteHandler = null;
} else if (isPendingResize(transition)) {
mPendingResize.onConsumed(aborted);
mPendingResize = null;
@@ -437,9 +404,6 @@ class SplitScreenTransitions {
if (isPendingEnter(mAnimatingTransition)) {
mPendingEnter.onFinished(wct, mFinishTransaction);
mPendingEnter = null;
} else if (isPendingRecent(mAnimatingTransition)) {
mPendingRecent.onFinished(wct, mFinishTransaction);
mPendingRecent = null;
} else if (isPendingDismiss(mAnimatingTransition)) {
mPendingDismiss.onFinished(wct, mFinishTransaction);
mPendingDismiss = null;
@@ -549,7 +513,7 @@ class SplitScreenTransitions {
}
/** Calls when the transition finished. */
interface TransitionFinishedCallback {
public interface TransitionFinishedCallback {
void onFinished(WindowContainerTransaction wct, SurfaceControl.Transaction t);
}

View File

@@ -259,37 +259,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
};
private final SplitScreenTransitions.TransitionFinishedCallback
mRecentTransitionFinishedCallback =
new SplitScreenTransitions.TransitionFinishedCallback() {
@Override
public void onFinished(WindowContainerTransaction finishWct,
SurfaceControl.Transaction finishT) {
// Check if the recent transition is finished by returning to the current
// split, so we
// can restore the divider bar.
for (int i = 0; i < finishWct.getHierarchyOps().size(); ++i) {
final WindowContainerTransaction.HierarchyOp op =
finishWct.getHierarchyOps().get(i);
final IBinder container = op.getContainer();
if (op.getType() == HIERARCHY_OP_TYPE_REORDER && op.getToTop()
&& (mMainStage.containsContainer(container)
|| mSideStage.containsContainer(container))) {
updateSurfaceBounds(mSplitLayout, finishT,
false /* applyResizingOffset */);
setDividerVisibility(true, finishT);
return;
}
}
// Dismiss the split screen if it's not returning to split.
prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, finishWct);
setSplitsVisible(false);
setDividerVisibility(false, finishT);
logExit(EXIT_REASON_UNKNOWN);
}
};
protected StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
ShellTaskOrganizer taskOrganizer, DisplayController displayController,
DisplayImeController displayImeController,
@@ -388,6 +357,11 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
return mMainStage.isActive();
}
/** Checks if `transition` is a pending enter-split transition. */
public boolean isPendingEnter(IBinder transition) {
return mSplitTransitions.isPendingEnter(transition);
}
@StageType
int getStageOfTask(int taskId) {
if (mMainStage.containsTask(taskId)) {
@@ -2266,9 +2240,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
final int activityType = triggerTask.getActivityType();
if (activityType == ACTIVITY_TYPE_HOME
|| activityType == ACTIVITY_TYPE_RECENTS) {
// Enter overview panel, so start recent transition.
mSplitTransitions.setRecentTransition(transition, request.getRemoteTransition(),
mRecentTransitionFinishedCallback);
// starting recents, so don't handle this.
return null;
}
}
} else {
@@ -2397,8 +2370,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
if (mSplitTransitions.isPendingEnter(transition)) {
shouldAnimate = startPendingEnterAnimation(
transition, info, startTransaction, finishTransaction);
} else if (mSplitTransitions.isPendingRecent(transition)) {
shouldAnimate = startPendingRecentAnimation(transition, info, startTransaction);
} else if (mSplitTransitions.isPendingDismiss(transition)) {
shouldAnimate = startPendingDismissAnimation(
mSplitTransitions.mPendingDismiss, info, startTransaction, finishTransaction);
@@ -2653,10 +2624,35 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
return true;
}
private boolean startPendingRecentAnimation(@NonNull IBinder transition,
@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t) {
/** Call this when starting the open-recents animation while split-screen is active. */
public void onRecentsInSplitAnimationStart(@NonNull SurfaceControl.Transaction t) {
setDividerVisibility(false, t);
return true;
}
/** Call this when the recents animation during split-screen finishes. */
public void onRecentsInSplitAnimationFinish(WindowContainerTransaction finishWct,
SurfaceControl.Transaction finishT) {
// Check if the recent transition is finished by returning to the current
// split, so we can restore the divider bar.
for (int i = 0; i < finishWct.getHierarchyOps().size(); ++i) {
final WindowContainerTransaction.HierarchyOp op =
finishWct.getHierarchyOps().get(i);
final IBinder container = op.getContainer();
if (op.getType() == HIERARCHY_OP_TYPE_REORDER && op.getToTop()
&& (mMainStage.containsContainer(container)
|| mSideStage.containsContainer(container))) {
updateSurfaceBounds(mSplitLayout, finishT,
false /* applyResizingOffset */);
setDividerVisibility(true, finishT);
return;
}
}
// Dismiss the split screen if it's not returning to split.
prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, finishWct);
setSplitsVisible(false);
setDividerVisibility(false, finishT);
logExit(EXIT_REASON_UNKNOWN);
}
private void addDividerBarToTransition(@NonNull TransitionInfo info,

View File

@@ -18,6 +18,7 @@ package com.android.wm.shell.transition;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
@@ -25,6 +26,7 @@ import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP;
import static com.android.wm.shell.util.TransitionUtil.isOpeningType;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -68,14 +70,20 @@ 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. */
static final int TYPE_RECENTS_DURING_SPLIT = 4;
/** The default animation for this mixed transition. */
static final int ANIM_TYPE_DEFAULT = 0;
/** For ENTER_PIP_FROM_SPLIT, indicates that this is a to-home animation. */
static final int ANIM_TYPE_GOING_HOME = 1;
/** For RECENTS_DURING_SPLIT, is set when this turns into a pair->pair task switch. */
static final int ANIM_TYPE_PAIR_TO_PAIR = 1;
final int mType;
int mAnimType = 0;
int mAnimType = ANIM_TYPE_DEFAULT;
final IBinder mTransition;
Transitions.TransitionHandler mLeftoversHandler = null;
@@ -167,6 +175,25 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler {
mixed.mLeftoversHandler = handler.first;
mActiveTransitions.add(mixed);
return handler.second;
} else if (mSplitHandler.isSplitActive()
&& isOpeningType(request.getType())
&& request.getTriggerTask() != null
&& request.getTriggerTask().getWindowingMode() == WINDOWING_MODE_FULLSCREEN
&& (request.getTriggerTask().getActivityType() == ACTIVITY_TYPE_HOME
|| request.getTriggerTask().getActivityType() == ACTIVITY_TYPE_RECENTS)) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a recents request while "
+ "Split-Screen is active, so treat it as Mixed.");
Pair<Transitions.TransitionHandler, WindowContainerTransaction> handler =
mPlayer.dispatchRequest(transition, request, this);
if (handler == null) {
// fall through -- it will probably be picked-up by normal split handler.
return null;
}
final MixedTransition mixed = new MixedTransition(
MixedTransition.TYPE_RECENTS_DURING_SPLIT, transition);
mixed.mLeftoversHandler = handler.first;
mActiveTransitions.add(mixed);
return handler.second;
}
return null;
}
@@ -214,6 +241,9 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler {
} else if (mixed.mType == MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) {
return animateOpenIntentWithRemoteAndPip(mixed, info, startTransaction,
finishTransaction, finishCallback);
} else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_SPLIT) {
return animateRecentsDuringSplit(mixed, info, startTransaction, finishTransaction,
finishCallback);
} else {
mActiveTransitions.remove(mixed);
throw new IllegalStateException("Starting mixed animation without a known mixed type? "
@@ -439,12 +469,40 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler {
return true;
}
private boolean animateRecentsDuringSplit(@NonNull final MixedTransition mixed,
@NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
// Split-screen is only interested in the recents transition finishing (and merging), so
// just wrap finish and start recents animation directly.
Transitions.TransitionFinishCallback finishCB = (wct, wctCB) -> {
mixed.mInFlightSubAnimations = 0;
mActiveTransitions.remove(mixed);
// If pair-to-pair switching, the post-recents clean-up isn't needed.
if (mixed.mAnimType != MixedTransition.ANIM_TYPE_PAIR_TO_PAIR) {
wct = wct != null ? wct : new WindowContainerTransaction();
mSplitHandler.onRecentsInSplitAnimationFinish(wct, finishTransaction);
}
mSplitHandler.onTransitionAnimationComplete();
finishCallback.onTransitionFinished(wct, wctCB);
};
mixed.mInFlightSubAnimations = 1;
mSplitHandler.onRecentsInSplitAnimationStart(startTransaction);
final boolean handled = mixed.mLeftoversHandler.startAnimation(mixed.mTransition, info,
startTransaction, finishTransaction, finishCB);
if (!handled) {
mActiveTransitions.remove(mixed);
}
return handled;
}
@Override
public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
for (int i = 0; i < mActiveTransitions.size(); ++i) {
if (mActiveTransitions.get(i) != mergeTarget) continue;
if (mActiveTransitions.get(i).mTransition != mergeTarget) continue;
MixedTransition mixed = mActiveTransitions.get(i);
if (mixed.mInFlightSubAnimations <= 0) {
// Already done, so no need to end it.
@@ -472,6 +530,14 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler {
mixed.mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget,
finishCallback);
}
} else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_SPLIT) {
if (mSplitHandler.isPendingEnter(transition)) {
// Recents -> enter-split means that we are switching from one pair to
// another pair.
mixed.mAnimType = MixedTransition.ANIM_TYPE_PAIR_TO_PAIR;
}
mixed.mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget,
finishCallback);
} else {
throw new IllegalStateException("Playing a mixed transition with unknown type? "
+ mixed.mType);
@@ -491,6 +557,8 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler {
if (mixed == null) return;
if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) {
mPipHandler.onTransitionConsumed(transition, aborted, finishT);
} else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_SPLIT) {
mixed.mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT);
}
}
}

View File

@@ -35,6 +35,7 @@ import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_P
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -249,7 +250,7 @@ public class SplitTransitionTests extends ShellTestCase {
@Test
@UiThreadTest
public void testEnterRecents() {
public void testEnterRecentsAndCommit() {
enterSplit();
ActivityManager.RunningTaskInfo homeTask = new TestRunningTaskInfoBuilder()
@@ -261,24 +262,60 @@ public class SplitTransitionTests extends ShellTestCase {
TransitionRequestInfo request = new TransitionRequestInfo(TRANSIT_TO_FRONT, homeTask, null);
IBinder transition = mock(IBinder.class);
WindowContainerTransaction result = mStageCoordinator.handleRequest(transition, request);
assertTrue(result.isEmpty());
// Don't handle recents opening
assertNull(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 info = new TransitionInfoBuilder(TRANSIT_TO_FRONT, 0)
.addChange(TRANSIT_TO_FRONT, homeTask)
.addChange(TRANSIT_TO_BACK, mMainChild)
.addChange(TRANSIT_TO_BACK, mSideChild)
.build();
// simulate the start of recents transition
mMainStage.onTaskVanished(mMainChild);
mSideStage.onTaskVanished(mSideChild);
mStageCoordinator.startAnimation(transition, info,
mock(SurfaceControl.Transaction.class),
mock(SurfaceControl.Transaction.class),
mock(Transitions.TransitionFinishCallback.class));
mStageCoordinator.onRecentsInSplitAnimationStart(mock(SurfaceControl.Transaction.class));
assertTrue(mStageCoordinator.isSplitScreenVisible());
// Make sure it cleans-up if recents doesn't restore
WindowContainerTransaction commitWCT = new WindowContainerTransaction();
mStageCoordinator.onRecentsInSplitAnimationFinish(commitWCT,
mock(SurfaceControl.Transaction.class));
assertFalse(mStageCoordinator.isSplitScreenVisible());
}
@Test
@UiThreadTest
public void testEnterRecentsAndRestore() {
enterSplit();
ActivityManager.RunningTaskInfo homeTask = new TestRunningTaskInfoBuilder()
.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);
IBinder transition = mock(IBinder.class);
WindowContainerTransaction result = mStageCoordinator.handleRequest(transition, request);
// Don't handle recents opening
assertNull(result);
// make sure we haven't made any local changes yet (need to wait until transition is ready)
assertTrue(mStageCoordinator.isSplitScreenVisible());
// simulate the start of recents transition
mMainStage.onTaskVanished(mMainChild);
mSideStage.onTaskVanished(mSideChild);
mStageCoordinator.onRecentsInSplitAnimationStart(mock(SurfaceControl.Transaction.class));
assertTrue(mStageCoordinator.isSplitScreenVisible());
// Make sure we remain in split after recents restores.
WindowContainerTransaction restoreWCT = new WindowContainerTransaction();
restoreWCT.reorder(mMainChild.token, true /* toTop */);
restoreWCT.reorder(mSideChild.token, true /* toTop */);
// simulate the restoreWCT being applied:
mMainStage.onTaskAppeared(mMainChild, mock(SurfaceControl.class));
mSideStage.onTaskAppeared(mSideChild, mock(SurfaceControl.class));
mStageCoordinator.onRecentsInSplitAnimationFinish(restoreWCT,
mock(SurfaceControl.Transaction.class));
assertTrue(mStageCoordinator.isSplitScreenVisible());
}