Loading libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java +21 −1 Original line number Diff line number Diff line Loading @@ -64,7 +64,9 @@ public interface SplitScreen { default void onSplitVisibilityChanged(boolean visible) {} } /** Callback interface for listening to requests to enter split select */ /** * Callback interface for listening to requests to enter split select. Used for desktop -> split */ interface SplitSelectListener { default boolean onRequestEnterSplitSelect(ActivityManager.RunningTaskInfo taskInfo, int splitPosition, Rect taskBounds) { Loading @@ -72,6 +74,15 @@ public interface SplitScreen { } } interface SplitInvocationListener { /** * Called whenever shell starts or stops the split screen animation * @param animationRunning if {@code true} the animation has begun, if {@code false} the * animation has finished */ default void onSplitAnimationInvoked(boolean animationRunning) { } } /** Registers listener that gets split screen callback. */ void registerSplitScreenListener(@NonNull SplitScreenListener listener, @NonNull Executor executor); Loading @@ -79,6 +90,15 @@ public interface SplitScreen { /** Unregisters listener that gets split screen callback. */ void unregisterSplitScreenListener(@NonNull SplitScreenListener listener); /** * Registers a {@link SplitInvocationListener} to notify when the animation to enter split * screen has started and stopped * * @param executor callbacks to the listener will be executed on this executor */ void registerSplitAnimationListener(@NonNull SplitInvocationListener listener, @NonNull Executor executor); /** Called when device waking up finished. */ void onFinishedWakingUp(); Loading libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +6 −0 Original line number Diff line number Diff line Loading @@ -1137,6 +1137,12 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, }); } @Override public void registerSplitAnimationListener(@NonNull SplitInvocationListener listener, @NonNull Executor executor) { mStageCoordinator.registerSplitAnimationListener(listener, executor); } @Override public void onFinishedWakingUp() { mMainExecutor.execute(SplitScreenController.this::onFinishedWakingUp); Loading libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java +13 −0 Original line number Diff line number Diff line Loading @@ -55,6 +55,7 @@ import com.android.wm.shell.transition.OneShotRemoteHandler; import com.android.wm.shell.transition.Transitions; import java.util.ArrayList; import java.util.concurrent.Executor; /** Manages transition animations for split-screen. */ class SplitScreenTransitions { Loading @@ -79,6 +80,8 @@ class SplitScreenTransitions { private Transitions.TransitionFinishCallback mFinishCallback = null; private SurfaceControl.Transaction mFinishTransaction; private SplitScreen.SplitInvocationListener mSplitInvocationListener; private Executor mSplitInvocationListenerExecutor; SplitScreenTransitions(@NonNull TransactionPool pool, @NonNull Transitions transitions, @NonNull Runnable onFinishCallback, StageCoordinator stageCoordinator) { Loading Loading @@ -353,6 +356,10 @@ class SplitScreenTransitions { + " skip to start enter split transition since it already exist. "); return null; } if (mSplitInvocationListenerExecutor != null && mSplitInvocationListener != null) { mSplitInvocationListenerExecutor.execute(() -> mSplitInvocationListener .onSplitAnimationInvoked(true /*animationRunning*/)); } final IBinder transition = mTransitions.startTransition(transitType, wct, handler); setEnterTransition(transition, remoteTransition, extraTransitType, resizeAnim); return transition; Loading Loading @@ -529,6 +536,12 @@ class SplitScreenTransitions { mTransitions.getAnimExecutor().execute(va::start); } public void registerSplitAnimListener(@NonNull SplitScreen.SplitInvocationListener listener, @NonNull Executor executor) { mSplitInvocationListener = listener; mSplitInvocationListenerExecutor = executor; } /** Calls when the transition got consumed. */ interface TransitionConsumedCallback { void onConsumed(boolean aborted); Loading libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +36 −7 Original line number Diff line number Diff line Loading @@ -156,6 +156,7 @@ import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.concurrent.Executor; /** * Coordinates the staging (visibility, sizing, ...) of the split-screen {@link MainStage} and Loading Loading @@ -236,6 +237,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private DefaultMixedHandler mMixedHandler; private final Toast mSplitUnsupportedToast; private SplitRequest mSplitRequest; /** Used to notify others of when shell is animating into split screen */ private SplitScreen.SplitInvocationListener mSplitInvocationListener; private Executor mSplitInvocationListenerExecutor; /** * Since StageCoordinator only coordinates MainStage and SideStage, it shouldn't support Loading @@ -246,6 +250,14 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, return false; } /** NOTE: Will overwrite any previously set {@link #mSplitInvocationListener} */ public void registerSplitAnimationListener( @NonNull SplitScreen.SplitInvocationListener listener, @NonNull Executor executor) { mSplitInvocationListener = listener; mSplitInvocationListenerExecutor = executor; mSplitTransitions.registerSplitAnimListener(listener, executor); } class SplitRequest { @SplitPosition int mActivatePosition; Loading Loading @@ -529,7 +541,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, null /* childrenToTop */, EXIT_REASON_UNKNOWN)); Log.w(TAG, splitFailureMessage("startShortcut", "side stage was not populated")); mSplitUnsupportedToast.show(); handleUnsupportedSplitStart(); } if (finishedCallback != null) { Loading Loading @@ -660,7 +672,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, null /* childrenToTop */, EXIT_REASON_UNKNOWN)); Log.w(TAG, splitFailureMessage("startIntentLegacy", "side stage was not populated")); mSplitUnsupportedToast.show(); handleUnsupportedSplitStart(); } if (apps != null) { Loading Loading @@ -1213,7 +1225,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, ? mSideStage : mMainStage, EXIT_REASON_UNKNOWN)); Log.w(TAG, splitFailureMessage("onRemoteAnimationFinishedOrCancelled", "main or side stage was not populated.")); mSplitUnsupportedToast.show(); handleUnsupportedSplitStart(); } else { mSyncQueue.queue(evictWct); mSyncQueue.runInSync(t -> { Loading @@ -1234,7 +1246,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, ? mSideStage : mMainStage, EXIT_REASON_UNKNOWN)); Log.w(TAG, splitFailureMessage("onRemoteAnimationFinished", "main or side stage was not populated")); mSplitUnsupportedToast.show(); handleUnsupportedSplitStart(); return; } Loading Loading @@ -2801,6 +2813,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (hasEnteringPip) { mMixedHandler.animatePendingEnterPipFromSplit(transition, info, startTransaction, finishTransaction, finishCallback); notifySplitAnimationFinished(); return true; } Loading Loading @@ -2835,6 +2848,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // the transition, or synchronize task-org callbacks. } // Use normal animations. notifySplitAnimationFinished(); return false; } else if (mMixedHandler != null && TransitionUtil.hasDisplayChange(info)) { // A display-change has been un-expectedly inserted into the transition. Redirect Loading @@ -2848,6 +2862,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSplitLayout.update(startTransaction, true /* resetImePosition */); startTransaction.apply(); } notifySplitAnimationFinished(); return true; } } Loading Loading @@ -3021,7 +3036,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, pendingEnter.mRemoteHandler.onTransitionConsumed(transition, false /*aborted*/, finishT); } mSplitUnsupportedToast.show(); handleUnsupportedSplitStart(); return true; } } Loading Loading @@ -3050,6 +3065,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, final TransitionInfo.Change finalMainChild = mainChild; final TransitionInfo.Change finalSideChild = sideChild; enterTransition.setFinishedCallback((callbackWct, callbackT) -> { notifySplitAnimationFinished(); if (finalMainChild != null) { if (!mainNotContainOpenTask) { mMainStage.evictOtherChildren(callbackWct, finalMainChild.getTaskInfo().taskId); Loading Loading @@ -3466,6 +3482,19 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSplitLayout.isLeftRightSplit()); } private void handleUnsupportedSplitStart() { mSplitUnsupportedToast.show(); notifySplitAnimationFinished(); } private void notifySplitAnimationFinished() { if (mSplitInvocationListener == null || mSplitInvocationListenerExecutor == null) { return; } mSplitInvocationListenerExecutor.execute(() -> mSplitInvocationListener.onSplitAnimationInvoked(false /*animationRunning*/)); } /** * Logs the exit of splitscreen to a specific stage. This must be called before the exit is * executed. Loading Loading @@ -3528,7 +3557,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (!ENABLE_SHELL_TRANSITIONS) { StageCoordinator.this.exitSplitScreen(isMainStage ? mMainStage : mSideStage, EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW); mSplitUnsupportedToast.show(); handleUnsupportedSplitStart(); return; } Loading @@ -3548,7 +3577,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, "app package " + taskInfo.baseActivity.getPackageName() + " does not support splitscreen, or is a controlled activity type")); if (splitScreenVisible) { mSplitUnsupportedToast.show(); handleUnsupportedSplitStart(); } } } Loading libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java +16 −0 Original line number Diff line number Diff line Loading @@ -39,10 +39,13 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.annotation.NonNull; import android.app.ActivityManager; Loading @@ -63,6 +66,7 @@ import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestRunningTaskInfoBuilder; import com.android.wm.shell.TestShellExecutor; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.DisplayInsetsController; Loading Loading @@ -105,6 +109,8 @@ public class SplitTransitionTests extends ShellTestCase { @Mock private ShellExecutor mMainExecutor; @Mock private LaunchAdjacentController mLaunchAdjacentController; @Mock private DefaultMixedHandler mMixedHandler; @Mock private SplitScreen.SplitInvocationListener mInvocationListener; private final TestShellExecutor mTestShellExecutor = new TestShellExecutor(); private SplitLayout mSplitLayout; private MainStage mMainStage; private SideStage mSideStage; Loading Loading @@ -147,6 +153,7 @@ public class SplitTransitionTests extends ShellTestCase { .setParentTaskId(mSideStage.mRootTaskInfo.taskId).build(); doReturn(mock(SplitDecorManager.class)).when(mMainStage).getSplitDecorManager(); doReturn(mock(SplitDecorManager.class)).when(mSideStage).getSplitDecorManager(); mStageCoordinator.registerSplitAnimationListener(mInvocationListener, mTestShellExecutor); } @Test Loading Loading @@ -452,6 +459,15 @@ public class SplitTransitionTests extends ShellTestCase { mMainStage.activate(new WindowContainerTransaction(), true /* includingTopTask */); } @Test @UiThreadTest public void testSplitInvocationCallback() { enterSplit(); mTestShellExecutor.flushAll(); verify(mInvocationListener, times(1)) .onSplitAnimationInvoked(eq(true)); } private boolean containsSplitEnter(@NonNull WindowContainerTransaction wct) { for (int i = 0; i < wct.getHierarchyOps().size(); ++i) { WindowContainerTransaction.HierarchyOp op = wct.getHierarchyOps().get(i); Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java +21 −1 Original line number Diff line number Diff line Loading @@ -64,7 +64,9 @@ public interface SplitScreen { default void onSplitVisibilityChanged(boolean visible) {} } /** Callback interface for listening to requests to enter split select */ /** * Callback interface for listening to requests to enter split select. Used for desktop -> split */ interface SplitSelectListener { default boolean onRequestEnterSplitSelect(ActivityManager.RunningTaskInfo taskInfo, int splitPosition, Rect taskBounds) { Loading @@ -72,6 +74,15 @@ public interface SplitScreen { } } interface SplitInvocationListener { /** * Called whenever shell starts or stops the split screen animation * @param animationRunning if {@code true} the animation has begun, if {@code false} the * animation has finished */ default void onSplitAnimationInvoked(boolean animationRunning) { } } /** Registers listener that gets split screen callback. */ void registerSplitScreenListener(@NonNull SplitScreenListener listener, @NonNull Executor executor); Loading @@ -79,6 +90,15 @@ public interface SplitScreen { /** Unregisters listener that gets split screen callback. */ void unregisterSplitScreenListener(@NonNull SplitScreenListener listener); /** * Registers a {@link SplitInvocationListener} to notify when the animation to enter split * screen has started and stopped * * @param executor callbacks to the listener will be executed on this executor */ void registerSplitAnimationListener(@NonNull SplitInvocationListener listener, @NonNull Executor executor); /** Called when device waking up finished. */ void onFinishedWakingUp(); Loading
libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +6 −0 Original line number Diff line number Diff line Loading @@ -1137,6 +1137,12 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, }); } @Override public void registerSplitAnimationListener(@NonNull SplitInvocationListener listener, @NonNull Executor executor) { mStageCoordinator.registerSplitAnimationListener(listener, executor); } @Override public void onFinishedWakingUp() { mMainExecutor.execute(SplitScreenController.this::onFinishedWakingUp); Loading
libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java +13 −0 Original line number Diff line number Diff line Loading @@ -55,6 +55,7 @@ import com.android.wm.shell.transition.OneShotRemoteHandler; import com.android.wm.shell.transition.Transitions; import java.util.ArrayList; import java.util.concurrent.Executor; /** Manages transition animations for split-screen. */ class SplitScreenTransitions { Loading @@ -79,6 +80,8 @@ class SplitScreenTransitions { private Transitions.TransitionFinishCallback mFinishCallback = null; private SurfaceControl.Transaction mFinishTransaction; private SplitScreen.SplitInvocationListener mSplitInvocationListener; private Executor mSplitInvocationListenerExecutor; SplitScreenTransitions(@NonNull TransactionPool pool, @NonNull Transitions transitions, @NonNull Runnable onFinishCallback, StageCoordinator stageCoordinator) { Loading Loading @@ -353,6 +356,10 @@ class SplitScreenTransitions { + " skip to start enter split transition since it already exist. "); return null; } if (mSplitInvocationListenerExecutor != null && mSplitInvocationListener != null) { mSplitInvocationListenerExecutor.execute(() -> mSplitInvocationListener .onSplitAnimationInvoked(true /*animationRunning*/)); } final IBinder transition = mTransitions.startTransition(transitType, wct, handler); setEnterTransition(transition, remoteTransition, extraTransitType, resizeAnim); return transition; Loading Loading @@ -529,6 +536,12 @@ class SplitScreenTransitions { mTransitions.getAnimExecutor().execute(va::start); } public void registerSplitAnimListener(@NonNull SplitScreen.SplitInvocationListener listener, @NonNull Executor executor) { mSplitInvocationListener = listener; mSplitInvocationListenerExecutor = executor; } /** Calls when the transition got consumed. */ interface TransitionConsumedCallback { void onConsumed(boolean aborted); Loading
libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +36 −7 Original line number Diff line number Diff line Loading @@ -156,6 +156,7 @@ import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.concurrent.Executor; /** * Coordinates the staging (visibility, sizing, ...) of the split-screen {@link MainStage} and Loading Loading @@ -236,6 +237,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private DefaultMixedHandler mMixedHandler; private final Toast mSplitUnsupportedToast; private SplitRequest mSplitRequest; /** Used to notify others of when shell is animating into split screen */ private SplitScreen.SplitInvocationListener mSplitInvocationListener; private Executor mSplitInvocationListenerExecutor; /** * Since StageCoordinator only coordinates MainStage and SideStage, it shouldn't support Loading @@ -246,6 +250,14 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, return false; } /** NOTE: Will overwrite any previously set {@link #mSplitInvocationListener} */ public void registerSplitAnimationListener( @NonNull SplitScreen.SplitInvocationListener listener, @NonNull Executor executor) { mSplitInvocationListener = listener; mSplitInvocationListenerExecutor = executor; mSplitTransitions.registerSplitAnimListener(listener, executor); } class SplitRequest { @SplitPosition int mActivatePosition; Loading Loading @@ -529,7 +541,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, null /* childrenToTop */, EXIT_REASON_UNKNOWN)); Log.w(TAG, splitFailureMessage("startShortcut", "side stage was not populated")); mSplitUnsupportedToast.show(); handleUnsupportedSplitStart(); } if (finishedCallback != null) { Loading Loading @@ -660,7 +672,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, null /* childrenToTop */, EXIT_REASON_UNKNOWN)); Log.w(TAG, splitFailureMessage("startIntentLegacy", "side stage was not populated")); mSplitUnsupportedToast.show(); handleUnsupportedSplitStart(); } if (apps != null) { Loading Loading @@ -1213,7 +1225,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, ? mSideStage : mMainStage, EXIT_REASON_UNKNOWN)); Log.w(TAG, splitFailureMessage("onRemoteAnimationFinishedOrCancelled", "main or side stage was not populated.")); mSplitUnsupportedToast.show(); handleUnsupportedSplitStart(); } else { mSyncQueue.queue(evictWct); mSyncQueue.runInSync(t -> { Loading @@ -1234,7 +1246,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, ? mSideStage : mMainStage, EXIT_REASON_UNKNOWN)); Log.w(TAG, splitFailureMessage("onRemoteAnimationFinished", "main or side stage was not populated")); mSplitUnsupportedToast.show(); handleUnsupportedSplitStart(); return; } Loading Loading @@ -2801,6 +2813,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (hasEnteringPip) { mMixedHandler.animatePendingEnterPipFromSplit(transition, info, startTransaction, finishTransaction, finishCallback); notifySplitAnimationFinished(); return true; } Loading Loading @@ -2835,6 +2848,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // the transition, or synchronize task-org callbacks. } // Use normal animations. notifySplitAnimationFinished(); return false; } else if (mMixedHandler != null && TransitionUtil.hasDisplayChange(info)) { // A display-change has been un-expectedly inserted into the transition. Redirect Loading @@ -2848,6 +2862,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSplitLayout.update(startTransaction, true /* resetImePosition */); startTransaction.apply(); } notifySplitAnimationFinished(); return true; } } Loading Loading @@ -3021,7 +3036,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, pendingEnter.mRemoteHandler.onTransitionConsumed(transition, false /*aborted*/, finishT); } mSplitUnsupportedToast.show(); handleUnsupportedSplitStart(); return true; } } Loading Loading @@ -3050,6 +3065,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, final TransitionInfo.Change finalMainChild = mainChild; final TransitionInfo.Change finalSideChild = sideChild; enterTransition.setFinishedCallback((callbackWct, callbackT) -> { notifySplitAnimationFinished(); if (finalMainChild != null) { if (!mainNotContainOpenTask) { mMainStage.evictOtherChildren(callbackWct, finalMainChild.getTaskInfo().taskId); Loading Loading @@ -3466,6 +3482,19 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSplitLayout.isLeftRightSplit()); } private void handleUnsupportedSplitStart() { mSplitUnsupportedToast.show(); notifySplitAnimationFinished(); } private void notifySplitAnimationFinished() { if (mSplitInvocationListener == null || mSplitInvocationListenerExecutor == null) { return; } mSplitInvocationListenerExecutor.execute(() -> mSplitInvocationListener.onSplitAnimationInvoked(false /*animationRunning*/)); } /** * Logs the exit of splitscreen to a specific stage. This must be called before the exit is * executed. Loading Loading @@ -3528,7 +3557,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (!ENABLE_SHELL_TRANSITIONS) { StageCoordinator.this.exitSplitScreen(isMainStage ? mMainStage : mSideStage, EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW); mSplitUnsupportedToast.show(); handleUnsupportedSplitStart(); return; } Loading @@ -3548,7 +3577,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, "app package " + taskInfo.baseActivity.getPackageName() + " does not support splitscreen, or is a controlled activity type")); if (splitScreenVisible) { mSplitUnsupportedToast.show(); handleUnsupportedSplitStart(); } } } Loading
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java +16 −0 Original line number Diff line number Diff line Loading @@ -39,10 +39,13 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.annotation.NonNull; import android.app.ActivityManager; Loading @@ -63,6 +66,7 @@ import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestRunningTaskInfoBuilder; import com.android.wm.shell.TestShellExecutor; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.DisplayInsetsController; Loading Loading @@ -105,6 +109,8 @@ public class SplitTransitionTests extends ShellTestCase { @Mock private ShellExecutor mMainExecutor; @Mock private LaunchAdjacentController mLaunchAdjacentController; @Mock private DefaultMixedHandler mMixedHandler; @Mock private SplitScreen.SplitInvocationListener mInvocationListener; private final TestShellExecutor mTestShellExecutor = new TestShellExecutor(); private SplitLayout mSplitLayout; private MainStage mMainStage; private SideStage mSideStage; Loading Loading @@ -147,6 +153,7 @@ public class SplitTransitionTests extends ShellTestCase { .setParentTaskId(mSideStage.mRootTaskInfo.taskId).build(); doReturn(mock(SplitDecorManager.class)).when(mMainStage).getSplitDecorManager(); doReturn(mock(SplitDecorManager.class)).when(mSideStage).getSplitDecorManager(); mStageCoordinator.registerSplitAnimationListener(mInvocationListener, mTestShellExecutor); } @Test Loading Loading @@ -452,6 +459,15 @@ public class SplitTransitionTests extends ShellTestCase { mMainStage.activate(new WindowContainerTransaction(), true /* includingTopTask */); } @Test @UiThreadTest public void testSplitInvocationCallback() { enterSplit(); mTestShellExecutor.flushAll(); verify(mInvocationListener, times(1)) .onSplitAnimationInvoked(eq(true)); } private boolean containsSplitEnter(@NonNull WindowContainerTransaction wct) { for (int i = 0; i < wct.getHierarchyOps().size(); ++i) { WindowContainerTransaction.HierarchyOp op = wct.getHierarchyOps().get(i); Loading