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

Commit ce0ca84f authored by Evan Rosky's avatar Evan Rosky Committed by Android (Google) Code Review
Browse files

Merge "Move recents-during-split handling to MixedHandler" into udc-dev

parents ced6af5d 74b6fd42
Loading
Loading
Loading
Loading
+44 −40
Original line number Diff line number Diff line
@@ -259,37 +259,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
                }
            };

    private final SplitScreenTransitions.TransitionFinishedCallback
            mRecentTransitionFinishedCallback =
            new SplitScreenTransitions.TransitionFinishedCallback() {
                @Override
                public void onFinished(WindowContainerTransaction finishWct,
                        SurfaceControl.Transaction finishT) {
                    // Check if the recent transition is finished by returning to the current
                    // split, so we
                    // can restore the divider bar.
                    for (int i = 0; i < finishWct.getHierarchyOps().size(); ++i) {
                        final WindowContainerTransaction.HierarchyOp op =
                                finishWct.getHierarchyOps().get(i);
                        final IBinder container = op.getContainer();
                        if (op.getType() == HIERARCHY_OP_TYPE_REORDER && op.getToTop()
                                && (mMainStage.containsContainer(container)
                                || mSideStage.containsContainer(container))) {
                            updateSurfaceBounds(mSplitLayout, finishT,
                                    false /* applyResizingOffset */);
                            setDividerVisibility(true, finishT);
                            return;
                        }
                    }

                    // Dismiss the split screen if it's not returning to split.
                    prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, finishWct);
                    setSplitsVisible(false);
                    setDividerVisibility(false, finishT);
                    logExit(EXIT_REASON_UNKNOWN);
                }
            };

    protected StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
            ShellTaskOrganizer taskOrganizer, DisplayController displayController,
            DisplayImeController displayImeController,
@@ -388,6 +357,11 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
        return mMainStage.isActive();
    }

    /** Checks if `transition` is a pending enter-split transition. */
    public boolean isPendingEnter(IBinder transition) {
        return mSplitTransitions.isPendingEnter(transition);
    }

    @StageType
    int getStageOfTask(int taskId) {
        if (mMainStage.containsTask(taskId)) {
@@ -2264,11 +2238,16 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
                }
            } else if (isOpening && inFullscreen) {
                final int activityType = triggerTask.getActivityType();
                if (activityType == ACTIVITY_TYPE_HOME
                        || activityType == ACTIVITY_TYPE_RECENTS) {
                    // Enter overview panel, so start recent transition.
                    mSplitTransitions.setRecentTransition(transition, request.getRemoteTransition(),
                            mRecentTransitionFinishedCallback);
                if (activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS) {
                    if (request.getRemoteTransition() != null) {
                        // starting recents/home, so don't handle this and let it fall-through to
                        // the remote handler.
                        return null;
                    }
                    // Need to use the old stuff for non-remote animations, otherwise we don't
                    // exit split-screen.
                    mSplitTransitions.setRecentTransition(transition, null /* remote */,
                            this::onRecentsInSplitAnimationFinish);
                }
            }
        } else {
@@ -2398,7 +2377,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
            shouldAnimate = startPendingEnterAnimation(
                    transition, info, startTransaction, finishTransaction);
        } else if (mSplitTransitions.isPendingRecent(transition)) {
            shouldAnimate = startPendingRecentAnimation(transition, info, startTransaction);
            onRecentsInSplitAnimationStart(startTransaction);
        } else if (mSplitTransitions.isPendingDismiss(transition)) {
            shouldAnimate = startPendingDismissAnimation(
                    mSplitTransitions.mPendingDismiss, info, startTransaction, finishTransaction);
@@ -2653,10 +2632,35 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
        return true;
    }

    private boolean startPendingRecentAnimation(@NonNull IBinder transition,
            @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t) {
    /** Call this when starting the open-recents animation while split-screen is active. */
    public void onRecentsInSplitAnimationStart(@NonNull SurfaceControl.Transaction t) {
        setDividerVisibility(false, t);
        return true;
    }

    /** Call this when the recents animation during split-screen finishes. */
    public void onRecentsInSplitAnimationFinish(WindowContainerTransaction finishWct,
            SurfaceControl.Transaction finishT) {
        // Check if the recent transition is finished by returning to the current
        // split, so we can restore the divider bar.
        for (int i = 0; i < finishWct.getHierarchyOps().size(); ++i) {
            final WindowContainerTransaction.HierarchyOp op =
                    finishWct.getHierarchyOps().get(i);
            final IBinder container = op.getContainer();
            if (op.getType() == HIERARCHY_OP_TYPE_REORDER && op.getToTop()
                    && (mMainStage.containsContainer(container)
                    || mSideStage.containsContainer(container))) {
                updateSurfaceBounds(mSplitLayout, finishT,
                        false /* applyResizingOffset */);
                setDividerVisibility(true, finishT);
                return;
            }
        }

        // Dismiss the split screen if it's not returning to split.
        prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, finishWct);
        setSplitsVisible(false);
        setDividerVisibility(false, finishT);
        logExit(EXIT_REASON_UNKNOWN);
    }

    private void addDividerBarToTransition(@NonNull TransitionInfo info,
+72 −2
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.wm.shell.transition;

import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
@@ -25,6 +26,7 @@ import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP;
import static com.android.wm.shell.util.TransitionUtil.isOpeningType;

import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -68,14 +70,20 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler {
        /** Pip was entered while handling an intent with its own remoteTransition. */
        static final int TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE = 3;

        /** Recents transition while split-screen active. */
        static final int TYPE_RECENTS_DURING_SPLIT = 4;

        /** The default animation for this mixed transition. */
        static final int ANIM_TYPE_DEFAULT = 0;

        /** For ENTER_PIP_FROM_SPLIT, indicates that this is a to-home animation. */
        static final int ANIM_TYPE_GOING_HOME = 1;

        /** For RECENTS_DURING_SPLIT, is set when this turns into a pair->pair task switch. */
        static final int ANIM_TYPE_PAIR_TO_PAIR = 1;

        final int mType;
        int mAnimType = 0;
        int mAnimType = ANIM_TYPE_DEFAULT;
        final IBinder mTransition;

        Transitions.TransitionHandler mLeftoversHandler = null;
@@ -167,6 +175,27 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler {
            mixed.mLeftoversHandler = handler.first;
            mActiveTransitions.add(mixed);
            return handler.second;
        } else if (mSplitHandler.isSplitActive()
                && isOpeningType(request.getType())
                && request.getTriggerTask() != null
                && request.getTriggerTask().getWindowingMode() == WINDOWING_MODE_FULLSCREEN
                && (request.getTriggerTask().getActivityType() == ACTIVITY_TYPE_HOME
                        || request.getTriggerTask().getActivityType() == ACTIVITY_TYPE_RECENTS)
                && request.getRemoteTransition() != null) {
            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a recents request while "
                    + "Split-Screen is active, so treat it as Mixed.");
            Pair<Transitions.TransitionHandler, WindowContainerTransaction> handler =
                    mPlayer.dispatchRequest(transition, request, this);
            if (handler == null) {
                android.util.Log.e(Transitions.TAG, "   No handler for remote? This is unexpected"
                        + ", there should at-least be RemoteHandler.");
                return null;
            }
            final MixedTransition mixed = new MixedTransition(
                    MixedTransition.TYPE_RECENTS_DURING_SPLIT, transition);
            mixed.mLeftoversHandler = handler.first;
            mActiveTransitions.add(mixed);
            return handler.second;
        }
        return null;
    }
@@ -216,6 +245,9 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler {
        } else if (mixed.mType == MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) {
            return animateOpenIntentWithRemoteAndPip(mixed, info, startTransaction,
                    finishTransaction, finishCallback);
        } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_SPLIT) {
            return animateRecentsDuringSplit(mixed, info, startTransaction, finishTransaction,
                    finishCallback);
        } else {
            mActiveTransitions.remove(mixed);
            throw new IllegalStateException("Starting mixed animation without a known mixed type? "
@@ -441,12 +473,40 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler {
        return true;
    }

    private boolean animateRecentsDuringSplit(@NonNull final MixedTransition mixed,
            @NonNull TransitionInfo info,
            @NonNull SurfaceControl.Transaction startTransaction,
            @NonNull SurfaceControl.Transaction finishTransaction,
            @NonNull Transitions.TransitionFinishCallback finishCallback) {
        // Split-screen is only interested in the recents transition finishing (and merging), so
        // just wrap finish and start recents animation directly.
        Transitions.TransitionFinishCallback finishCB = (wct, wctCB) -> {
            mixed.mInFlightSubAnimations = 0;
            mActiveTransitions.remove(mixed);
            // If pair-to-pair switching, the post-recents clean-up isn't needed.
            if (mixed.mAnimType != MixedTransition.ANIM_TYPE_PAIR_TO_PAIR) {
                wct = wct != null ? wct : new WindowContainerTransaction();
                mSplitHandler.onRecentsInSplitAnimationFinish(wct, finishTransaction);
            }
            mSplitHandler.onTransitionAnimationComplete();
            finishCallback.onTransitionFinished(wct, wctCB);
        };
        mixed.mInFlightSubAnimations = 1;
        mSplitHandler.onRecentsInSplitAnimationStart(startTransaction);
        final boolean handled = mixed.mLeftoversHandler.startAnimation(mixed.mTransition, info,
                startTransaction, finishTransaction, finishCB);
        if (!handled) {
            mActiveTransitions.remove(mixed);
        }
        return handled;
    }

    @Override
    public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
            @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
            @NonNull Transitions.TransitionFinishCallback finishCallback) {
        for (int i = 0; i < mActiveTransitions.size(); ++i) {
            if (mActiveTransitions.get(i) != mergeTarget) continue;
            if (mActiveTransitions.get(i).mTransition != mergeTarget) continue;
            MixedTransition mixed = mActiveTransitions.get(i);
            if (mixed.mInFlightSubAnimations <= 0) {
                // Already done, so no need to end it.
@@ -474,6 +534,14 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler {
                    mixed.mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget,
                            finishCallback);
                }
            } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_SPLIT) {
                if (mSplitHandler.isPendingEnter(transition)) {
                    // Recents -> enter-split means that we are switching from one pair to
                    // another pair.
                    mixed.mAnimType = MixedTransition.ANIM_TYPE_PAIR_TO_PAIR;
                }
                mixed.mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget,
                        finishCallback);
            } else {
                throw new IllegalStateException("Playing a mixed transition with unknown type? "
                        + mixed.mType);
@@ -493,6 +561,8 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler {
        if (mixed == null) return;
        if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) {
            mPipHandler.onTransitionConsumed(transition, aborted, finishT);
        } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_SPLIT) {
            mixed.mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT);
        }
    }
}
+52 −13
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_P

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -249,7 +250,7 @@ public class SplitTransitionTests extends ShellTestCase {

    @Test
    @UiThreadTest
    public void testEnterRecents() {
    public void testEnterRecentsAndCommit() {
        enterSplit();

        ActivityManager.RunningTaskInfo homeTask = new TestRunningTaskInfoBuilder()
@@ -258,27 +259,65 @@ public class SplitTransitionTests extends ShellTestCase {
                .build();

        // Create a request to bring home forward
        TransitionRequestInfo request = new TransitionRequestInfo(TRANSIT_TO_FRONT, homeTask, null);
        TransitionRequestInfo request = new TransitionRequestInfo(TRANSIT_TO_FRONT, homeTask,
                mock(RemoteTransition.class));
        IBinder transition = mock(IBinder.class);
        WindowContainerTransaction result = mStageCoordinator.handleRequest(transition, request);

        assertTrue(result.isEmpty());
        // Don't handle recents opening
        assertNull(result);

        // make sure we haven't made any local changes yet (need to wait until transition is ready)
        assertTrue(mStageCoordinator.isSplitScreenVisible());

        // simulate the transition
        TransitionInfo info = new TransitionInfoBuilder(TRANSIT_TO_FRONT, 0)
                .addChange(TRANSIT_TO_FRONT, homeTask)
                .addChange(TRANSIT_TO_BACK, mMainChild)
                .addChange(TRANSIT_TO_BACK, mSideChild)
        // simulate the start of recents transition
        mMainStage.onTaskVanished(mMainChild);
        mSideStage.onTaskVanished(mSideChild);
        mStageCoordinator.onRecentsInSplitAnimationStart(mock(SurfaceControl.Transaction.class));
        assertTrue(mStageCoordinator.isSplitScreenVisible());

        // Make sure it cleans-up if recents doesn't restore
        WindowContainerTransaction commitWCT = new WindowContainerTransaction();
        mStageCoordinator.onRecentsInSplitAnimationFinish(commitWCT,
                mock(SurfaceControl.Transaction.class));
        assertFalse(mStageCoordinator.isSplitScreenVisible());
    }

    @Test
    @UiThreadTest
    public void testEnterRecentsAndRestore() {
        enterSplit();

        ActivityManager.RunningTaskInfo homeTask = new TestRunningTaskInfoBuilder()
                .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
                .setActivityType(ACTIVITY_TYPE_HOME)
                .build();

        // Create a request to bring home forward
        TransitionRequestInfo request = new TransitionRequestInfo(TRANSIT_TO_FRONT, homeTask,
                mock(RemoteTransition.class));
        IBinder transition = mock(IBinder.class);
        WindowContainerTransaction result = mStageCoordinator.handleRequest(transition, request);
        // Don't handle recents opening
        assertNull(result);

        // make sure we haven't made any local changes yet (need to wait until transition is ready)
        assertTrue(mStageCoordinator.isSplitScreenVisible());

        // simulate the start of recents transition
        mMainStage.onTaskVanished(mMainChild);
        mSideStage.onTaskVanished(mSideChild);
        mStageCoordinator.startAnimation(transition, info,
                mock(SurfaceControl.Transaction.class),
                mock(SurfaceControl.Transaction.class),
                mock(Transitions.TransitionFinishCallback.class));
        mStageCoordinator.onRecentsInSplitAnimationStart(mock(SurfaceControl.Transaction.class));
        assertTrue(mStageCoordinator.isSplitScreenVisible());

        // Make sure we remain in split after recents restores.
        WindowContainerTransaction restoreWCT = new WindowContainerTransaction();
        restoreWCT.reorder(mMainChild.token, true /* toTop */);
        restoreWCT.reorder(mSideChild.token, true /* toTop */);
        // simulate the restoreWCT being applied:
        mMainStage.onTaskAppeared(mMainChild, mock(SurfaceControl.class));
        mSideStage.onTaskAppeared(mSideChild, mock(SurfaceControl.class));
        mStageCoordinator.onRecentsInSplitAnimationFinish(restoreWCT,
                mock(SurfaceControl.Transaction.class));
        assertTrue(mStageCoordinator.isSplitScreenVisible());
    }