Loading libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java +102 −55 Original line number Diff line number Diff line Loading @@ -108,6 +108,14 @@ class SplitScreenTransitions { private void playInternalAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t, @NonNull WindowContainerToken mainRoot, @NonNull WindowContainerToken sideRoot, @NonNull WindowContainerToken topRoot) { final TransitSession pendingTransition = getPendingTransition(transition); if (pendingTransition != null && pendingTransition.mCanceled) { // The pending transition was canceled, so skip playing animation. t.apply(); onFinish(null /* wct */, null /* wctCB */); return; } // Play some place-holder fade animations for (int i = info.getChanges().size() - 1; i >= 0; --i) { final TransitionInfo.Change change = info.getChanges().get(i); Loading Loading @@ -170,9 +178,7 @@ class SplitScreenTransitions { } boolean isPendingTransition(IBinder transition) { return isPendingEnter(transition) || isPendingDismiss(transition) || isPendingRecent(transition); return getPendingTransition(transition) != null; } boolean isPendingEnter(IBinder transition) { Loading @@ -187,22 +193,38 @@ class SplitScreenTransitions { return mPendingDismiss != null && mPendingDismiss.mTransition == transition; } @Nullable private TransitSession getPendingTransition(IBinder transition) { if (isPendingEnter(transition)) { return mPendingEnter; } else if (isPendingRecent(transition)) { return mPendingRecent; } else if (isPendingDismiss(transition)) { return mPendingDismiss; } return null; } /** Starts a transition to enter split with a remote transition animator. */ IBinder startEnterTransition( @WindowManager.TransitionType int transitType, WindowContainerTransaction wct, @Nullable RemoteTransition remoteTransition, Transitions.TransitionHandler handler, @Nullable TransitionCallback callback) { @Nullable TransitionConsumedCallback consumedCallback, @Nullable TransitionFinishedCallback finishedCallback) { final IBinder transition = mTransitions.startTransition(transitType, wct, handler); setEnterTransition(transition, remoteTransition, callback); setEnterTransition(transition, remoteTransition, consumedCallback, finishedCallback); return transition; } /** Sets a transition to enter split. */ void setEnterTransition(@NonNull IBinder transition, @Nullable RemoteTransition remoteTransition, @Nullable TransitionCallback callback) { mPendingEnter = new TransitSession(transition, callback); @Nullable RemoteTransition remoteTransition, @Nullable TransitionConsumedCallback consumedCallback, @Nullable TransitionFinishedCallback finishedCallback) { mPendingEnter = new TransitSession(transition, consumedCallback, finishedCallback); if (remoteTransition != null) { // Wrapping it for ease-of-use (OneShot handles all the binder linking/death stuff) Loading Loading @@ -237,8 +259,9 @@ class SplitScreenTransitions { } void setRecentTransition(@NonNull IBinder transition, @Nullable RemoteTransition remoteTransition, @Nullable TransitionCallback callback) { mPendingRecent = new TransitSession(transition, callback); @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) Loading @@ -256,14 +279,9 @@ class SplitScreenTransitions { if (mergeTarget != mAnimatingTransition) return; if (isPendingEnter(transition) && isPendingRecent(mergeTarget)) { mPendingRecent.mCallback = new TransitionCallback() { @Override public void onTransitionFinished(WindowContainerTransaction finishWct, SurfaceControl.Transaction finishT) { // 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) { Loading @@ -277,7 +295,7 @@ class SplitScreenTransitions { } boolean end() { // If its remote, there's nothing we can do right now. // If It's remote, there's nothing we can do right now. if (mActiveRemoteHandler != null) return false; for (int i = mAnimations.size() - 1; i >= 0; --i) { final Animator anim = mAnimations.get(i); Loading @@ -290,20 +308,20 @@ class SplitScreenTransitions { @Nullable SurfaceControl.Transaction finishT) { if (isPendingEnter(transition)) { if (!aborted) { // An enter transition got merged, appends the rest operations to finish entering // An entering transition got merged, appends the rest operations to finish entering // split screen. mStageCoordinator.finishEnterSplitScreen(finishT); mPendingRemoteHandler = null; } mPendingEnter.mCallback.onTransitionConsumed(aborted); mPendingEnter.onConsumed(aborted); mPendingEnter = null; mPendingRemoteHandler = null; } else if (isPendingDismiss(transition)) { mPendingDismiss.mCallback.onTransitionConsumed(aborted); mPendingDismiss.onConsumed(aborted); mPendingDismiss = null; } else if (isPendingRecent(transition)) { mPendingRecent.mCallback.onTransitionConsumed(aborted); mPendingRecent.onConsumed(aborted); mPendingRecent = null; mPendingRemoteHandler = null; } Loading @@ -312,23 +330,16 @@ class SplitScreenTransitions { void onFinish(WindowContainerTransaction wct, WindowContainerTransactionCallback wctCB) { if (!mAnimations.isEmpty()) return; TransitionCallback callback = null; if (wct == null) wct = new WindowContainerTransaction(); if (isPendingEnter(mAnimatingTransition)) { callback = mPendingEnter.mCallback; mPendingEnter.onFinished(wct, mFinishTransaction); mPendingEnter = null; } if (isPendingDismiss(mAnimatingTransition)) { callback = mPendingDismiss.mCallback; mPendingDismiss = null; } if (isPendingRecent(mAnimatingTransition)) { callback = mPendingRecent.mCallback; } else if (isPendingRecent(mAnimatingTransition)) { mPendingRecent.onFinished(wct, mFinishTransaction); mPendingRecent = null; } if (callback != null) { if (wct == null) wct = new WindowContainerTransaction(); callback.onTransitionFinished(wct, mFinishTransaction); } else if (isPendingDismiss(mAnimatingTransition)) { mPendingDismiss.onFinished(wct, mFinishTransaction); mPendingDismiss = null; } mPendingRemoteHandler = null; Loading Loading @@ -363,10 +374,7 @@ class SplitScreenTransitions { onFinish(null /* wct */, null /* wctCB */); }); }; va.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } va.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { finisher.run(); Loading @@ -376,9 +384,6 @@ class SplitScreenTransitions { public void onAnimationCancel(Animator animation) { finisher.run(); } @Override public void onAnimationRepeat(Animator animation) { } }); mAnimations.add(va); mTransitions.getAnimExecutor().execute(va::start); Loading Loading @@ -432,24 +437,66 @@ class SplitScreenTransitions { || info.getType() == TRANSIT_SPLIT_SCREEN_PAIR_OPEN; } /** Clean-up callbacks for transition. */ interface TransitionCallback { /** Calls when the transition got consumed. */ default void onTransitionConsumed(boolean aborted) {} interface TransitionConsumedCallback { void onConsumed(boolean aborted); } /** Calls when the transition finished. */ default void onTransitionFinished(WindowContainerTransaction finishWct, SurfaceControl.Transaction finishT) {} interface TransitionFinishedCallback { void onFinished(WindowContainerTransaction wct, SurfaceControl.Transaction t); } /** Session for a transition and its clean-up callback. */ static class TransitSession { final IBinder mTransition; TransitionCallback mCallback; TransitionConsumedCallback mConsumedCallback; TransitionFinishedCallback mFinishedCallback; /** Whether the transition was canceled. */ boolean mCanceled; TransitSession(IBinder transition, @Nullable TransitionCallback callback) { TransitSession(IBinder transition, @Nullable TransitionConsumedCallback consumedCallback, @Nullable TransitionFinishedCallback finishedCallback) { mTransition = transition; mCallback = callback != null ? callback : new TransitionCallback() {}; mConsumedCallback = consumedCallback; mFinishedCallback = finishedCallback; } /** Sets transition consumed callback. */ void setConsumedCallback(@Nullable TransitionConsumedCallback callback) { mConsumedCallback = callback; } /** Sets transition finished callback. */ void setFinishedCallback(@Nullable TransitionFinishedCallback callback) { mFinishedCallback = callback; } /** * Cancels the transition. This should be called before playing animation. A canceled * transition will skip playing animation. * * @param finishedCb new finish callback to override. */ void cancel(@Nullable TransitionFinishedCallback finishedCb) { mCanceled = true; setFinishedCallback(finishedCb); } void onConsumed(boolean aborted) { if (mConsumedCallback != null) { mConsumedCallback.onConsumed(aborted); } } void onFinished(WindowContainerTransaction finishWct, SurfaceControl.Transaction finishT) { if (mFinishedCallback != null) { mFinishedCallback.onFinished(finishWct, finishT); } } } Loading @@ -459,7 +506,7 @@ class SplitScreenTransitions { final @SplitScreen.StageType int mDismissTop; DismissTransition(IBinder transition, int reason, int dismissTop) { super(transition, null /* callback */); super(transition, null /* consumedCallback */, null /* finishedCallback */); this.mReason = reason; this.mDismissTop = dismissTop; } Loading libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +71 −71 Original line number Diff line number Diff line Loading @@ -226,12 +226,14 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } }; private final SplitScreenTransitions.TransitionCallback mRecentTransitionCallback = new SplitScreenTransitions.TransitionCallback() { private final SplitScreenTransitions.TransitionFinishedCallback mRecentTransitionFinishedCallback = new SplitScreenTransitions.TransitionFinishedCallback() { @Override public void onTransitionFinished(WindowContainerTransaction finishWct, public void onFinished(WindowContainerTransaction finishWct, SurfaceControl.Transaction finishT) { // Check if the recent transition is finished by returning to the current split, so we // 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 = Loading @@ -240,7 +242,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (op.getType() == HIERARCHY_OP_TYPE_REORDER && op.getToTop() && (mMainStage.containsContainer(container) || mSideStage.containsContainer(container))) { updateSurfaceBounds(mSplitLayout, finishT, false /* applyResizingOffset */); updateSurfaceBounds(mSplitLayout, finishT, false /* applyResizingOffset */); setDividerVisibility(true, finishT); return; } Loading Loading @@ -389,15 +392,11 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (ENABLE_SHELL_TRANSITIONS) { prepareEnterSplitScreen(wct); mSplitTransitions.startEnterTransition(TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, wct, null, this, new SplitScreenTransitions.TransitionCallback() { @Override public void onTransitionFinished(WindowContainerTransaction finishWct, SurfaceControl.Transaction finishT) { null, this, null /* consumedCallback */, (finishWct, finishT) -> { if (!evictWct.isEmpty()) { finishWct.merge(evictWct, true); } } }); } /* finishedCallback */); } else { if (!evictWct.isEmpty()) { wct.merge(evictWct, true /* transfer */); Loading Loading @@ -434,28 +433,25 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, options = resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, null /* wct */); wct.sendPendingIntent(intent, fillInIntent, options); // If split screen is not activated, we're expecting to open a pair of apps to split. final int transitType = mMainStage.isActive() ? TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE : TRANSIT_SPLIT_SCREEN_PAIR_OPEN; prepareEnterSplitScreen(wct, null /* taskInfo */, position); mSplitTransitions.startEnterTransition(TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, wct, null, this, new SplitScreenTransitions.TransitionCallback() { @Override public void onTransitionConsumed(boolean aborted) { mSplitTransitions.startEnterTransition(transitType, wct, null, this, aborted -> { // Switch the split position if launching as MULTIPLE_TASK failed. if (aborted && (fillInIntent.getFlags() & FLAG_ACTIVITY_MULTIPLE_TASK) != 0) { if (aborted && (fillInIntent.getFlags() & FLAG_ACTIVITY_MULTIPLE_TASK) != 0) { setSideStagePositionAnimated( SplitLayout.reversePosition(mSideStagePosition)); } } @Override public void onTransitionFinished(WindowContainerTransaction finishWct, SurfaceControl.Transaction finishT) { } /* consumedCallback */, (finishWct, finishT) -> { if (!evictWct.isEmpty()) { finishWct.merge(evictWct, true); } } }); } /* finishedCallback */); } /** Launches an activity into split by legacy transition. */ Loading Loading @@ -592,7 +588,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, wct.startTask(mainTaskId, mainOptions); mSplitTransitions.startEnterTransition( TRANSIT_SPLIT_SCREEN_PAIR_OPEN, wct, remoteTransition, this, null); TRANSIT_SPLIT_SCREEN_PAIR_OPEN, wct, remoteTransition, this, null, null); setEnterInstanceId(instanceId); } Loading Loading @@ -1839,7 +1835,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, || activityType == ACTIVITY_TYPE_RECENTS) { // Enter overview panel, so start recent transition. mSplitTransitions.setRecentTransition(transition, request.getRemoteTransition(), mRecentTransitionCallback); mRecentTransitionFinishedCallback); } else if (mSplitTransitions.mPendingRecent == null) { // If split-task is not controlled by recents animation // and occluded by the other fullscreen task, dismiss both. Loading @@ -1853,8 +1849,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // One task is appearing into split, prepare to enter split screen. out = new WindowContainerTransaction(); prepareEnterSplitScreen(out); mSplitTransitions.setEnterTransition( transition, request.getRemoteTransition(), null /* callback */); mSplitTransitions.setEnterTransition(transition, request.getRemoteTransition(), null /* consumedCallback */, null /* finishedCallback */); } } return out; Loading Loading @@ -2031,17 +2027,21 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } } // TODO(b/250853925): 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."); Log.w(TAG, "Launched a task in split, but didn't receive any task in transition."); mSplitTransitions.mPendingEnter.cancel(null /* finishedCb */); return true; } } else { if (mainChild == null || sideChild == null) { throw new IllegalStateException("Launched 2 tasks in split, but didn't receive" Log.w(TAG, "Launched 2 tasks in split, but didn't receive" + " 2 tasks in transition. Possibly one of them failed to launch"); final int dismissTop = mainChild != null ? STAGE_TYPE_MAIN : (sideChild != null ? STAGE_TYPE_SIDE : STAGE_TYPE_UNDEFINED); mSplitTransitions.mPendingEnter.cancel( (cancelWct, cancelT) -> prepareExitSplitScreen(dismissTop, cancelWct)); return true; } } Loading libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java +2 −2 Original line number Diff line number Diff line Loading @@ -181,7 +181,7 @@ public class SplitTransitionTests extends ShellTestCase { IBinder transition = mSplitScreenTransitions.startEnterTransition( TRANSIT_SPLIT_SCREEN_PAIR_OPEN, new WindowContainerTransaction(), new RemoteTransition(testRemote), mStageCoordinator, null); new RemoteTransition(testRemote), mStageCoordinator, null, null); mMainStage.onTaskAppeared(mMainChild, createMockSurface()); mSideStage.onTaskAppeared(mSideChild, createMockSurface()); boolean accepted = mStageCoordinator.startAnimation(transition, info, Loading Loading @@ -421,7 +421,7 @@ public class SplitTransitionTests extends ShellTestCase { TransitionInfo enterInfo = createEnterPairInfo(); IBinder enterTransit = mSplitScreenTransitions.startEnterTransition( TRANSIT_SPLIT_SCREEN_PAIR_OPEN, new WindowContainerTransaction(), new RemoteTransition(new TestRemoteTransition()), mStageCoordinator, null); new RemoteTransition(new TestRemoteTransition()), mStageCoordinator, null, null); mMainStage.onTaskAppeared(mMainChild, createMockSurface()); mSideStage.onTaskAppeared(mSideChild, createMockSurface()); mStageCoordinator.startAnimation(enterTransit, enterInfo, Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java +102 −55 Original line number Diff line number Diff line Loading @@ -108,6 +108,14 @@ class SplitScreenTransitions { private void playInternalAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t, @NonNull WindowContainerToken mainRoot, @NonNull WindowContainerToken sideRoot, @NonNull WindowContainerToken topRoot) { final TransitSession pendingTransition = getPendingTransition(transition); if (pendingTransition != null && pendingTransition.mCanceled) { // The pending transition was canceled, so skip playing animation. t.apply(); onFinish(null /* wct */, null /* wctCB */); return; } // Play some place-holder fade animations for (int i = info.getChanges().size() - 1; i >= 0; --i) { final TransitionInfo.Change change = info.getChanges().get(i); Loading Loading @@ -170,9 +178,7 @@ class SplitScreenTransitions { } boolean isPendingTransition(IBinder transition) { return isPendingEnter(transition) || isPendingDismiss(transition) || isPendingRecent(transition); return getPendingTransition(transition) != null; } boolean isPendingEnter(IBinder transition) { Loading @@ -187,22 +193,38 @@ class SplitScreenTransitions { return mPendingDismiss != null && mPendingDismiss.mTransition == transition; } @Nullable private TransitSession getPendingTransition(IBinder transition) { if (isPendingEnter(transition)) { return mPendingEnter; } else if (isPendingRecent(transition)) { return mPendingRecent; } else if (isPendingDismiss(transition)) { return mPendingDismiss; } return null; } /** Starts a transition to enter split with a remote transition animator. */ IBinder startEnterTransition( @WindowManager.TransitionType int transitType, WindowContainerTransaction wct, @Nullable RemoteTransition remoteTransition, Transitions.TransitionHandler handler, @Nullable TransitionCallback callback) { @Nullable TransitionConsumedCallback consumedCallback, @Nullable TransitionFinishedCallback finishedCallback) { final IBinder transition = mTransitions.startTransition(transitType, wct, handler); setEnterTransition(transition, remoteTransition, callback); setEnterTransition(transition, remoteTransition, consumedCallback, finishedCallback); return transition; } /** Sets a transition to enter split. */ void setEnterTransition(@NonNull IBinder transition, @Nullable RemoteTransition remoteTransition, @Nullable TransitionCallback callback) { mPendingEnter = new TransitSession(transition, callback); @Nullable RemoteTransition remoteTransition, @Nullable TransitionConsumedCallback consumedCallback, @Nullable TransitionFinishedCallback finishedCallback) { mPendingEnter = new TransitSession(transition, consumedCallback, finishedCallback); if (remoteTransition != null) { // Wrapping it for ease-of-use (OneShot handles all the binder linking/death stuff) Loading Loading @@ -237,8 +259,9 @@ class SplitScreenTransitions { } void setRecentTransition(@NonNull IBinder transition, @Nullable RemoteTransition remoteTransition, @Nullable TransitionCallback callback) { mPendingRecent = new TransitSession(transition, callback); @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) Loading @@ -256,14 +279,9 @@ class SplitScreenTransitions { if (mergeTarget != mAnimatingTransition) return; if (isPendingEnter(transition) && isPendingRecent(mergeTarget)) { mPendingRecent.mCallback = new TransitionCallback() { @Override public void onTransitionFinished(WindowContainerTransaction finishWct, SurfaceControl.Transaction finishT) { // 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) { Loading @@ -277,7 +295,7 @@ class SplitScreenTransitions { } boolean end() { // If its remote, there's nothing we can do right now. // If It's remote, there's nothing we can do right now. if (mActiveRemoteHandler != null) return false; for (int i = mAnimations.size() - 1; i >= 0; --i) { final Animator anim = mAnimations.get(i); Loading @@ -290,20 +308,20 @@ class SplitScreenTransitions { @Nullable SurfaceControl.Transaction finishT) { if (isPendingEnter(transition)) { if (!aborted) { // An enter transition got merged, appends the rest operations to finish entering // An entering transition got merged, appends the rest operations to finish entering // split screen. mStageCoordinator.finishEnterSplitScreen(finishT); mPendingRemoteHandler = null; } mPendingEnter.mCallback.onTransitionConsumed(aborted); mPendingEnter.onConsumed(aborted); mPendingEnter = null; mPendingRemoteHandler = null; } else if (isPendingDismiss(transition)) { mPendingDismiss.mCallback.onTransitionConsumed(aborted); mPendingDismiss.onConsumed(aborted); mPendingDismiss = null; } else if (isPendingRecent(transition)) { mPendingRecent.mCallback.onTransitionConsumed(aborted); mPendingRecent.onConsumed(aborted); mPendingRecent = null; mPendingRemoteHandler = null; } Loading @@ -312,23 +330,16 @@ class SplitScreenTransitions { void onFinish(WindowContainerTransaction wct, WindowContainerTransactionCallback wctCB) { if (!mAnimations.isEmpty()) return; TransitionCallback callback = null; if (wct == null) wct = new WindowContainerTransaction(); if (isPendingEnter(mAnimatingTransition)) { callback = mPendingEnter.mCallback; mPendingEnter.onFinished(wct, mFinishTransaction); mPendingEnter = null; } if (isPendingDismiss(mAnimatingTransition)) { callback = mPendingDismiss.mCallback; mPendingDismiss = null; } if (isPendingRecent(mAnimatingTransition)) { callback = mPendingRecent.mCallback; } else if (isPendingRecent(mAnimatingTransition)) { mPendingRecent.onFinished(wct, mFinishTransaction); mPendingRecent = null; } if (callback != null) { if (wct == null) wct = new WindowContainerTransaction(); callback.onTransitionFinished(wct, mFinishTransaction); } else if (isPendingDismiss(mAnimatingTransition)) { mPendingDismiss.onFinished(wct, mFinishTransaction); mPendingDismiss = null; } mPendingRemoteHandler = null; Loading Loading @@ -363,10 +374,7 @@ class SplitScreenTransitions { onFinish(null /* wct */, null /* wctCB */); }); }; va.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } va.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { finisher.run(); Loading @@ -376,9 +384,6 @@ class SplitScreenTransitions { public void onAnimationCancel(Animator animation) { finisher.run(); } @Override public void onAnimationRepeat(Animator animation) { } }); mAnimations.add(va); mTransitions.getAnimExecutor().execute(va::start); Loading Loading @@ -432,24 +437,66 @@ class SplitScreenTransitions { || info.getType() == TRANSIT_SPLIT_SCREEN_PAIR_OPEN; } /** Clean-up callbacks for transition. */ interface TransitionCallback { /** Calls when the transition got consumed. */ default void onTransitionConsumed(boolean aborted) {} interface TransitionConsumedCallback { void onConsumed(boolean aborted); } /** Calls when the transition finished. */ default void onTransitionFinished(WindowContainerTransaction finishWct, SurfaceControl.Transaction finishT) {} interface TransitionFinishedCallback { void onFinished(WindowContainerTransaction wct, SurfaceControl.Transaction t); } /** Session for a transition and its clean-up callback. */ static class TransitSession { final IBinder mTransition; TransitionCallback mCallback; TransitionConsumedCallback mConsumedCallback; TransitionFinishedCallback mFinishedCallback; /** Whether the transition was canceled. */ boolean mCanceled; TransitSession(IBinder transition, @Nullable TransitionCallback callback) { TransitSession(IBinder transition, @Nullable TransitionConsumedCallback consumedCallback, @Nullable TransitionFinishedCallback finishedCallback) { mTransition = transition; mCallback = callback != null ? callback : new TransitionCallback() {}; mConsumedCallback = consumedCallback; mFinishedCallback = finishedCallback; } /** Sets transition consumed callback. */ void setConsumedCallback(@Nullable TransitionConsumedCallback callback) { mConsumedCallback = callback; } /** Sets transition finished callback. */ void setFinishedCallback(@Nullable TransitionFinishedCallback callback) { mFinishedCallback = callback; } /** * Cancels the transition. This should be called before playing animation. A canceled * transition will skip playing animation. * * @param finishedCb new finish callback to override. */ void cancel(@Nullable TransitionFinishedCallback finishedCb) { mCanceled = true; setFinishedCallback(finishedCb); } void onConsumed(boolean aborted) { if (mConsumedCallback != null) { mConsumedCallback.onConsumed(aborted); } } void onFinished(WindowContainerTransaction finishWct, SurfaceControl.Transaction finishT) { if (mFinishedCallback != null) { mFinishedCallback.onFinished(finishWct, finishT); } } } Loading @@ -459,7 +506,7 @@ class SplitScreenTransitions { final @SplitScreen.StageType int mDismissTop; DismissTransition(IBinder transition, int reason, int dismissTop) { super(transition, null /* callback */); super(transition, null /* consumedCallback */, null /* finishedCallback */); this.mReason = reason; this.mDismissTop = dismissTop; } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +71 −71 Original line number Diff line number Diff line Loading @@ -226,12 +226,14 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } }; private final SplitScreenTransitions.TransitionCallback mRecentTransitionCallback = new SplitScreenTransitions.TransitionCallback() { private final SplitScreenTransitions.TransitionFinishedCallback mRecentTransitionFinishedCallback = new SplitScreenTransitions.TransitionFinishedCallback() { @Override public void onTransitionFinished(WindowContainerTransaction finishWct, public void onFinished(WindowContainerTransaction finishWct, SurfaceControl.Transaction finishT) { // Check if the recent transition is finished by returning to the current split, so we // 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 = Loading @@ -240,7 +242,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (op.getType() == HIERARCHY_OP_TYPE_REORDER && op.getToTop() && (mMainStage.containsContainer(container) || mSideStage.containsContainer(container))) { updateSurfaceBounds(mSplitLayout, finishT, false /* applyResizingOffset */); updateSurfaceBounds(mSplitLayout, finishT, false /* applyResizingOffset */); setDividerVisibility(true, finishT); return; } Loading Loading @@ -389,15 +392,11 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (ENABLE_SHELL_TRANSITIONS) { prepareEnterSplitScreen(wct); mSplitTransitions.startEnterTransition(TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, wct, null, this, new SplitScreenTransitions.TransitionCallback() { @Override public void onTransitionFinished(WindowContainerTransaction finishWct, SurfaceControl.Transaction finishT) { null, this, null /* consumedCallback */, (finishWct, finishT) -> { if (!evictWct.isEmpty()) { finishWct.merge(evictWct, true); } } }); } /* finishedCallback */); } else { if (!evictWct.isEmpty()) { wct.merge(evictWct, true /* transfer */); Loading Loading @@ -434,28 +433,25 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, options = resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, null /* wct */); wct.sendPendingIntent(intent, fillInIntent, options); // If split screen is not activated, we're expecting to open a pair of apps to split. final int transitType = mMainStage.isActive() ? TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE : TRANSIT_SPLIT_SCREEN_PAIR_OPEN; prepareEnterSplitScreen(wct, null /* taskInfo */, position); mSplitTransitions.startEnterTransition(TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, wct, null, this, new SplitScreenTransitions.TransitionCallback() { @Override public void onTransitionConsumed(boolean aborted) { mSplitTransitions.startEnterTransition(transitType, wct, null, this, aborted -> { // Switch the split position if launching as MULTIPLE_TASK failed. if (aborted && (fillInIntent.getFlags() & FLAG_ACTIVITY_MULTIPLE_TASK) != 0) { if (aborted && (fillInIntent.getFlags() & FLAG_ACTIVITY_MULTIPLE_TASK) != 0) { setSideStagePositionAnimated( SplitLayout.reversePosition(mSideStagePosition)); } } @Override public void onTransitionFinished(WindowContainerTransaction finishWct, SurfaceControl.Transaction finishT) { } /* consumedCallback */, (finishWct, finishT) -> { if (!evictWct.isEmpty()) { finishWct.merge(evictWct, true); } } }); } /* finishedCallback */); } /** Launches an activity into split by legacy transition. */ Loading Loading @@ -592,7 +588,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, wct.startTask(mainTaskId, mainOptions); mSplitTransitions.startEnterTransition( TRANSIT_SPLIT_SCREEN_PAIR_OPEN, wct, remoteTransition, this, null); TRANSIT_SPLIT_SCREEN_PAIR_OPEN, wct, remoteTransition, this, null, null); setEnterInstanceId(instanceId); } Loading Loading @@ -1839,7 +1835,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, || activityType == ACTIVITY_TYPE_RECENTS) { // Enter overview panel, so start recent transition. mSplitTransitions.setRecentTransition(transition, request.getRemoteTransition(), mRecentTransitionCallback); mRecentTransitionFinishedCallback); } else if (mSplitTransitions.mPendingRecent == null) { // If split-task is not controlled by recents animation // and occluded by the other fullscreen task, dismiss both. Loading @@ -1853,8 +1849,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // One task is appearing into split, prepare to enter split screen. out = new WindowContainerTransaction(); prepareEnterSplitScreen(out); mSplitTransitions.setEnterTransition( transition, request.getRemoteTransition(), null /* callback */); mSplitTransitions.setEnterTransition(transition, request.getRemoteTransition(), null /* consumedCallback */, null /* finishedCallback */); } } return out; Loading Loading @@ -2031,17 +2027,21 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } } // TODO(b/250853925): 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."); Log.w(TAG, "Launched a task in split, but didn't receive any task in transition."); mSplitTransitions.mPendingEnter.cancel(null /* finishedCb */); return true; } } else { if (mainChild == null || sideChild == null) { throw new IllegalStateException("Launched 2 tasks in split, but didn't receive" Log.w(TAG, "Launched 2 tasks in split, but didn't receive" + " 2 tasks in transition. Possibly one of them failed to launch"); final int dismissTop = mainChild != null ? STAGE_TYPE_MAIN : (sideChild != null ? STAGE_TYPE_SIDE : STAGE_TYPE_UNDEFINED); mSplitTransitions.mPendingEnter.cancel( (cancelWct, cancelT) -> prepareExitSplitScreen(dismissTop, cancelWct)); return true; } } Loading
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java +2 −2 Original line number Diff line number Diff line Loading @@ -181,7 +181,7 @@ public class SplitTransitionTests extends ShellTestCase { IBinder transition = mSplitScreenTransitions.startEnterTransition( TRANSIT_SPLIT_SCREEN_PAIR_OPEN, new WindowContainerTransaction(), new RemoteTransition(testRemote), mStageCoordinator, null); new RemoteTransition(testRemote), mStageCoordinator, null, null); mMainStage.onTaskAppeared(mMainChild, createMockSurface()); mSideStage.onTaskAppeared(mSideChild, createMockSurface()); boolean accepted = mStageCoordinator.startAnimation(transition, info, Loading Loading @@ -421,7 +421,7 @@ public class SplitTransitionTests extends ShellTestCase { TransitionInfo enterInfo = createEnterPairInfo(); IBinder enterTransit = mSplitScreenTransitions.startEnterTransition( TRANSIT_SPLIT_SCREEN_PAIR_OPEN, new WindowContainerTransaction(), new RemoteTransition(new TestRemoteTransition()), mStageCoordinator, null); new RemoteTransition(new TestRemoteTransition()), mStageCoordinator, null, null); mMainStage.onTaskAppeared(mMainChild, createMockSurface()); mSideStage.onTaskAppeared(mSideChild, createMockSurface()); mStageCoordinator.startAnimation(enterTransit, enterInfo, Loading