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:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user