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

Commit 104a5f5c authored by Tony Huang's avatar Tony Huang
Browse files

Implement split resize shell transition

For reduce jank or flicker when resize ActivityEmbedding in split
case, we should apply shell transition to resize case too.

Fix: 247678744
Test: manual
Test: pass existing tests
Change-Id: I8a1aa500d5928284ca7955042dffb617be15635a
parent b60de628
Loading
Loading
Loading
Loading
+55 −22
Original line number Diff line number Diff line
@@ -51,8 +51,6 @@ import com.android.wm.shell.R;
import com.android.wm.shell.common.ScreenshotUtils;
import com.android.wm.shell.common.SurfaceUtils;

import java.util.function.Consumer;

/**
 * Handles split decor like showing resizing hint for a specific split.
 */
@@ -72,17 +70,18 @@ public class SplitDecorManager extends WindowlessWindowManager {
    private SurfaceControl mIconLeash;
    private SurfaceControl mBackgroundLeash;
    private SurfaceControl mGapBackgroundLeash;
    private SurfaceControl mScreenshot;

    private boolean mShown;
    private boolean mIsResizing;
    private final Rect mBounds = new Rect();
    private final Rect mResizingBounds = new Rect();
    private final Rect mTempRect = new Rect();
    private ValueAnimator mFadeAnimator;

    private int mIconSize;
    private int mOffsetX;
    private int mOffsetY;
    private int mRunningAnimationCount = 0;

    public SplitDecorManager(Configuration configuration, IconProvider iconProvider,
            SurfaceSession surfaceSession) {
@@ -173,7 +172,6 @@ public class SplitDecorManager extends WindowlessWindowManager {
            mIsResizing = true;
            mBounds.set(newBounds);
        }
        mResizingBounds.set(newBounds);
        mOffsetX = offsetX;
        mOffsetY = offsetY;

@@ -227,33 +225,41 @@ public class SplitDecorManager extends WindowlessWindowManager {
                t.setVisibility(mBackgroundLeash, show);
                t.setVisibility(mIconLeash, show);
            } else {
                startFadeAnimation(show, null /* finishedConsumer */);
                startFadeAnimation(show, false, null);
            }
            mShown = show;
        }
    }

    /** Stops showing resizing hint. */
    public void onResized(SurfaceControl.Transaction t) {
        if (!mShown && mIsResizing) {
            mTempRect.set(mResizingBounds);
            mTempRect.offsetTo(-mOffsetX, -mOffsetY);
            final SurfaceControl screenshot = ScreenshotUtils.takeScreenshot(t,
                    mHostLeash, mTempRect, Integer.MAX_VALUE - 1);
    public void onResized(SurfaceControl.Transaction t, Runnable animFinishedCallback) {
        if (mScreenshot != null) {
            t.setPosition(mScreenshot, mOffsetX, mOffsetY);

            final SurfaceControl.Transaction animT = new SurfaceControl.Transaction();
            final ValueAnimator va = ValueAnimator.ofFloat(1, 0);
            va.addUpdateListener(valueAnimator -> {
                final float progress = (float) valueAnimator.getAnimatedValue();
                animT.setAlpha(screenshot, progress);
                animT.setAlpha(mScreenshot, progress);
                animT.apply();
            });
            va.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationStart(Animator animation) {
                    mRunningAnimationCount++;
                }

                @Override
                public void onAnimationEnd(@androidx.annotation.NonNull Animator animation) {
                    animT.remove(screenshot);
                    mRunningAnimationCount--;
                    animT.remove(mScreenshot);
                    animT.apply();
                    animT.close();
                    mScreenshot = null;

                    if (mRunningAnimationCount == 0 && animFinishedCallback != null) {
                        animFinishedCallback.run();
                    }
                }
            });
            va.start();
@@ -285,10 +291,34 @@ public class SplitDecorManager extends WindowlessWindowManager {
            mFadeAnimator.cancel();
        }
        if (mShown) {
            fadeOutDecor(null /* finishedCallback */);
            fadeOutDecor(animFinishedCallback);
        } else {
            // Decor surface is hidden so release it directly.
            releaseDecor(t);
            if (mRunningAnimationCount == 0 && animFinishedCallback != null) {
                animFinishedCallback.run();
            }
        }
    }

    /** Screenshot host leash and attach on it if meet some conditions */
    public void screenshotIfNeeded(SurfaceControl.Transaction t) {
        if (!mShown && mIsResizing) {
            mTempRect.set(mBounds);
            mTempRect.offsetTo(0, 0);
            mScreenshot = ScreenshotUtils.takeScreenshot(t, mHostLeash, mTempRect,
                    Integer.MAX_VALUE - 1);
        }
    }

    /** Set screenshot and attach on host leash it if meet some conditions */
    public void setScreenshotIfNeeded(SurfaceControl screenshot, SurfaceControl.Transaction t) {
        if (screenshot == null || !screenshot.isValid()) return;

        if (!mShown && mIsResizing) {
            mScreenshot = screenshot;
            t.reparent(screenshot, mHostLeash);
            t.setLayer(screenshot, Integer.MAX_VALUE - 1);
        }
    }

@@ -296,18 +326,15 @@ public class SplitDecorManager extends WindowlessWindowManager {
     * directly. */
    public void fadeOutDecor(Runnable finishedCallback) {
        if (mShown) {
            startFadeAnimation(false /* show */, transaction -> {
                releaseDecor(transaction);
                if (finishedCallback != null) finishedCallback.run();
            });
            startFadeAnimation(false /* show */, true, finishedCallback);
            mShown = false;
        } else {
            if (finishedCallback != null) finishedCallback.run();
        }
    }

    private void startFadeAnimation(boolean show,
            Consumer<SurfaceControl.Transaction> finishedConsumer) {
    private void startFadeAnimation(boolean show, boolean releaseSurface,
            Runnable finishedCallback) {
        final SurfaceControl.Transaction animT = new SurfaceControl.Transaction();
        mFadeAnimator = ValueAnimator.ofFloat(0f, 1f);
        mFadeAnimator.setDuration(FADE_DURATION);
@@ -324,6 +351,7 @@ public class SplitDecorManager extends WindowlessWindowManager {
        mFadeAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(@NonNull Animator animation) {
                mRunningAnimationCount++;
                if (show) {
                    animT.show(mBackgroundLeash).show(mIconLeash);
                }
@@ -335,6 +363,7 @@ public class SplitDecorManager extends WindowlessWindowManager {

            @Override
            public void onAnimationEnd(@NonNull Animator animation) {
                mRunningAnimationCount--;
                if (!show) {
                    if (mBackgroundLeash != null) {
                        animT.hide(mBackgroundLeash);
@@ -343,11 +372,15 @@ public class SplitDecorManager extends WindowlessWindowManager {
                        animT.hide(mIconLeash);
                    }
                }
                if (finishedConsumer != null) {
                    finishedConsumer.accept(animT);
                if (releaseSurface) {
                    releaseDecor(animT);
                }
                animT.apply();
                animT.close();

                if (mRunningAnimationCount == 0 && finishedCallback != null) {
                    finishedCallback.run();
                }
            }
        });
        mFadeAnimator.start();
+67 −0
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@ import android.window.WindowContainerTransactionCallback;

import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.split.SplitDecorManager;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.transition.OneShotRemoteHandler;
import com.android.wm.shell.transition.Transitions;
@@ -64,6 +65,7 @@ class SplitScreenTransitions {
    DismissTransition mPendingDismiss = null;
    TransitSession mPendingEnter = null;
    TransitSession mPendingRecent = null;
    TransitSession mPendingResize = null;

    private IBinder mAnimatingTransition = null;
    OneShotRemoteHandler mPendingRemoteHandler = null;
@@ -177,6 +179,43 @@ class SplitScreenTransitions {
        onFinish(null /* wct */, null /* wctCB */);
    }

    void applyResizeTransition(@NonNull IBinder transition, @NonNull TransitionInfo info,
            @NonNull SurfaceControl.Transaction startTransaction,
            @NonNull SurfaceControl.Transaction finishTransaction,
            @NonNull Transitions.TransitionFinishCallback finishCallback,
            @NonNull WindowContainerToken mainRoot, @NonNull WindowContainerToken sideRoot,
            @NonNull SplitDecorManager mainDecor, @NonNull SplitDecorManager sideDecor) {
        mFinishCallback = finishCallback;
        mAnimatingTransition = transition;
        mFinishTransaction = finishTransaction;

        for (int i = info.getChanges().size() - 1; i >= 0; --i) {
            final TransitionInfo.Change change = info.getChanges().get(i);
            if (mainRoot.equals(change.getContainer()) || sideRoot.equals(change.getContainer())) {
                final SurfaceControl leash = change.getLeash();
                startTransaction.setPosition(leash, change.getEndAbsBounds().left,
                        change.getEndAbsBounds().top);
                startTransaction.setWindowCrop(leash, change.getEndAbsBounds().width(),
                        change.getEndAbsBounds().height());

                SplitDecorManager decor = mainRoot.equals(change.getContainer())
                        ? mainDecor : sideDecor;
                ValueAnimator va = new ValueAnimator();
                mAnimations.add(va);
                decor.setScreenshotIfNeeded(change.getSnapshot(), startTransaction);
                decor.onResized(startTransaction, () -> {
                    mTransitions.getMainExecutor().execute(() -> {
                        mAnimations.remove(va);
                        onFinish(null /* wct */, null /* wctCB */);
                    });
                });
            }
        }

        startTransaction.apply();
        onFinish(null /* wct */, null /* wctCB */);
    }

    boolean isPendingTransition(IBinder transition) {
        return getPendingTransition(transition) != null;
    }
@@ -193,6 +232,10 @@ class SplitScreenTransitions {
        return mPendingDismiss != null && mPendingDismiss.mTransition == transition;
    }

    boolean isPendingResize(IBinder transition) {
        return mPendingResize != null && mPendingResize.mTransition == transition;
    }

    @Nullable
    private TransitSession getPendingTransition(IBinder transition) {
        if (isPendingEnter(transition)) {
@@ -201,11 +244,14 @@ class SplitScreenTransitions {
            return mPendingRecent;
        } else if (isPendingDismiss(transition)) {
            return mPendingDismiss;
        } else if (isPendingResize(transition)) {
            return mPendingResize;
        }

        return null;
    }


    /** Starts a transition to enter split with a remote transition animator. */
    IBinder startEnterTransition(
            @WindowManager.TransitionType int transitType,
@@ -258,6 +304,21 @@ class SplitScreenTransitions {
                exitReasonToString(reason), stageTypeToString(dismissTop));
    }

    IBinder startResizeTransition(WindowContainerTransaction wct,
            Transitions.TransitionHandler handler,
            @Nullable TransitionFinishedCallback finishCallback) {
        IBinder transition = mTransitions.startTransition(TRANSIT_CHANGE, wct, handler);
        setResizeTransition(transition, finishCallback);
        return transition;
    }

    void setResizeTransition(@NonNull IBinder transition,
            @Nullable TransitionFinishedCallback finishCallback) {
        mPendingResize = new TransitSession(transition, null /* consumedCb */, finishCallback);
        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "  splitTransition "
                + " deduced Resize split screen");
    }

    void setRecentTransition(@NonNull IBinder transition,
            @Nullable RemoteTransition remoteTransition,
            @Nullable TransitionFinishedCallback finishCallback) {
@@ -324,6 +385,9 @@ class SplitScreenTransitions {
            mPendingRecent.onConsumed(aborted);
            mPendingRecent = null;
            mPendingRemoteHandler = null;
        } else if (isPendingResize(transition)) {
            mPendingResize.onConsumed(aborted);
            mPendingResize = null;
        }
    }

@@ -340,6 +404,9 @@ class SplitScreenTransitions {
        } else if (isPendingDismiss(mAnimatingTransition)) {
            mPendingDismiss.onFinished(wct, mFinishTransaction);
            mPendingDismiss = null;
        } else if (isPendingResize(mAnimatingTransition)) {
            mPendingResize.onFinished(wct, mFinishTransaction);
            mPendingResize = null;
        }

        mPendingRemoteHandler = null;
+26 −6
Original line number Diff line number Diff line
@@ -1670,15 +1670,29 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
    public void onLayoutSizeChanged(SplitLayout layout) {
        // Reset this flag every time onLayoutSizeChanged.
        mShowDecorImmediately = false;

        if (!ENABLE_SHELL_TRANSITIONS) {
            // Only need screenshot for legacy case because shell transition should screenshot
            // itself during transition.
            final SurfaceControl.Transaction startT = mTransactionPool.acquire();
            mMainStage.screenshotIfNeeded(startT);
            mSideStage.screenshotIfNeeded(startT);
            mTransactionPool.release(startT);
        }

        final WindowContainerTransaction wct = new WindowContainerTransaction();
        updateWindowBounds(layout, wct);
        sendOnBoundsChanged();
        if (ENABLE_SHELL_TRANSITIONS) {
            mSplitTransitions.startResizeTransition(wct, this, null /* callback */);
        } else {
            mSyncQueue.queue(wct);
            mSyncQueue.runInSync(t -> {
                updateSurfaceBounds(layout, t, false /* applyResizingOffset */);
                mMainStage.onResized(t);
                mSideStage.onResized(t);
            });
        }
        mLogger.logResize(mSplitLayout.getDividerPositionAsFraction());
    }

@@ -2032,6 +2046,12 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
        } else if (mSplitTransitions.isPendingDismiss(transition)) {
            shouldAnimate = startPendingDismissAnimation(
                    mSplitTransitions.mPendingDismiss, info, startTransaction, finishTransaction);
        } else if (mSplitTransitions.isPendingResize(transition)) {
            mSplitTransitions.applyResizeTransition(transition, info, startTransaction,
                    finishTransaction, finishCallback, mMainStage.mRootTaskInfo.token,
                    mSideStage.mRootTaskInfo.token, mMainStage.getSplitDecorManager(),
                    mSideStage.getSplitDecorManager());
            return true;
        }
        if (!shouldAnimate) return false;

+11 −1
Original line number Diff line number Diff line
@@ -292,7 +292,13 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {

    void onResized(SurfaceControl.Transaction t) {
        if (mSplitDecorManager != null) {
            mSplitDecorManager.onResized(t);
            mSplitDecorManager.onResized(t, null);
        }
    }

    void screenshotIfNeeded(SurfaceControl.Transaction t) {
        if (mSplitDecorManager != null) {
            mSplitDecorManager.screenshotIfNeeded(t);
        }
    }

@@ -304,6 +310,10 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
        }
    }

    SplitDecorManager getSplitDecorManager() {
        return mSplitDecorManager;
    }

    void addTask(ActivityManager.RunningTaskInfo task, WindowContainerTransaction wct) {
        // Clear overridden bounds and windowing mode to make sure the child task can inherit
        // windowing mode and bounds from split root.
+1 −1
Original line number Diff line number Diff line
@@ -2177,7 +2177,7 @@ class Task extends TaskFragment {
    }

    private boolean shouldStartChangeTransition(int prevWinMode, @NonNull Rect prevBounds) {
        if (!isLeafTask() || !canStartChangeTransition()) {
        if (!(isLeafTask() || mCreatedByOrganizer) || !canStartChangeTransition()) {
            return false;
        }
        final int newWinMode = getWindowingMode();