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

Commit 35385b8c authored by Riddle Hsu's avatar Riddle Hsu
Browse files

Synchronize rotating non-app windows with fade animation

By capturing the sync draw transaction, the window can keep the
appearance in old rotating when running fade out animation. That
avoids flickering if the window is drawn earlier with new rotation.

Also centralize notifyStartingWindowDrawn to reduce duplicated code
and increase consistency of measurement.

Bug: 212570760
Test: atest TransitionTests#testAppTransitionWithRotationChange

Change-Id: I521b9c4278dd57bb6f277c13e6e7189a0c030e79
parent d29ddfd1
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -6337,7 +6337,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
                }
            } else if (w.isDrawn()) {
                // The starting window for this container is drawn.
                mTaskSupervisor.getActivityMetricsLogger().notifyStartingWindowDrawn(this);
                startingDisplayed = true;
            }
        }
+53 −9
Original line number Diff line number Diff line
@@ -59,8 +59,13 @@ public class FadeRotationAnimationController extends FadeAnimationController {
    /** The list to store the drawn tokens before the rotation animation starts. */
    private ArrayList<WindowToken> mPendingShowTokens;

    /** It is used when the display has rotated, but some windows fade out in old rotation. */
    private SeamlessRotator mRotator;
    /**
     * The sync transactions of the target windows. It is used when the display has rotated but
     * the windows need to fade out in previous rotation. These transactions will be applied with
     * fade-in animation, so there won't be a flickering such as the windows have redrawn during
     * fading out.
     */
    private ArrayMap<WindowState, SurfaceControl.Transaction> mCapturedDrawTransactions;

    private final int mOriginalRotation;
    private final boolean mHasScreenRotationAnimation;
@@ -110,16 +115,36 @@ public class FadeRotationAnimationController extends FadeAnimationController {
                mTargetWindowTokens.put(w.mToken, null);
            }
        }, true /* traverseTopToBottom */);

        // The transition sync group may be finished earlier because it doesn't wait for these
        // target windows. But the windows still need to use sync transaction to keep the appearance
        // in previous rotation, so request a no-op sync to keep the state.
        if (!mIsChangeTransition && transitionType != WindowManager.TRANSIT_NONE) {
            for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
                final WindowToken token = mTargetWindowTokens.keyAt(i);
                for (int j = token.getChildCount() - 1; j >= 0; j--) {
                    token.getChildAt(j).applyWithNextDraw(t -> {});
                }
            }
        }
    }

    @Override
    public void fadeWindowToken(boolean show, WindowToken windowToken, int animationType) {
        if (show) {
            final SurfaceControl leash = mTargetWindowTokens.remove(windowToken);
            if (leash != null && mRotator != null) {
                // The leash was unrotated by start transaction of transition. Clear the transform
                // to reshow the window in current rotation.
                mRotator.setIdentityMatrix(mDisplayContent.getPendingTransaction(), leash);
            // The previous animation leash will be dropped when preparing fade-in animation, so
            // simply remove it without restoring the transformation.
            mTargetWindowTokens.remove(windowToken);
            if (mCapturedDrawTransactions != null) {
                // Unblock the window to draw its latest content with fade-in animation.
                final SurfaceControl.Transaction t = mDisplayContent.getPendingTransaction();
                for (int i = windowToken.getChildCount() - 1; i >= 0; i--) {
                    final SurfaceControl.Transaction drawT =
                            mCapturedDrawTransactions.remove(windowToken.getChildAt(i));
                    if (drawT != null) {
                        t.merge(drawT);
                    }
                }
            }
        }
        super.fadeWindowToken(show, windowToken, animationType);
@@ -225,14 +250,14 @@ public class FadeRotationAnimationController extends FadeAnimationController {
            // Take OPEN/CLOSE transition type as the example, the non-activity windows need to
            // fade out in previous rotation while display has rotated to the new rotation, so
            // their leashes are unrotated with the start transaction.
            mRotator = new SeamlessRotator(mOriginalRotation,
            final SeamlessRotator rotator = new SeamlessRotator(mOriginalRotation,
                    mDisplayContent.getWindowConfiguration().getRotation(),
                    mDisplayContent.getDisplayInfo(),
                    false /* applyFixedTransformationHint */);
            for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
                final SurfaceControl leash = mTargetWindowTokens.valueAt(i);
                if (leash != null) {
                    mRotator.applyTransform(t, leash);
                    rotator.applyTransform(t, leash);
                }
            }
            return;
@@ -280,6 +305,25 @@ public class FadeRotationAnimationController extends FadeAnimationController {
        }
    }

    /** Captures the post draw transaction if the window should update with fade-in animation. */
    boolean handleFinishDrawing(WindowState w, SurfaceControl.Transaction postDrawTransaction) {
        if (mIsChangeTransition || !isTargetToken(w.mToken)) return false;
        if (postDrawTransaction != null && w.mTransitionController.inTransition()) {
            if (mCapturedDrawTransactions == null) {
                mCapturedDrawTransactions = new ArrayMap<>();
            }
            final SurfaceControl.Transaction t = mCapturedDrawTransactions.get(w);
            if (t == null) {
                mCapturedDrawTransactions.put(w, postDrawTransaction);
            } else {
                t.merge(postDrawTransaction);
            }
            return true;
        }
        mDisplayContent.finishFadeRotationAnimation(w.mToken);
        return false;
    }

    @Override
    public Animation getFadeInAnimation() {
        if (mHasScreenRotationAnimation) {
+5 −1
Original line number Diff line number Diff line
@@ -3354,8 +3354,12 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
        for (int i = mChildren.size() - 1; i >= 0; --i) {
            mChildren.get(i).finishSync(outMergedTransaction, cancel);
        }
        mSyncState = SYNC_STATE_NONE;
        if (cancel && mSyncGroup != null) mSyncGroup.onCancelSync(this);
        clearSyncState();
    }

    void clearSyncState() {
        mSyncState = SYNC_STATE_NONE;
        mSyncGroup = null;
    }

+22 −15
Original line number Diff line number Diff line
@@ -5747,29 +5747,36 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
            Slog.i(TAG, "finishDrawing of relaunch: " + this + " " + duration + "ms");
            mActivityRecord.mRelaunchStartTime = 0;
        }

        executeDrawHandlers(postDrawTransaction);

        final boolean applyPostDrawNow = mClientWasDrawingForSync && postDrawTransaction != null;
        mClientWasDrawingForSync = false;
        if (!onSyncFinishedDrawing()) {
            return mWinAnimator.finishDrawingLocked(postDrawTransaction, applyPostDrawNow);
        }

        if (mActivityRecord != null
                && mTransitionController.isShellTransitionsEnabled()
                && mAttrs.type == TYPE_APPLICATION_STARTING) {
        if (mActivityRecord != null && mAttrs.type == TYPE_APPLICATION_STARTING) {
            mWmService.mAtmService.mTaskSupervisor.getActivityMetricsLogger()
                    .notifyStartingWindowDrawn(mActivityRecord);
        }

        if (postDrawTransaction != null) {
        final boolean hasSyncHandlers = executeDrawHandlers(postDrawTransaction);

        boolean skipLayout = false;
        // Control the timing to switch the appearance of window with different rotations.
        final FadeRotationAnimationController fadeRotationController =
                mDisplayContent.getFadeRotationAnimationController();
        if (fadeRotationController != null
                && fadeRotationController.handleFinishDrawing(this, postDrawTransaction)) {
            // Consume the transaction because the controller will apply it with fade animation.
            // Layout is not needed because the window will be hidden by the fade leash. Clear
            // sync state because its sync transaction doesn't need to be merged to sync group.
            postDrawTransaction = null;
            skipLayout = true;
            clearSyncState();
        } else if (onSyncFinishedDrawing() && postDrawTransaction != null) {
            mSyncTransaction.merge(postDrawTransaction);
            // Consume the transaction because the sync group will merge it.
            postDrawTransaction = null;
        }

        mWinAnimator.finishDrawingLocked(null, false /* forceApplyNow */);
        final boolean layoutNeeded =
                mWinAnimator.finishDrawingLocked(postDrawTransaction, mClientWasDrawingForSync);
        mClientWasDrawingForSync = false;
        // We always want to force a traversal after a finish draw for blast sync.
        return true;
        return !skipLayout && (hasSyncHandlers || layoutNeeded);
    }

    void immediatelyNotifyBlastSync() {
+8 −1
Original line number Diff line number Diff line
@@ -556,8 +556,15 @@ public class TransitionTests extends WindowTestsBase {

        // The redrawn window will be faded in when the transition finishes. And because this test
        // only use one non-activity window, the fade rotation controller should also be cleared.
        statusBar.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN;
        statusBar.mWinAnimator.mDrawState = WindowStateAnimator.DRAW_PENDING;
        final SurfaceControl.Transaction postDrawTransaction =
                mock(SurfaceControl.Transaction.class);
        final boolean layoutNeeded = statusBar.finishDrawing(postDrawTransaction);
        assertFalse(layoutNeeded);
        player.finish();
        // The controller should capture the draw transaction and merge it when preparing to run
        // fade-in animation.
        verify(mDisplayContent.getPendingTransaction()).merge(eq(postDrawTransaction));
        assertNull(mDisplayContent.getFadeRotationAnimationController());
    }