diff options
| author | 2024-03-06 17:11:53 -0800 | |
|---|---|---|
| committer | 2024-05-06 16:21:32 -0700 | |
| commit | e0bf32a0ee04110cc0c5bb7243bd42ca84d4bf63 (patch) | |
| tree | 63e183526c5acb8d004d555d896947fae057f0f0 | |
| parent | 20d149afc7e10a0f05bf4a40c47d29f6065856a4 (diff) | |
Disable recents gesture when split animation running in shell
* Pass in SysUiState flag to disable and re-enable system
gestures in TouchInteractionService.
* We disable gestures on any transitions that StageCoordinator
itself starts
* We re-enable on the finishCallback of the tranisiton for success
cases or anywhere else downstream of startAnimation() where we
return early. We also re-enable on all places the split unsupported
toast is currently shown.
Bug: 286509643
Test: Invoked split in various ways and immediately swiping up after
split is created doesn't repro bug
Change-Id: I50d52fb8b099a6a000373e04d9a6016bd18e7169
8 files changed, 108 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 6aad4e2c9da4..8df287d12cbc 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 @@ -69,7 +69,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) { @@ -90,6 +92,24 @@ public interface SplitScreen { /** Unregisters listener that gets split screen callback. */ void unregisterSplitScreenListener(@NonNull SplitScreenListener listener); + 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 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 547457b018a1..b9d70e1a599d 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 @@ -1166,6 +1166,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..6e5b7673e206 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; @@ -457,6 +464,7 @@ class SplitScreenTransitions { mPendingEnter.onConsumed(aborted); mPendingEnter = null; + mStageCoordinator.notifySplitAnimationFinished(); ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTransitionConsumed for enter transition"); } else if (isPendingDismiss(transition)) { mPendingDismiss.onConsumed(aborted); @@ -529,6 +537,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 2a50b191b1e9..db9f3d64f4dc 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 @@ -157,6 +157,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 @@ -237,6 +238,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 @@ -247,6 +251,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; @@ -530,7 +542,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) { @@ -661,7 +673,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) { @@ -1214,7 +1226,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 -> { @@ -1235,7 +1247,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; } @@ -2817,6 +2829,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (hasEnteringPip) { mMixedHandler.animatePendingEnterPipFromSplit(transition, info, startTransaction, finishTransaction, finishCallback); + notifySplitAnimationFinished(); return true; } @@ -2851,6 +2864,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 @@ -2864,6 +2878,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSplitLayout.update(startTransaction, true /* resetImePosition */); startTransaction.apply(); } + notifySplitAnimationFinished(); return true; } } @@ -3037,7 +3052,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, pendingEnter.mRemoteHandler.onTransitionConsumed(transition, false /*aborted*/, finishT); } - mSplitUnsupportedToast.show(); + handleUnsupportedSplitStart(); return true; } } @@ -3066,6 +3081,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); @@ -3487,6 +3503,19 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSplitLayout.isLeftRightSplit()); } + private void handleUnsupportedSplitStart() { + mSplitUnsupportedToast.show(); + notifySplitAnimationFinished(); + } + + 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. @@ -3549,7 +3578,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; } @@ -3569,7 +3598,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 b92e8eb72353..981add3d4626 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -37,7 +37,6 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_F 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; @@ -115,8 +114,6 @@ import com.android.systemui.unfold.progress.UnfoldTransitionProgressForwarder; import com.android.wm.shell.desktopmode.DesktopModeStatus; import com.android.wm.shell.sysui.ShellInterface; -import dagger.Lazy; - import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; @@ -128,6 +125,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. */ @@ -691,8 +690,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 263ddc175647..b86a7c92ba47 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; @@ -273,6 +274,13 @@ public final class WMShell implements splitScreen.setSplitscreenFocus(leftOrTop); } }); + 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 |