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

Commit e5bca046 authored by Tony Huang's avatar Tony Huang
Browse files

Implement resize enter animation

Implement same animation as legacy transition.
Drag drop to split use fade in animation and the other cases
use fling animation.

Bug: 245472831
Fix: 279176453
Fix: 280400154
Test: manual
Test: pass existing tests
Change-Id: Ib722b75af6ee7125d9631c6fb0ea842708bbff64
parent 9ccb30e3
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -22,6 +22,8 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMA
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;

import static com.android.wm.shell.common.split.SplitScreenConstants.FADE_DURATION;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
@@ -58,7 +60,6 @@ public class SplitDecorManager extends WindowlessWindowManager {
    private static final String TAG = SplitDecorManager.class.getSimpleName();
    private static final String RESIZING_BACKGROUND_SURFACE_NAME = "ResizingBackground";
    private static final String GAP_BACKGROUND_SURFACE_NAME = "GapBackground";
    private static final long FADE_DURATION = 133;

    private final IconProvider mIconProvider;
    private final SurfaceSession mSurfaceSession;
@@ -261,6 +262,7 @@ public class SplitDecorManager extends WindowlessWindowManager {

            final SurfaceControl.Transaction animT = new SurfaceControl.Transaction();
            mScreenshotAnimator = ValueAnimator.ofFloat(1, 0);
            mScreenshotAnimator.setDuration(FADE_DURATION);
            mScreenshotAnimator.addUpdateListener(valueAnimator -> {
                final float progress = (float) valueAnimator.getAnimatedValue();
                animT.setAlpha(mScreenshot, progress);
+5 −0
Original line number Diff line number Diff line
@@ -43,6 +43,11 @@ public class SplitScreenConstants {
     */
    public static final int SPLIT_POSITION_BOTTOM_OR_RIGHT = 1;

    /**
     * Duration used for every split fade-in or fade-out.
     */
    public static final int FADE_DURATION = 133;

    @IntDef(prefix = {"SPLIT_POSITION_"}, value = {
            SPLIT_POSITION_UNDEFINED,
            SPLIT_POSITION_TOP_OR_LEFT,
+65 −79
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ 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 com.android.wm.shell.common.split.SplitScreenConstants.FADE_DURATION;
import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
import static com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
@@ -34,7 +35,6 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Rect;
import android.os.IBinder;
import android.view.SurfaceControl;
import android.view.WindowManager;
@@ -63,7 +63,7 @@ class SplitScreenTransitions {
    private final Runnable mOnFinish;

    DismissSession mPendingDismiss = null;
    TransitSession mPendingEnter = null;
    EnterSession mPendingEnter = null;
    TransitSession mPendingResize = null;

    private IBinder mAnimatingTransition = null;
@@ -120,6 +120,7 @@ class SplitScreenTransitions {
            @NonNull SurfaceControl.Transaction t, @NonNull WindowContainerToken mainRoot,
            @NonNull WindowContainerToken sideRoot, @NonNull WindowContainerToken topRoot) {
        // Play some place-holder fade animations
        final boolean isEnter = isPendingEnter(transition);
        for (int i = info.getChanges().size() - 1; i >= 0; --i) {
            final TransitionInfo.Change change = info.getChanges().get(i);
            final SurfaceControl leash = change.getLeash();
@@ -142,17 +143,23 @@ class SplitScreenTransitions {
                            change.getEndRelOffset().x, change.getEndRelOffset().y);
                }
            }
            boolean isRootOrSplitSideRoot = change.getParent() == null
                    || topRoot.equals(change.getParent());
            boolean isDivider = change.getFlags() == FLAG_IS_DIVIDER_BAR;
            // For enter or exit, we only want to animate side roots and the divider but not the
            // top-root.
            if (!isRootOrSplitSideRoot || topRoot.equals(change.getContainer()) || isDivider) {
                continue;
            }

            if (isPendingEnter(transition) && (mainRoot.equals(change.getContainer())
                    || sideRoot.equals(change.getContainer()))) {
            final boolean isTopRoot = topRoot.equals(change.getContainer());
            final boolean isMainRoot = mainRoot.equals(change.getContainer());
            final boolean isSideRoot = sideRoot.equals(change.getContainer());
            final boolean isDivider = change.getFlags() == FLAG_IS_DIVIDER_BAR;
            final boolean isMainChild = mainRoot.equals(change.getParent());
            final boolean isSideChild = sideRoot.equals(change.getParent());
            if (isEnter && (isMainChild || isSideChild)) {
                // Reset child tasks bounds on finish.
                mFinishTransaction.setPosition(leash,
                        change.getEndRelOffset().x, change.getEndRelOffset().y);
                mFinishTransaction.setCrop(leash, null);
            } else if (isEnter && isTopRoot) {
                // Ensure top root is visible at start.
                t.setAlpha(leash, 1.f);
                t.show(leash);
            } else if (isEnter && isMainRoot || isSideRoot) {
                t.setPosition(leash, change.getEndAbsBounds().left, change.getEndAbsBounds().top);
                t.setWindowCrop(leash, change.getEndAbsBounds().width(),
                        change.getEndAbsBounds().height());
@@ -161,10 +168,24 @@ class SplitScreenTransitions {
                t.setLayer(leash, Integer.MAX_VALUE);
                t.show(leash);
            }
            // These container changes we don't want to animate them.
            // We should only animate stage root, divider and child tasks are not under stage root.
            if (isTopRoot || isMainChild || isSideChild || change.getTaskInfo() == null) {
                continue;
            }

            if (isEnter && mPendingEnter.mResizeAnim) {
                // We will run animation in next transition so skip anim here
                continue;
            } else if (isEnter && isMainRoot) {
                // Main stage already on top so skip fade in animation to reduce flicker.
                continue;
            }

            boolean isOpening = TransitionUtil.isOpeningType(info.getType());
            if (isOpening && (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT)) {
                // fade in
                startExampleAnimation(leash, true /* show */);
                startFadeAnimation(leash, true /* show */);
            } else if (!isOpening && (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK)) {
                // fade out
                if (info.getType() == TRANSIT_SPLIT_DISMISS_SNAP) {
@@ -173,7 +194,7 @@ class SplitScreenTransitions {
                    // and don't animate it so it doesn't pop-in when reparented.
                    t.setAlpha(leash, 0.f);
                } else {
                    startExampleAnimation(leash, false /* show */);
                    startFadeAnimation(leash, false /* show */);
                }
            }
        }
@@ -265,7 +286,7 @@ class SplitScreenTransitions {
            Transitions.TransitionHandler handler,
            @Nullable TransitionConsumedCallback consumedCallback,
            @Nullable TransitionFinishedCallback finishedCallback,
            int extraTransitType) {
            int extraTransitType, boolean resizeAnim) {
        if (mPendingEnter != null) {
            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "  splitTransition "
                    + " skip to start enter split transition since it already exist. ");
@@ -273,7 +294,7 @@ class SplitScreenTransitions {
        }
        final IBinder transition = mTransitions.startTransition(transitType, wct, handler);
        setEnterTransition(transition, remoteTransition, consumedCallback, finishedCallback,
                extraTransitType);
                extraTransitType, resizeAnim);
        return transition;
    }

@@ -282,9 +303,10 @@ class SplitScreenTransitions {
            @Nullable RemoteTransition remoteTransition,
            @Nullable TransitionConsumedCallback consumedCallback,
            @Nullable TransitionFinishedCallback finishedCallback,
            int extraTransitType) {
        mPendingEnter = new TransitSession(
                transition, consumedCallback, finishedCallback, remoteTransition, extraTransitType);
            int extraTransitType, boolean resizeAnim) {
        mPendingEnter = new EnterSession(
                transition, consumedCallback, finishedCallback, remoteTransition, extraTransitType,
                resizeAnim);

        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "  splitTransition "
                + " deduced Enter split screen");
@@ -407,78 +429,27 @@ class SplitScreenTransitions {
        }
    }

    // TODO(shell-transitions): real animations
    private void startExampleAnimation(@NonNull SurfaceControl leash, boolean show) {
    private void startFadeAnimation(@NonNull SurfaceControl leash, boolean show) {
        final float end = show ? 1.f : 0.f;
        final float start = 1.f - end;
        final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
        final ValueAnimator va = ValueAnimator.ofFloat(start, end);
        va.setDuration(500);
        va.setDuration(FADE_DURATION);
        va.addUpdateListener(animation -> {
            float fraction = animation.getAnimatedFraction();
            transaction.setAlpha(leash, start * (1.f - fraction) + end * fraction);
            transaction.apply();
        });
        final Runnable finisher = () -> {
            transaction.setAlpha(leash, end);
            transaction.apply();
            mTransactionPool.release(transaction);
            mTransitions.getMainExecutor().execute(() -> {
                mAnimations.remove(va);
                onFinish(null /* wct */, null /* wctCB */);
            });
        };
        va.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                finisher.run();
            }

            @Override
            public void onAnimationCancel(Animator animation) {
                finisher.run();
            }
        });
        mAnimations.add(va);
        mTransitions.getAnimExecutor().execute(va::start);
    }

    // TODO(shell-transitions): real animations
    private void startExampleResizeAnimation(@NonNull SurfaceControl leash,
            @NonNull Rect startBounds, @NonNull Rect endBounds) {
        final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
        final ValueAnimator va = ValueAnimator.ofFloat(0.f, 1.f);
        va.setDuration(500);
        va.addUpdateListener(animation -> {
            float fraction = animation.getAnimatedFraction();
            transaction.setWindowCrop(leash,
                    (int) (startBounds.width() * (1.f - fraction) + endBounds.width() * fraction),
                    (int) (startBounds.height() * (1.f - fraction)
                            + endBounds.height() * fraction));
            transaction.setPosition(leash,
                    startBounds.left * (1.f - fraction) + endBounds.left * fraction,
                    startBounds.top * (1.f - fraction) + endBounds.top * fraction);
            transaction.apply();
        });
        final Runnable finisher = () -> {
            transaction.setWindowCrop(leash, 0, 0);
            transaction.setPosition(leash, endBounds.left, endBounds.top);
                transaction.setAlpha(leash, end);
                transaction.apply();
                mTransactionPool.release(transaction);
                mTransitions.getMainExecutor().execute(() -> {
                    mAnimations.remove(va);
                    onFinish(null /* wct */, null /* wctCB */);
                });
        };
        va.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                finisher.run();
            }

            @Override
            public void onAnimationCancel(Animator animation) {
                finisher.run();
            }
        });
        mAnimations.add(va);
@@ -567,6 +538,21 @@ class SplitScreenTransitions {
        }
    }

    /** Bundled information of enter transition. */
    class EnterSession extends TransitSession {
        final boolean mResizeAnim;

        EnterSession(IBinder transition,
                @Nullable TransitionConsumedCallback consumedCallback,
                @Nullable TransitionFinishedCallback finishedCallback,
                @Nullable RemoteTransition remoteTransition,
                int extraTransitType, boolean resizeAnim) {
            super(transition, consumedCallback, finishedCallback, remoteTransition,
                    extraTransitType);
            this.mResizeAnim = resizeAnim;
        }
    }

    /** Bundled information of dismiss transition. */
    class DismissSession extends TransitSession {
        final int mReason;
+32 −45
Original line number Diff line number Diff line
@@ -394,7 +394,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
            mSplitTransitions.startEnterTransition(TRANSIT_TO_FRONT, wct,
                    null, this, null /* consumedCallback */, null /* finishedCallback */,
                    isSplitScreenVisible()
                            ? TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE : TRANSIT_SPLIT_SCREEN_PAIR_OPEN);
                            ? TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE : TRANSIT_SPLIT_SCREEN_PAIR_OPEN,
                    !mIsDropEntering);
        } else {
            mSyncQueue.queue(wct);
            mSyncQueue.runInSync(t -> {
@@ -502,7 +503,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
        prepareEnterSplitScreen(wct, null /* taskInfo */, position);

        mSplitTransitions.startEnterTransition(TRANSIT_TO_FRONT, wct, null, this,
                null /* consumedCallback */, null /* finishedCallback */, extraTransitType);
                null /* consumedCallback */, null /* finishedCallback */, extraTransitType,
                !mIsDropEntering);
    }

    /** Launches an activity into split by legacy transition. */
@@ -660,7 +662,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,

        mSplitTransitions.startEnterTransition(
                TRANSIT_TO_FRONT, wct, remoteTransition, this, null, null,
                TRANSIT_SPLIT_SCREEN_PAIR_OPEN);
                TRANSIT_SPLIT_SCREEN_PAIR_OPEN, false);
        setEnterInstanceId(instanceId);
    }

@@ -712,7 +714,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,

        mSplitTransitions.startEnterTransition(
                TRANSIT_TO_FRONT, wct, remoteTransition, this, null, null,
                TRANSIT_SPLIT_SCREEN_PAIR_OPEN);
                TRANSIT_SPLIT_SCREEN_PAIR_OPEN, false);
        setEnterInstanceId(instanceId);
    }

@@ -1493,14 +1495,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,

    private void prepareBringSplit(WindowContainerTransaction wct,
            @Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition) {
        StageTaskListener targetStage;
        if (isSplitScreenVisible()) {
            // If the split screen is foreground, retrieves target stage based on position.
            targetStage = startPosition == mSideStagePosition ? mSideStage : mMainStage;
        } else {
            targetStage = mSideStage;
        }

        if (taskInfo != null) {
            wct.startTask(taskInfo.taskId,
                    resolveStartStage(STAGE_TYPE_UNDEFINED, startPosition, null, wct));
@@ -1509,13 +1503,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
        // and evict all tasks current under its.
        if (!isSplitScreenVisible()) {
            // Recreate so we need to reset position rather than keep position of background split.
            mSplitLayout.resetDividerPosition();
            updateWindowBounds(mSplitLayout, wct);
            final StageTaskListener anotherStage = targetStage == mMainStage
                    ? mSideStage : mMainStage;
            anotherStage.reparentTopTask(wct);
            wct.reorder(mRootTaskInfo.token, true);
            setRootForceTranslucent(false, wct);
            mMainStage.reparentTopTask(wct);
            prepareSplitLayout(wct);
        }
    }

@@ -1531,8 +1520,22 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
            mSideStage.addTask(taskInfo, wct);
        }
        mMainStage.activate(wct, true /* includingTopTask */);
        prepareSplitLayout(wct);
    }

    private void prepareSplitLayout(WindowContainerTransaction wct) {
        if (mIsDropEntering) {
            mSplitLayout.resetDividerPosition();
        } else {
            mSplitLayout.setDividerAtBorder(mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT);
        }
        updateWindowBounds(mSplitLayout, wct);
        if (!mIsDropEntering) {
            // Reset its smallest width dp to avoid is change layout before it actually resized to
            // split bounds.
            wct.setSmallestScreenWidthDp(mMainStage.mRootTaskInfo.token,
                    SMALLEST_SCREEN_WIDTH_DP_UNDEFINED);
        }
        wct.reorder(mRootTaskInfo.token, true);
        setRootForceTranslucent(false, wct);
    }
@@ -1547,6 +1550,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
        updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
        t.show(mRootTaskLeash);
        setSplitsVisible(true);
        mIsDropEntering = false;
        updateRecentTasksSplitPair();
        if (!mLogger.hasStartedSession()) {
            mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(),
@@ -1776,18 +1780,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
        // Handle entering split screen while there is a split pair running in the background.
        if (stageListener == mSideStageListener && !isSplitScreenVisible() && isSplitActive()
                && mSplitRequest == null) {
            if (mIsDropEntering) {
                mSplitLayout.resetDividerPosition();
            } else {
                mSplitLayout.setDividerAtBorder(mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT);
            }
            final WindowContainerTransaction wct = new WindowContainerTransaction();
            mMainStage.reparentTopTask(wct);
            prepareEnterSplitScreen(wct);
            mMainStage.evictAllChildren(wct);
            mSideStage.evictOtherChildren(wct, taskId);
            updateWindowBounds(mSplitLayout, wct);
            wct.reorder(mRootTaskInfo.token, true);
            setRootForceTranslucent(false, wct);

            mSyncQueue.queue(wct);
            mSyncQueue.runInSync(t -> {
@@ -1982,20 +1978,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
                exitSplitScreen(null /* childrenToTop */, EXIT_REASON_APP_FINISHED);
            }
        } else if (isSideStage && hasChildren && !mMainStage.isActive()) {
            mSplitLayout.init();

            final WindowContainerTransaction wct = new WindowContainerTransaction();
            if (mIsDropEntering) {
            prepareEnterSplitScreen(wct);
            } else {
                // TODO (b/238697912) : Add the validation to prevent entering non-recovered status
                onSplitScreenEnter();
                mSplitLayout.setDividerAtBorder(mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT);
                mMainStage.activate(wct, true /* includingTopTask */);
                updateWindowBounds(mSplitLayout, wct);
                wct.reorder(mRootTaskInfo.token, true);
                setRootForceTranslucent(false, wct);
            }

            mSyncQueue.queue(wct);
            mSyncQueue.runInSync(t -> {
@@ -2344,7 +2328,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
                prepareEnterSplitScreen(out);
                mSplitTransitions.setEnterTransition(transition, request.getRemoteTransition(),
                        null /* consumedCallback */, null /* finishedCallback */,
                        0 /* extraTransitType */);
                        0 /* extraTransitType */, !mIsDropEntering);
            }
        }
        return out;
@@ -2583,7 +2567,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
    }

    private boolean startPendingEnterAnimation(
            @NonNull SplitScreenTransitions.TransitSession enterTransition,
            @NonNull SplitScreenTransitions.EnterSession enterTransition,
            @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t,
            @NonNull SurfaceControl.Transaction finishT) {
        // First, verify that we actually have opened apps in both splits.
@@ -2641,7 +2625,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
                    + " to have been called with " + sideChild.getTaskInfo().taskId
                    + " before startAnimation().");
        }

        final TransitionInfo.Change finalMainChild = mainChild;
        final TransitionInfo.Change finalSideChild = sideChild;
        enterTransition.setFinishedCallback((callbackWct, callbackT) -> {
@@ -2651,6 +2634,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
            if (finalSideChild != null) {
                mSideStage.evictOtherChildren(callbackWct, finalSideChild.getTaskInfo().taskId);
            }
            if (enterTransition.mResizeAnim) {
                mShowDecorImmediately = true;
                mSplitLayout.flingDividerToCenter();
            }
        });

        finishEnterSplitScreen(finishT);
+2 −2
Original line number Diff line number Diff line
@@ -182,7 +182,7 @@ public class SplitTransitionTests extends ShellTestCase {
        IBinder transition = mSplitScreenTransitions.startEnterTransition(
                TRANSIT_OPEN, new WindowContainerTransaction(),
                new RemoteTransition(testRemote, "Test"), mStageCoordinator, null, null,
                TRANSIT_SPLIT_SCREEN_PAIR_OPEN);
                TRANSIT_SPLIT_SCREEN_PAIR_OPEN, false);
        mMainStage.onTaskAppeared(mMainChild, createMockSurface());
        mSideStage.onTaskAppeared(mSideChild, createMockSurface());
        boolean accepted = mStageCoordinator.startAnimation(transition, info,
@@ -408,7 +408,7 @@ public class SplitTransitionTests extends ShellTestCase {
        IBinder enterTransit = mSplitScreenTransitions.startEnterTransition(
                TRANSIT_OPEN, new WindowContainerTransaction(),
                new RemoteTransition(new TestRemoteTransition(), "Test"),
                mStageCoordinator, null, null, TRANSIT_SPLIT_SCREEN_PAIR_OPEN);
                mStageCoordinator, null, null, TRANSIT_SPLIT_SCREEN_PAIR_OPEN, false);
        mMainStage.onTaskAppeared(mMainChild, createMockSurface());
        mSideStage.onTaskAppeared(mSideChild, createMockSurface());
        mStageCoordinator.startAnimation(enterTransit, enterInfo,
Loading