diff options
8 files changed, 107 insertions, 17 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java index ad4049320d93..f7ffdd1d573e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java @@ -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) { @@ -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); @@ -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(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index 952e2d4b3b9a..79bc24edf961 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -1134,6 +1134,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); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java index 1a53a1d10dd2..e69ff70e5273 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java @@ -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 { @@ -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) { @@ -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; @@ -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); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index 76504447339f..3dcf3b61f41f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -155,6 +155,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 @@ -235,6 +236,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 @@ -245,6 +249,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; @@ -528,7 +540,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) { @@ -659,7 +671,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) { @@ -1211,7 +1223,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 -> { @@ -1232,7 +1244,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; } @@ -2787,6 +2799,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (hasEnteringPip) { mMixedHandler.animatePendingEnterPipFromSplit(transition, info, startTransaction, finishTransaction, finishCallback); + notifySplitAnimationFinished(); return true; } @@ -2821,6 +2834,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 @@ -2834,6 +2848,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSplitLayout.update(startTransaction, true /* resetImePosition */); startTransaction.apply(); } + notifySplitAnimationFinished(); return true; } } @@ -3007,7 +3022,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, pendingEnter.mRemoteHandler.onTransitionConsumed(transition, false /*aborted*/, finishT); } - mSplitUnsupportedToast.show(); + handleUnsupportedSplitStart(); return true; } } @@ -3036,6 +3051,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); @@ -3452,6 +3468,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. @@ -3514,7 +3543,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; } @@ -3534,7 +3563,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(); } } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java index befc702b01aa..34b2eebb15a1 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java @@ -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; @@ -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; @@ -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; @@ -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 @@ -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); diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java index c08b0837da8e..69aa90996946 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java @@ -77,7 +77,7 @@ public class QuickStepContract { // settings is expanded. public static final int SYSUI_STATE_QUICK_SETTINGS_EXPANDED = 1 << 11; // Winscope tracing is enabled - public static final int SYSUI_STATE_TRACING_ENABLED = 1 << 12; + public static final int SYSUI_STATE_DISABLE_GESTURE_SPLIT_INVOCATION = 1 << 12; // The Assistant gesture should be constrained. It is up to the launcher implementation to // decide how to constrain it public static final int SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED = 1 << 13; @@ -148,7 +148,7 @@ public class QuickStepContract { SYSUI_STATE_OVERVIEW_DISABLED, SYSUI_STATE_HOME_DISABLED, SYSUI_STATE_SEARCH_DISABLED, - SYSUI_STATE_TRACING_ENABLED, + SYSUI_STATE_DISABLE_GESTURE_SPLIT_INVOCATION, SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED, SYSUI_STATE_BUBBLES_EXPANDED, SYSUI_STATE_DIALOG_SHOWING, @@ -211,8 +211,8 @@ public class QuickStepContract { if ((flags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0) { str.add("a11y_long_click"); } - if ((flags & SYSUI_STATE_TRACING_ENABLED) != 0) { - str.add("tracing"); + if ((flags & SYSUI_STATE_DISABLE_GESTURE_SPLIT_INVOCATION) != 0) { + str.add("disable_gesture_split_invocation"); } if ((flags & SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED) != 0) { str.add("asst_gesture_constrain"); diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index 5f8b5ddc9de6..80da44e8aa5b 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -36,7 +36,6 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_D import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_GOING_AWAY; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED; -import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_WAKEFULNESS_TRANSITION; @@ -113,8 +112,6 @@ import com.android.systemui.statusbar.policy.CallbackController; import com.android.systemui.unfold.progress.UnfoldTransitionProgressForwarder; import com.android.wm.shell.sysui.ShellInterface; -import dagger.Lazy; - import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; @@ -126,6 +123,8 @@ import java.util.function.Supplier; import javax.inject.Inject; import javax.inject.Provider; +import dagger.Lazy; + /** * Class to send information from overview to launcher with a binder. */ @@ -668,8 +667,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis // Listen for tracing state changes @Override public void onTracingStateChanged(boolean enabled) { - mSysUiState.setFlag(SYSUI_STATE_TRACING_ENABLED, enabled) - .commitUpdate(mContext.getDisplayId()); + // TODO(b/286509643) Cleanup callers of this; Unused downstream } @Override diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java index 324d723207cd..5b2dda80058f 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java @@ -21,6 +21,7 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_B import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DIALOG_SHOWING; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE; +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DISABLE_GESTURE_SPLIT_INVOCATION; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ONE_HANDED_ACTIVE; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED; @@ -260,6 +261,13 @@ public final class WMShell implements splitScreen.goToFullscreenFromSplit(); } }); + splitScreen.registerSplitAnimationListener(new SplitScreen.SplitInvocationListener() { + @Override + public void onSplitAnimationInvoked(boolean animationRunning) { + mSysUiState.setFlag(SYSUI_STATE_DISABLE_GESTURE_SPLIT_INVOCATION, animationRunning) + .commitUpdate(mDisplayTracker.getDefaultDisplayId()); + } + }, mSysUiMainExecutor); } @VisibleForTesting |