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

Commit 014b730b authored by Riddle Hsu's avatar Riddle Hsu
Browse files

Polish navbar while quick switching apps with different orientation

It was no problem in legacy transition because if a window is not
handled by AsyncRotationController (e.g. SecondaryHomeHandle is
shown in the middle of rotation change), WM core can still wait for
it synchronously with the rotation change.

But with shell transition, the transaction of display projection is
applied in shell side (also the global blast sync is not enabled),
so the async window is unable to draw in new rotation with the
rotation transition at the same time. This change mitigates the case
by deferring showing it until the rotation transition is finished.

Also fix another flickering case of navbar when it is attached to app.
By using both AsyncRotationController's ACTION_SEAMLESS with
NavBarFadeAnimationController rather than only fade-animation, the
bar won't disappear a few frames by drawing with new rotation
before the start transaction is applied, i.e. ACTION_SEAMLESS will
unrotate the surface until the transition starts.

Fix: 292192092
Test: atest TransitionTests#testAppTransitionWithRotationChange
Test: Use quickstep gesture to switch from a portrait app to a
       - Non immersive landscape app.
         - The navbar is not flickering at bottom of screen.
       - Immersive landscape app.
         - The secondary-navbar is not flickering at middle of screen.

Change-Id: I25441ed4a89c8fce605afd3093b9106335fbb1cd
parent 558f38ac
Loading
Loading
Loading
Loading
+39 −11
Original line number Diff line number Diff line
@@ -172,10 +172,9 @@ class AsyncRotationController extends FadeAnimationController implements Consume
                if (recents != null && recents.isNavigationBarAttachedToApp()) {
                    return;
                }
            } else if (navigationBarCanMove || mTransitionOp == OP_CHANGE_MAY_SEAMLESS) {
            } else if (navigationBarCanMove || mTransitionOp == OP_CHANGE_MAY_SEAMLESS
                    || mDisplayContent.mTransitionController.mNavigationBarAttachedToApp) {
                action = Operation.ACTION_SEAMLESS;
            } else if (mDisplayContent.mTransitionController.mNavigationBarAttachedToApp) {
                return;
            }
            mTargetWindowTokens.put(w.mToken, new Operation(action));
            return;
@@ -294,6 +293,11 @@ class AsyncRotationController extends FadeAnimationController implements Consume
            finishOp(mTargetWindowTokens.keyAt(i));
        }
        mTargetWindowTokens.clear();
        onAllCompleted();
    }

    private void onAllCompleted() {
        if (DEBUG) Slog.d(TAG, "onAllCompleted");
        if (mTimeoutRunnable != null) {
            mService.mH.removeCallbacks(mTimeoutRunnable);
        }
@@ -333,7 +337,7 @@ class AsyncRotationController extends FadeAnimationController implements Consume
            if (DEBUG) Slog.d(TAG, "Complete directly " + token.getTopChild());
            finishOp(token);
            if (mTargetWindowTokens.isEmpty()) {
                if (mTimeoutRunnable != null) mService.mH.removeCallbacks(mTimeoutRunnable);
                onAllCompleted();
                return true;
            }
        }
@@ -411,14 +415,18 @@ class AsyncRotationController extends FadeAnimationController implements Consume
        if (mDisplayContent.mInputMethodWindow == null) return;
        final WindowToken imeWindowToken = mDisplayContent.mInputMethodWindow.mToken;
        if (isTargetToken(imeWindowToken)) return;
        hideImmediately(imeWindowToken, Operation.ACTION_TOGGLE_IME);
        if (DEBUG) Slog.d(TAG, "hideImeImmediately " + imeWindowToken.getTopChild());
    }

    private void hideImmediately(WindowToken token, @Operation.Action int action) {
        final boolean original = mHideImmediately;
        mHideImmediately = true;
        final Operation op = new Operation(Operation.ACTION_TOGGLE_IME);
        mTargetWindowTokens.put(imeWindowToken, op);
        fadeWindowToken(false /* show */, imeWindowToken, ANIMATION_TYPE_TOKEN_TRANSFORM);
        op.mLeash = imeWindowToken.getAnimationLeash();
        final Operation op = new Operation(action);
        mTargetWindowTokens.put(token, op);
        fadeWindowToken(false /* show */, token, ANIMATION_TYPE_TOKEN_TRANSFORM);
        op.mLeash = token.getAnimationLeash();
        mHideImmediately = original;
        if (DEBUG) Slog.d(TAG, "hideImeImmediately " + imeWindowToken.getTopChild());
    }

    /** Returns {@code true} if the window will rotate independently. */
@@ -428,11 +436,20 @@ class AsyncRotationController extends FadeAnimationController implements Consume
                || isTargetToken(w.mToken);
    }

    /** Returns {@code true} if the controller will run fade animations on the window. */
    /**
     * Returns {@code true} if the rotation transition appearance of the window is currently
     * managed by this controller.
     */
    boolean isTargetToken(WindowToken token) {
        return mTargetWindowTokens.containsKey(token);
    }

    /** Returns {@code true} if the controller will run fade animations on the window. */
    boolean hasFadeOperation(WindowToken token) {
        final Operation op = mTargetWindowTokens.get(token);
        return op != null && op.mAction == Operation.ACTION_FADE;
    }

    /**
     * Whether the insets animation leash should use previous position when running fade animation
     * or seamless transformation in a rotated display.
@@ -564,7 +581,18 @@ class AsyncRotationController extends FadeAnimationController implements Consume
            return false;
        }
        final Operation op = mTargetWindowTokens.get(w.mToken);
        if (op == null) return false;
        if (op == null) {
            // If a window becomes visible after the rotation transition is requested but before
            // the transition is ready, hide it by an animation leash so it won't be flickering
            // by drawing the rotated content before applying projection transaction of display.
            // And it will fade in after the display transition is finished.
            if (mTransitionOp == OP_APP_SWITCH && !mIsStartTransactionCommitted
                    && canBeAsync(w.mToken)) {
                hideImmediately(w.mToken, Operation.ACTION_FADE);
                if (DEBUG) Slog.d(TAG, "Hide on finishDrawing " + w.mToken.getTopChild());
            }
            return false;
        }
        if (DEBUG) Slog.d(TAG, "handleFinishDrawing " + w);
        if (postDrawTransaction == null || !mIsSyncDrawRequested
                || canDrawBeforeStartTransaction(op)) {
+1 −1
Original line number Diff line number Diff line
@@ -86,7 +86,7 @@ public class NavBarFadeAnimationController extends FadeAnimationController{
                ANIMATION_TYPE_TOKEN_TRANSFORM);
        if (controller == null) {
            fadeAnim.run();
        } else if (!controller.isTargetToken(mNavigationBar.mToken)) {
        } else if (!controller.hasFadeOperation(mNavigationBar.mToken)) {
            // If fade rotation animation is running and the nav bar is not controlled by it:
            // - For fade-in animation, defer the animation until fade rotation animation finishes.
            // - For fade-out animation, just play the animation.
+0 −5
Original line number Diff line number Diff line
@@ -1930,11 +1930,6 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
            break;
        }

        final AsyncRotationController asyncRotationController = dc.getAsyncRotationController();
        if (asyncRotationController != null) {
            asyncRotationController.accept(navWindow);
        }

        if (animate) {
            final NavBarFadeAnimationController controller =
                    new NavBarFadeAnimationController(dc);
+9 −0
Original line number Diff line number Diff line
@@ -1186,6 +1186,7 @@ public class TransitionTests extends WindowTestsBase {
        final WindowState statusBar = createWindow(null, TYPE_STATUS_BAR, "statusBar");
        makeWindowVisible(statusBar);
        mDisplayContent.getDisplayPolicy().addWindowLw(statusBar, statusBar.mAttrs);
        final WindowState navBar = createWindow(null, TYPE_NAVIGATION_BAR, "navBar");
        final ActivityRecord app = createActivityRecord(mDisplayContent);
        final Transition transition = app.mTransitionController.createTransition(TRANSIT_OPEN);
        app.mTransitionController.requestStartTransition(transition, app.getTask(),
@@ -1215,9 +1216,17 @@ public class TransitionTests extends WindowTestsBase {
        mDisplayContent.mTransitionController.dispatchLegacyAppTransitionFinished(app);
        assertTrue(mDisplayContent.hasTopFixedRotationLaunchingApp());

        // The bar was invisible so it is not handled by the controller. But if it becomes visible
        // and drawn before the transition starts,
        assertFalse(asyncRotationController.isTargetToken(navBar.mToken));
        navBar.finishDrawing(null /* postDrawTransaction */, Integer.MAX_VALUE);
        assertTrue(asyncRotationController.isTargetToken(navBar.mToken));

        player.startTransition();
        // Non-app windows should not be collected.
        assertFalse(mDisplayContent.mTransitionController.isCollecting(statusBar.mToken));
        // Avoid DeviceStateController disturbing the test by triggering another rotation change.
        doReturn(false).when(mDisplayContent).updateRotationUnchecked();

        onRotationTransactionReady(player, mWm.mTransactionFactory.get()).onTransactionCommitted();
        assertEquals(ROTATION_ANIMATION_SEAMLESS, player.mLastReady.getChange(