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

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

Merge "Consolidate recent animation in split screen with shell-transition"

parents 0b46683f 3c1ea525
Loading
Loading
Loading
Loading
+40 −13
Original line number Diff line number Diff line
@@ -44,6 +44,7 @@ import android.window.RemoteTransition;
import android.window.TransitionInfo;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import android.window.WindowContainerTransactionCallback;

import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.TransactionPool;
@@ -66,28 +67,26 @@ class SplitScreenTransitions {

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

    private IBinder mAnimatingTransition = null;
    private OneShotRemoteHandler mRemoteHandler = null;

    private Transitions.TransitionFinishCallback mRemoteFinishCB = (wct, wctCB) -> {
        if (wct != null || wctCB != null) {
            throw new UnsupportedOperationException("finish transactions not supported yet.");
        }
        onFinish();
    };
    private final Transitions.TransitionFinishCallback mRemoteFinishCB = this::onFinish;

    /** Keeps track of currently running animations */
    private final ArrayList<Animator> mAnimations = new ArrayList<>();
    private final StageCoordinator mStageCoordinator;

    private Transitions.TransitionFinishCallback mFinishCallback = null;
    private SurfaceControl.Transaction mFinishTransaction;

    SplitScreenTransitions(@NonNull TransactionPool pool, @NonNull Transitions transitions,
            @NonNull Runnable onFinishCallback) {
            @NonNull Runnable onFinishCallback, StageCoordinator stageCoordinator) {
        mTransactionPool = pool;
        mTransitions = transitions;
        mOnFinish = onFinishCallback;
        mStageCoordinator = stageCoordinator;
    }

    void playAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@@ -166,7 +165,7 @@ class SplitScreenTransitions {
            }
        }
        t.apply();
        onFinish();
        onFinish(null /* wct */, null /* wctCB */);
    }

    /** Starts a transition to enter split with a remote transition animator. */
@@ -203,7 +202,26 @@ class SplitScreenTransitions {
        return transition;
    }

    void onFinish() {
    IBinder startRecentTransition(@Nullable IBinder transition, WindowContainerTransaction wct,
            Transitions.TransitionHandler handler, @Nullable RemoteTransition remoteTransition) {
        if (transition == null) {
            transition = mTransitions.startTransition(TRANSIT_OPEN, wct, handler);
        }
        mPendingRecent = transition;

        if (remoteTransition != null) {
            // Wrapping it for ease-of-use (OneShot handles all the binder linking/death stuff)
            mRemoteHandler = new OneShotRemoteHandler(
                    mTransitions.getMainExecutor(), remoteTransition);
            mRemoteHandler.setTransition(transition);
        }

        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "  splitTransition "
                        + " deduced Enter recent panel");
        return transition;
    }

    void onFinish(WindowContainerTransaction wct, WindowContainerTransactionCallback wctCB) {
        if (!mAnimations.isEmpty()) return;
        mOnFinish.run();
        if (mFinishTransaction != null) {
@@ -211,14 +229,23 @@ class SplitScreenTransitions {
            mTransactionPool.release(mFinishTransaction);
            mFinishTransaction = null;
        }
        mFinishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
        if (mFinishCallback != null) {
            mFinishCallback.onTransitionFinished(wct /* wct */, wctCB /* wctCB */);
            mFinishCallback = null;
        }
        if (mAnimatingTransition == mPendingEnter) {
            mPendingEnter = null;
        }
        if (mPendingDismiss != null && mPendingDismiss.mTransition == mAnimatingTransition) {
            mPendingDismiss = null;
        }
        if (mAnimatingTransition == mPendingRecent) {
            // If the wct is not null while finishing recent transition, it indicates it's not
            // returning to home and hence needing the wct to reorder tasks.
            final boolean toHome = wct == null;
            mStageCoordinator.finishRecentAnimation(toHome);
            mPendingRecent = null;
        }
        mAnimatingTransition = null;
    }

@@ -240,7 +267,7 @@ class SplitScreenTransitions {
            mTransactionPool.release(transaction);
            mTransitions.getMainExecutor().execute(() -> {
                mAnimations.remove(va);
                onFinish();
                onFinish(null /* wct */, null /* wctCB */);
            });
        };
        va.addListener(new Animator.AnimatorListener() {
@@ -288,7 +315,7 @@ class SplitScreenTransitions {
            mTransactionPool.release(transaction);
            mTransitions.getMainExecutor().execute(() -> {
                mAnimations.remove(va);
                onFinish();
                onFinish(null /* wct */, null /* wctCB */);
            });
        };
        va.addListener(new AnimatorListenerAdapter() {
+33 −9
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ 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.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
@@ -236,7 +237,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
        deviceStateManager.registerCallback(taskOrganizer.getExecutor(),
                new DeviceStateManager.FoldStateListener(mContext, this::onFoldedStateChanged));
        mSplitTransitions = new SplitScreenTransitions(transactionPool, transitions,
                mOnTransitionAnimationComplete);
                mOnTransitionAnimationComplete, this);
        mDisplayController.addDisplayWindowListener(this);
        mDisplayLayout = new DisplayLayout(displayController.getDisplayLayout(displayId));
        transitions.addHandler(this);
@@ -266,7 +267,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
        mRootTDAOrganizer.registerListener(displayId, this);
        mSplitLayout = splitLayout;
        mSplitTransitions = new SplitScreenTransitions(transactionPool, transitions,
                mOnTransitionAnimationComplete);
                mOnTransitionAnimationComplete, this);
        mMainUnfoldController = unfoldControllerProvider.get().orElse(null);
        mSideUnfoldController = unfoldControllerProvider.get().orElse(null);
        mLogger = logger;
@@ -1271,13 +1272,16 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
                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 (activityType == ACTIVITY_TYPE_HOME
                        || activityType == ACTIVITY_TYPE_RECENTS) {
                    // Enter overview panel, so start recent transition.
                    mSplitTransitions.startRecentTransition(transition, out, this,
                            request.getRemoteTransition());
                } else {
                    // Going home or occluded by the other fullscreen task, so dismiss both.
                    // 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);
                            STAGE_TYPE_UNDEFINED, EXIT_REASON_UNKNOWN);
                }
            }
        } else {
@@ -1307,13 +1311,14 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
            @NonNull SurfaceControl.Transaction startTransaction,
            @NonNull SurfaceControl.Transaction finishTransaction,
            @NonNull Transitions.TransitionFinishCallback finishCallback) {
        if (transition != mSplitTransitions.mPendingEnter && (
                mSplitTransitions.mPendingDismiss == null
        if (transition != mSplitTransitions.mPendingEnter
                && transition != mSplitTransitions.mPendingRecent
                && (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.
            if (!isSplitScreenVisible()) return false;
            if (!mMainStage.isActive()) return false;

            for (int iC = 0; iC < info.getChanges().size(); ++iC) {
                final TransitionInfo.Change change = info.getChanges().get(iC);
@@ -1356,6 +1361,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
        boolean shouldAnimate = true;
        if (mSplitTransitions.mPendingEnter == transition) {
            shouldAnimate = startPendingEnterAnimation(transition, info, startTransaction);
        } else if (mSplitTransitions.mPendingRecent == transition) {
            shouldAnimate = startPendingRecentAnimation(transition, info, startTransaction);
        } else if (mSplitTransitions.mPendingDismiss != null
                && mSplitTransitions.mPendingDismiss.mTransition == transition) {
            shouldAnimate = startPendingDismissAnimation(
@@ -1500,6 +1507,23 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
        return true;
    }

    private boolean startPendingRecentAnimation(@NonNull IBinder transition,
            @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t) {
        setDividerVisibility(false, t);
        return true;
    }

    void finishRecentAnimation(boolean toHome) {
        if (toHome) {
            final WindowContainerTransaction wct = new WindowContainerTransaction();
            prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct);
            mSplitTransitions.startDismissTransition(null /* transition */, wct, this,
                    STAGE_TYPE_UNDEFINED, EXIT_REASON_RETURN_HOME);
        } else {
            setDividerVisibility(true, null /* t */);
        }
    }

    private void addDividerBarToTransition(@NonNull TransitionInfo info,
            @NonNull SurfaceControl.Transaction t, boolean show) {
        final SurfaceControl leash = mSplitLayout.getDividerLeash();
+3 −3
Original line number Diff line number Diff line
@@ -251,7 +251,7 @@ public class SplitTransitionTests extends ShellTestCase {
    }

    @Test
    public void testDismissToHome() {
    public void testEnterRecents() {
        enterSplit();

        ActivityManager.RunningTaskInfo homeTask = new TestRunningTaskInfoBuilder()
@@ -264,7 +264,7 @@ public class SplitTransitionTests extends ShellTestCase {
        IBinder transition = mock(IBinder.class);
        WindowContainerTransaction result = mStageCoordinator.handleRequest(transition, request);

        assertTrue(containsSplitExit(result));
        assertTrue(result.isEmpty());

        // make sure we haven't made any local changes yet (need to wait until transition is ready)
        assertTrue(mStageCoordinator.isSplitScreenVisible());
@@ -284,7 +284,7 @@ public class SplitTransitionTests extends ShellTestCase {
                mock(SurfaceControl.Transaction.class),
                mock(SurfaceControl.Transaction.class),
                mock(Transitions.TransitionFinishCallback.class));
        assertFalse(mStageCoordinator.isSplitScreenVisible());
        assertTrue(mStageCoordinator.isSplitScreenVisible());
    }

    @Test
+12 −23
Original line number Diff line number Diff line
@@ -17,7 +17,6 @@
package com.android.systemui.shared.system;

import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
@@ -140,22 +139,11 @@ public class RemoteAnimationTargetCompat {
        // changes should be ordered top-to-bottom in z
        final int mode = change.getMode();

        // Don't move anything that isn't independent within its parents
        if (!TransitionInfo.isIndependent(change, info)) {
            if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT || mode == TRANSIT_CHANGE) {
                t.show(leash);
                t.setPosition(leash, change.getEndRelOffset().x, change.getEndRelOffset().y);
            }
            return;
        }

        boolean hasParent = change.getParent() != null;

        if (!hasParent) {
        // Launcher animates leaf tasks directly, so always reparent all task leashes to root leash.
        t.reparent(leash, info.getRootLeash());
        t.setPosition(leash, change.getStartAbsBounds().left - info.getRootOffset().x,
                change.getStartAbsBounds().top - info.getRootOffset().y);
        }

        t.show(leash);
        // Put all the OPEN/SHOW on top
        if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT) {
@@ -266,14 +254,15 @@ public class RemoteAnimationTargetCompat {
            SurfaceControl.Transaction t, ArrayMap<SurfaceControl, SurfaceControl> leashMap) {
        final ArrayList<RemoteAnimationTargetCompat> out = new ArrayList<>();
        for (int i = 0; i < info.getChanges().size(); i++) {
            boolean changeIsWallpaper =
                    (info.getChanges().get(i).getFlags() & TransitionInfo.FLAG_IS_WALLPAPER) != 0;
            final TransitionInfo.Change change = info.getChanges().get(i);
            final boolean changeIsWallpaper =
                    (change.getFlags() & TransitionInfo.FLAG_IS_WALLPAPER) != 0;
            if (wallpapers != changeIsWallpaper) continue;
            out.add(new RemoteAnimationTargetCompat(info.getChanges().get(i),
                    info.getChanges().size() - i, info, t));
            if (leashMap == null) continue;
            leashMap.put(info.getChanges().get(i).getLeash(),
                    out.get(out.size() - 1).leash);

            out.add(new RemoteAnimationTargetCompat(change, info.getChanges().size() - i, info, t));
            if (leashMap != null) {
                leashMap.put(change.getLeash(), out.get(out.size() - 1).leash);
            }
        }
        return out.toArray(new RemoteAnimationTargetCompat[out.size()]);
    }