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

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

Improve enter and dismiss transition by using snapshot

If reparent and bounds changed case, we have snapshot on those
transition change and we could use them to do cross fade animation.
And it could reduce flicker caused by black screen if we only do
fade-in animation(black screen on start of fade-in).

Bug: 245472831
Fix: 280013136
Fix: 281769738
Test: manual
Test: pass existing tests
Change-Id: I145f777ba5ec100be2ecbe4759a8c3d59fe59b27
parent 4422c68b
Loading
Loading
Loading
Loading
+38 −18
Original line number Diff line number Diff line
@@ -20,8 +20,9 @@ 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;
import static android.view.WindowManager.TRANSIT_TO_FRONT;

import static com.android.wm.shell.animation.Interpolators.ALPHA_IN;
import static com.android.wm.shell.animation.Interpolators.ALPHA_OUT;
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;
@@ -86,6 +87,7 @@ class SplitScreenTransitions {
        mStageCoordinator = stageCoordinator;
    }

    /** Play animation for enter transition or dismiss transition. */
    void playAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
            @NonNull SurfaceControl.Transaction startTransaction,
            @NonNull SurfaceControl.Transaction finishTransaction,
@@ -116,6 +118,7 @@ class SplitScreenTransitions {
        playInternalAnimation(transition, info, startTransaction, mainRoot, sideRoot, topRoot);
    }

    /** Internal funcation of playAnimation. */
    private void playInternalAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
            @NonNull SurfaceControl.Transaction t, @NonNull WindowContainerToken mainRoot,
            @NonNull WindowContainerToken sideRoot, @NonNull WindowContainerToken topRoot) {
@@ -126,8 +129,8 @@ class SplitScreenTransitions {
            final SurfaceControl leash = change.getLeash();
            final int mode = info.getChanges().get(i).getMode();

            if (mode == TRANSIT_CHANGE) {
            final int rootIdx = TransitionUtil.rootIndexFor(change, info);
            if (mode == TRANSIT_CHANGE) {
                if (change.getParent() != null) {
                    // This is probably reparented, so we want the parent to be immediately visible
                    final TransitionInfo.Change parentChange = info.getChange(change.getParent());
@@ -155,7 +158,7 @@ class SplitScreenTransitions {
                mFinishTransaction.setPosition(leash,
                        change.getEndRelOffset().x, change.getEndRelOffset().y);
                mFinishTransaction.setCrop(leash, null);
            } else if (isEnter && isTopRoot) {
            } else if (isTopRoot) {
                // Ensure top root is visible at start.
                t.setAlpha(leash, 1.f);
                t.show(leash);
@@ -168,40 +171,56 @@ 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) {

            // We want to use child tasks to animate so ignore split root container and non task
            // except divider change.
            if (isTopRoot || isMainRoot || isSideRoot
                    || (change.getTaskInfo() == null && !isDivider)) {
                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.
            } else if (isPendingDismiss(transition)
                    && mPendingDismiss.mReason == EXIT_REASON_DRAG_DIVIDER) {
                // TODO(b/280020345): need to refine animation for this but just skip anim now.
                continue;
            }

            // Because cross fade might be looked more flicker during animation
            // (surface become black in middle of animation), we only do fade-out
            // and show opening surface directly.
            boolean isOpening = TransitionUtil.isOpeningType(info.getType());
            if (isOpening && (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT)) {
                // fade in
                startFadeAnimation(leash, true /* show */);
            } else if (!isOpening && (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK)) {
            if (!isOpening && (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK)) {
                // fade out
                if (info.getType() == TRANSIT_SPLIT_DISMISS_SNAP) {
                    // Dismissing via snap-to-top/bottom means that the dismissed task is already
                    // not-visible (usually cropped to oblivion) so immediately set its alpha to 0
                    // and don't animate it so it doesn't pop-in when reparented.
                    t.setAlpha(leash, 0.f);
                if (change.getSnapshot() != null) {
                    // This case is happened if task is going to reparent to TDA, the origin leash
                    // doesn't rendor so we use snapshot to replace it animating.
                    t.reparent(change.getSnapshot(), info.getRoot(rootIdx).getLeash());
                    // Use origin leash layer.
                    t.setLayer(change.getSnapshot(), info.getChanges().size() - i);
                    t.setPosition(change.getSnapshot(), change.getStartAbsBounds().left,
                            change.getStartAbsBounds().top);
                    t.show(change.getSnapshot());
                    startFadeAnimation(change.getSnapshot(), false /* show */);
                } else {
                    startFadeAnimation(leash, false /* show */);
                }
            } else if (mode == TRANSIT_CHANGE && change.getSnapshot() != null) {
                t.reparent(change.getSnapshot(), info.getRoot(rootIdx).getLeash());
                // Ensure snapshot it on the top of all transition surfaces
                t.setLayer(change.getSnapshot(), info.getChanges().size() + 1);
                t.setPosition(change.getSnapshot(), change.getStartAbsBounds().left,
                        change.getStartAbsBounds().top);
                t.show(change.getSnapshot());
                startFadeAnimation(change.getSnapshot(), false /* show */);
            }
        }
        t.apply();
        onFinish(null /* wct */, null /* wctCB */);
    }

    /** Play animation for resize transition. */
    void playResizeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
            @NonNull SurfaceControl.Transaction startTransaction,
            @NonNull SurfaceControl.Transaction finishTransaction,
@@ -437,6 +456,7 @@ class SplitScreenTransitions {
        final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
        final ValueAnimator va = ValueAnimator.ofFloat(start, end);
        va.setDuration(FADE_DURATION);
        va.setInterpolator(show ? ALPHA_IN : ALPHA_OUT);
        va.addUpdateListener(animation -> {
            float fraction = animation.getAnimatedFraction();
            transaction.setAlpha(leash, start * (1.f - fraction) + end * fraction);