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

Commit fdf92de8 authored by Jerry Chang's avatar Jerry Chang Committed by Android (Google) Code Review
Browse files

Merge "Integrate RecetsTaskController with shell-transition"

parents 96709414 7eccf61d
Loading
Loading
Loading
Loading
+38 −14
Original line number Diff line number Diff line
@@ -23,6 +23,10 @@ import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.window.TransitionInfo.FLAG_FIRST_CUSTOM;

import static com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
import static com.android.wm.shell.splitscreen.SplitScreenController.exitReasonToString;
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_DISMISS;
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_DISMISS_SNAP;
import static com.android.wm.shell.transition.Transitions.isOpeningType;

@@ -40,7 +44,9 @@ import android.window.TransitionInfo;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;

import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.transition.OneShotRemoteHandler;
import com.android.wm.shell.transition.Transitions;

@@ -57,7 +63,7 @@ class SplitScreenTransitions {
    private final Transitions mTransitions;
    private final Runnable mOnFinish;

    IBinder mPendingDismiss = null;
    DismissTransition mPendingDismiss = null;
    IBinder mPendingEnter = null;

    private IBinder mAnimatingTransition = null;
@@ -127,12 +133,6 @@ class SplitScreenTransitions {
                }
                // TODO(shell-transitions): screenshot here
                final Rect startBounds = new Rect(change.getStartAbsBounds());
                if (info.getType() == TRANSIT_SPLIT_DISMISS_SNAP) {
                    // Dismissing split via snap which means the still-visible task has been
                    // dragged to its end position at animation start so reflect that here.
                    startBounds.offsetTo(change.getEndAbsBounds().left,
                            change.getEndAbsBounds().top);
                }
                final Rect endBounds = new Rect(change.getEndAbsBounds());
                startBounds.offset(-info.getRootOffset().x, -info.getRootOffset().y);
                endBounds.offset(-info.getRootOffset().x, -info.getRootOffset().y);
@@ -184,12 +184,20 @@ class SplitScreenTransitions {
        return transition;
    }

    /** Starts a transition for dismissing split after dragging the divider to a screen edge */
    IBinder startSnapToDismiss(@NonNull WindowContainerTransaction wct,
            @NonNull Transitions.TransitionHandler handler) {
        final IBinder transition = mTransitions.startTransition(
                TRANSIT_SPLIT_DISMISS_SNAP, wct, handler);
        mPendingDismiss = transition;
    /** Starts a transition to dismiss split. */
    IBinder startDismissTransition(@Nullable IBinder transition, WindowContainerTransaction wct,
            Transitions.TransitionHandler handler, @SplitScreen.StageType int dismissTop,
            @SplitScreenController.ExitReason int reason) {
        final int type = reason == EXIT_REASON_DRAG_DIVIDER
                ? TRANSIT_SPLIT_DISMISS_SNAP : TRANSIT_SPLIT_DISMISS;
        if (transition == null) {
            transition = mTransitions.startTransition(type, wct, handler);
        }
        mPendingDismiss = new DismissTransition(transition, reason, dismissTop);

        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "  splitTransition "
                        + " deduced Dismiss due to %s. toTop=%s",
                exitReasonToString(reason), stageTypeToString(dismissTop));
        return transition;
    }

@@ -206,7 +214,7 @@ class SplitScreenTransitions {
        if (mAnimatingTransition == mPendingEnter) {
            mPendingEnter = null;
        }
        if (mAnimatingTransition == mPendingDismiss) {
        if (mPendingDismiss != null && mPendingDismiss.mTransition == mAnimatingTransition) {
            mPendingDismiss = null;
        }
        mAnimatingTransition = null;
@@ -295,4 +303,20 @@ class SplitScreenTransitions {
        mAnimations.add(va);
        mTransitions.getAnimExecutor().execute(va::start);
    }

    /** Bundled information of dismiss transition. */
    static class DismissTransition {
        IBinder mTransition;

        int mReason;

        @SplitScreen.StageType
        int mDismissTop;

        DismissTransition(IBinder transition, int reason, int dismissTop) {
            this.mTransition = transition;
            this.mReason = reason;
            this.mDismissTop = dismissTop;
        }
    }
}
+99 −55
Original line number Diff line number Diff line
@@ -18,9 +18,10 @@ package com.android.wm.shell.splitscreen;

import static android.app.ActivityOptions.KEY_LAUNCH_ROOT_TASK_TOKEN;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.view.WindowManager.transitTypeToString;
@@ -32,7 +33,6 @@ import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSIT
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
import static com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_FINISHED;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP;
@@ -40,6 +40,7 @@ import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_RETURN_HOME;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_UNKNOWN;
import static com.android.wm.shell.splitscreen.SplitScreenController.exitReasonToString;
import static com.android.wm.shell.splitscreen.SplitScreenTransitions.FLAG_IS_DIVIDER_BAR;
import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
@@ -121,9 +122,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,

    private static final String TAG = StageCoordinator.class.getSimpleName();

    /** internal value for mDismissTop that represents no dismiss */
    private static final int NO_DISMISS = -2;

    private final SurfaceSession mSurfaceSession = new SurfaceSession();

    private final MainStage mMainStage;
@@ -156,9 +154,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
    private boolean mKeyguardOccluded;
    private boolean mDeviceSleep;

    @StageType
    private int mDismissTop = NO_DISMISS;

    /** The target stage to dismiss to when unlock after folded. */
    @StageType
    private int mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
@@ -171,7 +166,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
            setDividerVisibility(false);
            mSplitLayout.resetDividerPosition();
        }
        mDismissTop = NO_DISMISS;
    };

    private final SplitWindowManager.ParentContainerCallbacks mParentContainerCallbacks =
@@ -461,8 +455,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
                        options = resolveStartStage(STAGE_TYPE_SIDE, position, options, wct);
                    }
                } else {
                    // Exit split-screen and launch fullscreen since stage wasn't specified.
                    prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct);
                    Slog.w(TAG,
                            "No stage type nor split position specified to resolve start stage");
                }
                break;
            }
@@ -914,23 +908,22 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
        }
    }

    @VisibleForTesting
    IBinder onSnappedToDismissTransition(boolean mainStageToTop) {
        final WindowContainerTransaction wct = new WindowContainerTransaction();
        prepareExitSplitScreen(mainStageToTop ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE, wct);
        return mSplitTransitions.startSnapToDismiss(wct, this);
    }

    @Override
    public void onSnappedToDismiss(boolean bottomOrRight) {
        final boolean mainStageToTop =
                bottomOrRight ? mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT
                        : mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT;
        if (ENABLE_SHELL_TRANSITIONS) {
            onSnappedToDismissTransition(mainStageToTop);

        if (!ENABLE_SHELL_TRANSITIONS) {
            exitSplitScreen(mainStageToTop ? mMainStage : mSideStage, EXIT_REASON_DRAG_DIVIDER);
            return;
        }
        exitSplitScreen(mainStageToTop ? mMainStage : mSideStage, EXIT_REASON_DRAG_DIVIDER);

        final int dismissTop = mainStageToTop ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE;
        final WindowContainerTransaction wct = new WindowContainerTransaction();
        prepareExitSplitScreen(dismissTop, wct);
        mSplitTransitions.startDismissTransition(
                null /* transition */, wct, this, dismissTop, EXIT_REASON_DRAG_DIVIDER);
    }

    @Override
@@ -1109,14 +1102,25 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
            @Nullable TransitionRequestInfo request) {
        final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask();
        if (triggerTask == null) {
            // still want to monitor everything while in split-screen, so return non-null.
            // Still want to monitor everything while in split-screen, so return non-null.
            return isSplitScreenVisible() ? new WindowContainerTransaction() : null;
        } else if (triggerTask.displayId != mDisplayId) {
            // Skip handling task on the other display.
            return null;
        }

        WindowContainerTransaction out = null;
        final @WindowManager.TransitionType int type = request.getType();
        final boolean isOpening = isOpeningType(type);
        final boolean inFullscreen = triggerTask.getWindowingMode() == WINDOWING_MODE_FULLSCREEN;

        if (isOpening && inFullscreen) {
            // One task is opening into fullscreen mode, remove the corresponding split record.
            mRecentTasks.ifPresent(recentTasks -> recentTasks.removeSplitPair(triggerTask.taskId));
        }

        if (isSplitScreenVisible()) {
            // try to handle everything while in split-screen, so return a WCT even if it's empty.
            // Try to handle everything while in split-screen, so return a WCT even if it's empty.
            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "  split is active so using split"
                            + "Transition to handle request. triggerTask=%d type=%s mainChildren=%d"
                            + " sideChildren=%d", triggerTask.taskId, transitTypeToString(type),
@@ -1124,35 +1128,35 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
            out = new WindowContainerTransaction();
            final StageTaskListener stage = getStageOfTask(triggerTask);
            if (stage != null) {
                // dismiss split if the last task in one of the stages is going away
                // Dismiss split if the last task in one of the stages is going away
                if (isClosingType(type) && stage.getChildCount() == 1) {
                    // The top should be the opposite side that is closing:
                    mDismissTop = getStageType(stage) == STAGE_TYPE_MAIN
                            ? STAGE_TYPE_SIDE : STAGE_TYPE_MAIN;
                }
                    int dismissTop = getStageType(stage) == STAGE_TYPE_MAIN ? STAGE_TYPE_SIDE
                            : STAGE_TYPE_MAIN;
                    prepareExitSplitScreen(dismissTop, out);
                    mSplitTransitions.startDismissTransition(transition, out, this, dismissTop,
                            EXIT_REASON_APP_FINISHED);
                }
            } else if (isOpening && inFullscreen) {
                final int activityType = triggerTask.getActivityType();
                if (activityType == ACTIVITY_TYPE_ASSISTANT) {
                    // We don't want assistant panel to dismiss split screen, so do nothing.
                } else {
                if (triggerTask.getActivityType() == ACTIVITY_TYPE_HOME && isOpeningType(type)) {
                    // Going home so dismiss both.
                    mDismissTop = STAGE_TYPE_UNDEFINED;
                }
                    // Going home or occluded by the other fullscreen task, so dismiss both.
                    prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, out);
                    final int exitReason = activityType == ACTIVITY_TYPE_HOME
                            ? EXIT_REASON_RETURN_HOME : EXIT_REASON_UNKNOWN;
                    mSplitTransitions.startDismissTransition(transition, out, this,
                            STAGE_TYPE_UNDEFINED, exitReason);
                }
            if (mDismissTop != NO_DISMISS) {
                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "  splitTransition "
                                + " deduced Dismiss from request. toTop=%s",
                        stageTypeToString(mDismissTop));
                prepareExitSplitScreen(mDismissTop, out);
                mSplitTransitions.mPendingDismiss = transition;
            }
        } else {
            // Not in split mode, so look for an open into a split stage to active split screen.
            if ((type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT)) {
                if (getStageOfTask(triggerTask) != null) {
                    // One task is appearing in split, prepare to enter split screen.
            if (isOpening && getStageOfTask(triggerTask) != null) {
                // One task is appearing into split, prepare to enter split screen.
                out = new WindowContainerTransaction();
                    mSplitTransitions.mPendingEnter = transition;
                mMainStage.activate(getMainStageBounds(), out, true /* includingTopTask */);
                mSideStage.moveToTop(getSideStageBounds(), out);
                }
                mSplitTransitions.mPendingEnter = transition;
            }
        }
        return out;
@@ -1164,8 +1168,9 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
            @NonNull SurfaceControl.Transaction startTransaction,
            @NonNull SurfaceControl.Transaction finishTransaction,
            @NonNull Transitions.TransitionFinishCallback finishCallback) {
        if (transition != mSplitTransitions.mPendingDismiss
                && transition != mSplitTransitions.mPendingEnter) {
        if (transition != mSplitTransitions.mPendingEnter && (
                mSplitTransitions.mPendingDismiss == null
                        || mSplitTransitions.mPendingDismiss.mTransition != transition)) {
            // Not entering or exiting, so just do some house-keeping and validation.

            // If we're not in split-mode, just abort so something else can handle it.
@@ -1208,8 +1213,10 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
        boolean shouldAnimate = true;
        if (mSplitTransitions.mPendingEnter == transition) {
            shouldAnimate = startPendingEnterAnimation(transition, info, startTransaction);
        } else if (mSplitTransitions.mPendingDismiss == transition) {
            shouldAnimate = startPendingDismissAnimation(transition, info, startTransaction);
        } else if (mSplitTransitions.mPendingDismiss != null
                && mSplitTransitions.mPendingDismiss.mTransition == transition) {
            shouldAnimate = startPendingDismissAnimation(
                    mSplitTransitions.mPendingDismiss, info, startTransaction);
        }
        if (!shouldAnimate) return false;

@@ -1263,10 +1270,22 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
                    + " to have been called with " + sideChild.getTaskInfo().taskId
                    + " before startAnimation().");
        }

        mShouldUpdateRecents = true;
        updateRecentTasksSplitPair();

        if (!mLogger.hasStartedSession()) {
            mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(),
                    getMainStagePosition(), mMainStage.getTopChildTaskUid(),
                    getSideStagePosition(), mSideStage.getTopChildTaskUid(),
                    mSplitLayout.isLandscape());
        }

        return true;
    }

    private boolean startPendingDismissAnimation(@NonNull IBinder transition,
    private boolean startPendingDismissAnimation(
            @NonNull SplitScreenTransitions.DismissTransition dismissTransition,
            @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t) {
        // Make some noise if things aren't totally expected. These states shouldn't effect
        // transitions locally, but remotes (like Launcher) may get confused if they were
@@ -1295,6 +1314,21 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
                    + "] before startAnimation().");
        }

        mRecentTasks.ifPresent(recentTasks -> {
            // Notify recents if we are exiting in a way that breaks the pair, and disable further
            // updates to splits in the recents until we enter split again
            if (shouldBreakPairedTaskInRecents(dismissTransition.mReason) && mShouldUpdateRecents) {
                for (TransitionInfo.Change change : info.getChanges()) {
                    final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
                    if (taskInfo != null
                            && taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
                        recentTasks.removeSplitPair(taskInfo.taskId);
                    }
                }
            }
        });
        mShouldUpdateRecents = false;

        // Update local states.
        setSplitsVisible(false);
        // Wait until after animation to update divider
@@ -1305,15 +1339,17 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
            t.setWindowCrop(mSideStage.mRootLeash, null);
        }

        if (mDismissTop == STAGE_TYPE_UNDEFINED) {
            // Going home (dismissing both splits)

        if (dismissTransition.mDismissTop == STAGE_TYPE_UNDEFINED) {
            logExit(dismissTransition.mReason);
            // TODO: Have a proper remote for this. Until then, though, reset state and use the
            //       normal animation stuff (which falls back to the normal launcher remote).
            t.hide(mSplitLayout.getDividerLeash());
            setDividerVisibility(false);
            mSplitTransitions.mPendingDismiss = null;
            return false;
        } else {
            logExitToStage(dismissTransition.mReason,
                    dismissTransition.mDismissTop == STAGE_TYPE_MAIN);
        }

        addDividerBarToTransition(info, t, false /* show */);
@@ -1457,9 +1493,17 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
        @Override
        public void onNoLongerSupportMultiWindow() {
            if (mMainStage.isActive()) {
                if (!ENABLE_SHELL_TRANSITIONS) {
                    StageCoordinator.this.exitSplitScreen(null /* childrenToTop */,
                            EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW);
                }

                final WindowContainerTransaction wct = new WindowContainerTransaction();
                prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct);
                mSplitTransitions.startDismissTransition(null /* transition */, wct,
                        StageCoordinator.this, STAGE_TYPE_UNDEFINED,
                        EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW);
            }
        }

        private void reset() {
+0 −6
Original line number Diff line number Diff line
@@ -127,12 +127,6 @@ class SplitScreenTransitions {
                }
                // TODO(shell-transitions): screenshot here
                final Rect startBounds = new Rect(change.getStartAbsBounds());
                if (info.getType() == TRANSIT_SPLIT_DISMISS_SNAP) {
                    // Dismissing split via snap which means the still-visible task has been
                    // dragged to its end position at animation start so reflect that here.
                    startBounds.offsetTo(change.getEndAbsBounds().left,
                            change.getEndAbsBounds().top);
                }
                final Rect endBounds = new Rect(change.getEndAbsBounds());
                startBounds.offset(-info.getRootOffset().x, -info.getRootOffset().y);
                endBounds.offset(-info.getRootOffset().x, -info.getRootOffset().y);
+3 −0
Original line number Diff line number Diff line
@@ -89,6 +89,9 @@ public class Transitions implements RemoteCallable<Transitions> {
    /** Transition type for entering split by opening an app into side-stage. */
    public static final int TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE = TRANSIT_FIRST_CUSTOM + 5;

    /** Transition type for dismissing split-screen. */
    public static final int TRANSIT_SPLIT_DISMISS = TRANSIT_FIRST_CUSTOM + 6;

    private final WindowOrganizer mOrganizer;
    private final Context mContext;
    private final ShellExecutor mMainExecutor;
+9 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.wm.shell;

import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;

import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -36,6 +37,7 @@ public final class TestRunningTaskInfoBuilder {
    private WindowContainerToken mToken = createMockWCToken();
    private int mParentTaskId = INVALID_TASK_ID;
    private @WindowConfiguration.ActivityType int mActivityType = ACTIVITY_TYPE_STANDARD;
    private @WindowConfiguration.WindowingMode int mWindowingMode = WINDOWING_MODE_UNDEFINED;

    public static WindowContainerToken createMockWCToken() {
        final IWindowContainerToken itoken = mock(IWindowContainerToken.class);
@@ -60,6 +62,12 @@ public final class TestRunningTaskInfoBuilder {
        return this;
    }

    public TestRunningTaskInfoBuilder setWindowingMode(
            @WindowConfiguration.WindowingMode int windowingMode) {
        mWindowingMode = windowingMode;
        return this;
    }

    public ActivityManager.RunningTaskInfo build() {
        final ActivityManager.RunningTaskInfo info = new ActivityManager.RunningTaskInfo();
        info.parentTaskId = INVALID_TASK_ID;
@@ -67,6 +75,7 @@ public final class TestRunningTaskInfoBuilder {
        info.parentTaskId = mParentTaskId;
        info.configuration.windowConfiguration.setBounds(mBounds);
        info.configuration.windowConfiguration.setActivityType(mActivityType);
        info.configuration.windowConfiguration.setWindowingMode(mWindowingMode);
        info.token = mToken;
        info.isResizeable = true;
        info.supportsMultiWindow = true;
Loading