Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d46d51a5 authored by Vinit Nayak's avatar Vinit Nayak
Browse files

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: I3486274edeebc000b55a262be34b7f5cdeab63a4
parent 1c1bd35f
Loading
Loading
Loading
Loading
+21 −1
Original line number Diff line number Diff line
@@ -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();

+6 −0
Original line number Diff line number Diff line
@@ -1133,6 +1133,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);
+13 −0
Original line number Diff line number Diff line
@@ -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);
+36 −7
Original line number Diff line number Diff line
@@ -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();
                }
            }
        }
+16 −0
Original line number Diff line number Diff line
@@ -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);
Loading