Support to merge transition to remote handler while in split screen mode

Support to merge transition to activating remote transition while in
split screen. Like the case openning a normal app from overview during
split screen mode, it'll trigger a new app openning transition which
should be merged back to the remote transition running in launcher side
to finish the recent animation.

If a split dismissing transition was triggered right after recent
transition finished. It might be a no-op transition and thus won't
callback startAnimation. So also reset split screen state and visibility
when finishing transition.

Bug: 206487881
Test: enable shell transition and enter split screen, observed the
      transition switching to normal app won't hang.
Change-Id: I9bac5eab844f7835c69f9aad17a5c50dc01a76fa
This commit is contained in:
Jerry Chang
2022-01-20 04:23:37 +00:00
parent 1b7cf5a207
commit f37bfeaa2f
2 changed files with 60 additions and 34 deletions

View File

@@ -70,7 +70,8 @@ class SplitScreenTransitions {
IBinder mPendingRecent = null;
private IBinder mAnimatingTransition = null;
private OneShotRemoteHandler mRemoteHandler = null;
private OneShotRemoteHandler mPendingRemoteHandler = null;
private OneShotRemoteHandler mActiveRemoteHandler = null;
private final Transitions.TransitionFinishCallback mRemoteFinishCB = this::onFinish;
@@ -96,10 +97,11 @@ class SplitScreenTransitions {
@NonNull WindowContainerToken mainRoot, @NonNull WindowContainerToken sideRoot) {
mFinishCallback = finishCallback;
mAnimatingTransition = transition;
if (mRemoteHandler != null) {
mRemoteHandler.startAnimation(transition, info, startTransaction, finishTransaction,
mRemoteFinishCB);
mRemoteHandler = null;
if (mPendingRemoteHandler != null) {
mPendingRemoteHandler.startAnimation(transition, info, startTransaction,
finishTransaction, mRemoteFinishCB);
mActiveRemoteHandler = mPendingRemoteHandler;
mPendingRemoteHandler = null;
return;
}
playInternalAnimation(transition, info, startTransaction, mainRoot, sideRoot);
@@ -172,15 +174,14 @@ class SplitScreenTransitions {
IBinder startEnterTransition(@WindowManager.TransitionType int transitType,
@NonNull WindowContainerTransaction wct, @Nullable RemoteTransition remoteTransition,
@NonNull Transitions.TransitionHandler handler) {
if (remoteTransition != null) {
// Wrapping it for ease-of-use (OneShot handles all the binder linking/death stuff)
mRemoteHandler = new OneShotRemoteHandler(
mTransitions.getMainExecutor(), remoteTransition);
}
final IBinder transition = mTransitions.startTransition(transitType, wct, handler);
mPendingEnter = transition;
if (mRemoteHandler != null) {
mRemoteHandler.setTransition(transition);
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);
}
return transition;
}
@@ -211,9 +212,9 @@ class SplitScreenTransitions {
if (remoteTransition != null) {
// Wrapping it for ease-of-use (OneShot handles all the binder linking/death stuff)
mRemoteHandler = new OneShotRemoteHandler(
mPendingRemoteHandler = new OneShotRemoteHandler(
mTransitions.getMainExecutor(), remoteTransition);
mRemoteHandler.setTransition(transition);
mPendingRemoteHandler.setTransition(transition);
}
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition "
@@ -221,6 +222,13 @@ class SplitScreenTransitions {
return transition;
}
void mergeAnimation(IBinder transition, TransitionInfo info, SurfaceControl.Transaction t,
IBinder mergeTarget, Transitions.TransitionFinishCallback finishCallback) {
if (mergeTarget == mAnimatingTransition && mActiveRemoteHandler != null) {
mActiveRemoteHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback);
}
}
void onFinish(WindowContainerTransaction wct, WindowContainerTransactionCallback wctCB) {
if (!mAnimations.isEmpty()) return;
mOnFinish.run();
@@ -241,11 +249,13 @@ class SplitScreenTransitions {
}
if (mAnimatingTransition == mPendingRecent) {
// If the wct is not null while finishing recent transition, it indicates it's not
// returning to home and hence needing the wct to reorder tasks.
final boolean toHome = wct == null;
mStageCoordinator.finishRecentAnimation(toHome);
// dismissing split and thus need to reorder split task so they can be on top again.
final boolean dismissSplit = wct == null;
mStageCoordinator.finishRecentAnimation(dismissSplit);
mPendingRecent = null;
}
mPendingRemoteHandler = null;
mActiveRemoteHandler = null;
mAnimatingTransition = null;
}

View File

@@ -166,17 +166,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@StageType
private int mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
private final Runnable mOnTransitionAnimationComplete = () -> {
// If still playing, let it finish.
if (!isSplitScreenVisible()) {
// Update divider state after animation so that it is still around and positioned
// properly for the animation itself.
mSplitLayout.release();
mSplitLayout.resetDividerPosition();
mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
}
};
private final SplitWindowManager.ParentContainerCallbacks mParentContainerCallbacks =
new SplitWindowManager.ParentContainerCallbacks() {
@Override
@@ -237,7 +226,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
deviceStateManager.registerCallback(taskOrganizer.getExecutor(),
new DeviceStateManager.FoldStateListener(mContext, this::onFoldedStateChanged));
mSplitTransitions = new SplitScreenTransitions(transactionPool, transitions,
mOnTransitionAnimationComplete, this);
this::onTransitionAnimationComplete, this);
mDisplayController.addDisplayWindowListener(this);
mDisplayLayout = new DisplayLayout(displayController.getDisplayLayout(displayId));
transitions.addHandler(this);
@@ -267,7 +256,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mRootTDAOrganizer.registerListener(displayId, this);
mSplitLayout = splitLayout;
mSplitTransitions = new SplitScreenTransitions(transactionPool, transitions,
mOnTransitionAnimationComplete, this);
this::onTransitionAnimationComplete, this);
mMainUnfoldController = unfoldControllerProvider.get().orElse(null);
mSideUnfoldController = unfoldControllerProvider.get().orElse(null);
mLogger = logger;
@@ -1234,7 +1223,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask();
if (triggerTask == null) {
// Still want to monitor everything while in split-screen, so return non-null.
return isSplitScreenVisible() ? new WindowContainerTransaction() : null;
return mMainStage.isActive() ? new WindowContainerTransaction() : null;
} else if (triggerTask.displayId != mDisplayId) {
// Skip handling task on the other display.
return null;
@@ -1250,7 +1239,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mRecentTasks.ifPresent(recentTasks -> recentTasks.removeSplitPair(triggerTask.taskId));
}
if (isSplitScreenVisible()) {
if (mMainStage.isActive()) {
// 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"
@@ -1295,6 +1284,13 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
return out;
}
@Override
public void mergeAnimation(IBinder transition, TransitionInfo info,
SurfaceControl.Transaction t, IBinder mergeTarget,
Transitions.TransitionFinishCallback finishCallback) {
mSplitTransitions.mergeAnimation(transition, info, t, mergeTarget, finishCallback);
}
@Override
public void onTransitionMerged(@NonNull IBinder transition) {
// Once the pending enter transition got merged, make sure to bring divider bar visible and
@@ -1375,6 +1371,17 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
return true;
}
void onTransitionAnimationComplete() {
// If still playing, let it finish.
if (!mMainStage.isActive()) {
// Update divider state after animation so that it is still around and positioned
// properly for the animation itself.
mSplitLayout.release();
mSplitLayout.resetDividerPosition();
mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
}
}
private boolean startPendingEnterAnimation(@NonNull IBinder transition,
@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t) {
// First, verify that we actually have opened apps in both splits.
@@ -1513,8 +1520,17 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
return true;
}
void finishRecentAnimation(boolean toHome) {
if (toHome) {
void finishRecentAnimation(boolean dismissSplit) {
// Exclude the case that the split screen has been dismissed already.
if (!mMainStage.isActive()) {
// The latest split dismissing transition might be a no-op transition and thus won't
// callback startAnimation, update split visibility here to cover this kind of no-op
// transition case.
setSplitsVisible(false);
return;
}
if (dismissSplit) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct);
mSplitTransitions.startDismissTransition(null /* transition */, wct, this,