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

Commit 9aee2dc7 authored by shawnlin's avatar shawnlin
Browse files

Attach nav bar to app during transition for non-remote animation case

- Play fade-out/in animation in AppTransition.goodToGo() if it's not a
  remote animation.
- Create a new class NavFadeAnimationAdapter:
  - Able to play fade-out and fade-in sequentially.
  - Able to reparent the animation leash to the specified parent when
    the animation starts.

- This CL also refines a case when users launch an app and then swipe up
  immediately to enter recent app by canceling animation of the nav
  token in RecentsAnimationController.attachNavigationBarToApp().
  In this case, the swipe-up gesture should have higher priority to
  control the nav bar.

Bug: 185303414
Test: - atest RecentsAnimationControllerTest
      - Launch clock widget from launcher and observe the transition
      - Launch an app and then swipe up immediately to enter recent app
Change-Id: Ic8ad3dfe4fe3efcf88cf3cfcf362d18e3c47f632
parent 68667bdd
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -449,6 +449,19 @@ public class AppTransition implements Dump {

        if (mRemoteAnimationController != null) {
            mRemoteAnimationController.goodToGo(transit);
        } else if ((isTaskOpenTransitOld(transit) || transit == TRANSIT_OLD_WALLPAPER_CLOSE)
                && topOpeningAnim != null) {
            if (mDisplayContent.getDisplayPolicy().shouldAttachNavBarToAppDuringTransition()
                    && mService.getRecentsAnimationController() == null) {
                final NavBarFadeAnimationController controller =
                        new NavBarFadeAnimationController(mDisplayContent);
                // For remote animation case, the nav bar fades out and in is controlled by the
                // remote side. For non-remote animation case, we play the fade out/in animation
                // here. We play the nav bar fade-out animation when the app transition animation
                // starts and play the fade-in animation sequentially once the fade-out is finished.
                controller.fadeOutAndInSequentially(topOpeningAnim.getDurationHint(),
                        null /* fadeOutParent */, topOpeningApp.getSurfaceControl());
            }
        }
        return redoLayout;
    }
+0 −19
Original line number Diff line number Diff line
@@ -373,7 +373,6 @@ public class DisplayPolicy {
     * when the navigation bar mode is changed.
     */
    private boolean mShouldAttachNavBarToAppDuringTransition;
    private NavBarFadeAnimationController mNavBarFadeAnimationController;

    // -------- PolicyHandler --------
    private static final int MSG_REQUEST_TRANSIENT_BARS = 2;
@@ -1088,7 +1087,6 @@ public class DisplayPolicy {
                break;
            case TYPE_NAVIGATION_BAR:
                mNavigationBar = win;
                updateNavBarFadeController();
                mDisplayContent.setInsetProvider(ITYPE_NAVIGATION_BAR, win,
                        (displayFrames, windowState, inOutFrame) -> {

@@ -1234,7 +1232,6 @@ public class DisplayPolicy {
            mDisplayContent.setInsetProvider(ITYPE_STATUS_BAR, null, null);
        } else if (mNavigationBar == win || mNavigationBarAlt == win) {
            mNavigationBar = null;
            updateNavBarFadeController();
            mNavigationBarAlt = null;
            mDisplayContent.setInsetProvider(ITYPE_NAVIGATION_BAR, null, null);
        } else if (mNotificationShade == win) {
@@ -2060,7 +2057,6 @@ public class DisplayPolicy {
                res.getBoolean(R.bool.config_attachNavBarToAppDuringTransition);
        if (mShouldAttachNavBarToAppDuringTransition != shouldAttach) {
            mShouldAttachNavBarToAppDuringTransition = shouldAttach;
            updateNavBarFadeController();
        }
    }

@@ -3062,19 +3058,4 @@ public class DisplayPolicy {
    boolean shouldAttachNavBarToAppDuringTransition() {
        return mShouldAttachNavBarToAppDuringTransition && mNavigationBar != null;
    }

    @Nullable NavBarFadeAnimationController getNavBarFadeAnimationController() {
        return mNavBarFadeAnimationController;
    }

    private void updateNavBarFadeController() {
        if (shouldAttachNavBarToAppDuringTransition()) {
            if (mNavBarFadeAnimationController == null) {
                mNavBarFadeAnimationController =
                        new NavBarFadeAnimationController(mDisplayContent);
            }
        } else {
            mNavBarFadeAnimationController = null;
        }
    }
}
+20 −12
Original line number Diff line number Diff line
@@ -37,7 +37,7 @@ import java.io.PrintWriter;
 */
public class FadeAnimationController {
    protected final Context mContext;
    private final ArrayMap<WindowToken, Runnable> mDeferredFinishCallbacks = new ArrayMap<>();
    protected final ArrayMap<WindowToken, Runnable> mDeferredFinishCallbacks = new ArrayMap<>();

    public FadeAnimationController(DisplayContent displayContent) {
        mContext = displayContent.mWmService.mContext;
@@ -69,17 +69,11 @@ public class FadeAnimationController {
            return;
        }

        final Animation animation = show ? getFadeInAnimation() : getFadeOutAnimation();
        if (animation == null) {
        final FadeAnimationAdapter animationAdapter = createAdapter(show, windowToken);
        if (animationAdapter == null) {
            return;
        }

        final LocalAnimationAdapter.AnimationSpec windowAnimationSpec =
                createAnimationSpec(animation);

        final FadeAnimationAdapter animationAdapter = new FadeAnimationAdapter(
                windowAnimationSpec, windowToken.getSurfaceAnimationRunner(), show, windowToken);

        // We deferred the end of the animation when hiding the token, so we need to end it now that
        // it's shown again.
        final SurfaceAnimator.OnAnimationFinishedCallback finishedCallback = show ? (t, r) -> {
@@ -92,7 +86,21 @@ public class FadeAnimationController {
                show /* hidden */, animationType, finishedCallback);
    }

    private LocalAnimationAdapter.AnimationSpec createAnimationSpec(@NonNull Animation animation) {
    protected FadeAnimationAdapter createAdapter(boolean show, WindowToken windowToken) {
        final Animation animation = show ? getFadeInAnimation() : getFadeOutAnimation();
        if (animation == null) {
            return null;
        }

        final LocalAnimationAdapter.AnimationSpec windowAnimationSpec =
                createAnimationSpec(animation);

        return new FadeAnimationAdapter(
                windowAnimationSpec, windowToken.getSurfaceAnimationRunner(), show, windowToken);
    }

    protected LocalAnimationAdapter.AnimationSpec createAnimationSpec(
            @NonNull Animation animation) {
        return new LocalAnimationAdapter.AnimationSpec() {

            final Transformation mTransformation = new Transformation();
@@ -130,8 +138,8 @@ public class FadeAnimationController {
        };
    }

    private class FadeAnimationAdapter extends LocalAnimationAdapter {
        private final boolean mShow;
    protected class FadeAnimationAdapter extends LocalAnimationAdapter {
        protected final boolean mShow;
        private final WindowToken mToken;

        FadeAnimationAdapter(AnimationSpec windowAnimationSpec,
+98 −1
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.server.wm;

import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;

import android.view.SurfaceControl;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.Interpolator;
@@ -35,12 +36,17 @@ public class NavBarFadeAnimationController extends FadeAnimationController{
    private static final Interpolator FADE_OUT_INTERPOLATOR =
            new PathInterpolator(0.2f, 0f, 1f, 1f);

    private DisplayContent mDisplayContent;
    private final WindowState mNavigationBar;
    private Animation mFadeInAnimation;
    private Animation mFadeOutAnimation;
    private SurfaceControl mFadeInParent;
    private SurfaceControl mFadeOutParent;
    private boolean mPlaySequentially = false;

    public NavBarFadeAnimationController(DisplayContent displayContent) {
        super(displayContent);
        mDisplayContent = displayContent;
        mNavigationBar = displayContent.getDisplayPolicy().getNavigationBar();
        mFadeInAnimation = new AlphaAnimation(0f, 1f);
        mFadeInAnimation.setDuration(FADE_IN_DURATION);
@@ -61,12 +67,103 @@ public class NavBarFadeAnimationController extends FadeAnimationController{
        return mFadeOutAnimation;
    }

    @Override
    protected FadeAnimationAdapter createAdapter(boolean show, WindowToken windowToken) {
        final Animation animation = show ? getFadeInAnimation() : getFadeOutAnimation();
        if (animation == null) {
            return null;
        }

        final LocalAnimationAdapter.AnimationSpec windowAnimationSpec =
                createAnimationSpec(animation);
        return new NavFadeAnimationAdapter(
                windowAnimationSpec, windowToken.getSurfaceAnimationRunner(), show, windowToken,
                show ? mFadeInParent : mFadeOutParent);
    }

    /**
     * Run the fade-in/out animation for the navigation bar.
     *
     * @param show true for fade-in, otherwise for fade-out.
     */
    public void fadeWindowToken(boolean show) {
        fadeWindowToken(show, mNavigationBar.mToken, ANIMATION_TYPE_APP_TRANSITION);
        final FadeRotationAnimationController controller =
                mDisplayContent.getFadeRotationAnimationController();
        final Runnable fadeAnim = () -> fadeWindowToken(show, mNavigationBar.mToken,
                ANIMATION_TYPE_APP_TRANSITION);
        if (controller == null) {
            fadeAnim.run();
        } else if (!controller.isTargetToken(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.
            if (show) {
                controller.setOnShowRunnable(fadeAnim);
            } else {
                fadeAnim.run();
            }
        } else {
            // If fade rotation animation is running and controlling the nav bar, make sure we empty
            // the mDeferredFinishCallbacks and defer the runnable until fade rotation animation
            // finishes.
            final Runnable runnable = mDeferredFinishCallbacks.remove(mNavigationBar.mToken);
            if (runnable != null) {
                controller.setOnShowRunnable(runnable);
            }
        }
    }

    void fadeOutAndInSequentially(long totalDuration, SurfaceControl fadeOutParent,
            SurfaceControl fadeInParent) {
        mPlaySequentially = true;
        if (totalDuration > 0) {
            // The animation duration of each animation varies so we set the fade-out duration to
            // 1/3 of the total app transition duration and set the fade-in duration to 2/3 of it.
            final long fadeInDuration = totalDuration * 2L / 3L;
            mFadeOutAnimation.setDuration(totalDuration - fadeInDuration);
            mFadeInAnimation.setDuration(fadeInDuration);
        }
        mFadeOutParent = fadeOutParent;
        mFadeInParent = fadeInParent;
        fadeWindowToken(false);
    }

    /**
     * The animation adapter that is capable of playing fade-out and fade-in sequentially and
     * reparenting the navigation bar to a specified SurfaceControl when fade animation starts.
     */
    protected class NavFadeAnimationAdapter extends FadeAnimationAdapter {
        private SurfaceControl mParent;

        NavFadeAnimationAdapter(AnimationSpec windowAnimationSpec,
                SurfaceAnimationRunner surfaceAnimationRunner, boolean show,
                WindowToken token, SurfaceControl parent) {
            super(windowAnimationSpec, surfaceAnimationRunner, show, token);
            mParent = parent;
        }

        @Override
        public void startAnimation(SurfaceControl animationLeash, SurfaceControl.Transaction t,
                int type, SurfaceAnimator.OnAnimationFinishedCallback finishCallback) {
            super.startAnimation(animationLeash, t, type, finishCallback);
            if (mParent != null && mParent.isValid()) {
                t.reparent(animationLeash, mParent);
                // Place the nav bar on top of anything else (e.g. ime and starting window) in the
                // parent.
                t.setLayer(animationLeash, Integer.MAX_VALUE);
            }
        }

        @Override
        public boolean shouldDeferAnimationFinish(Runnable endDeferFinishCallback) {
            if (mPlaySequentially) {
                if (!mShow) {
                    fadeWindowToken(true);
                }
                return false;
            } else {
                return super.shouldDeferAnimationFinish(endDeferFinishCallback);
            }
        }
    }
}
+6 −18
Original line number Diff line number Diff line
@@ -633,6 +633,7 @@ public class RecentsAnimationController implements DeathRecipient {
            return;
        }
        mNavigationBarAttachedToApp = true;
        navWindow.mToken.cancelAnimation();
        final SurfaceControl.Transaction t = navWindow.mToken.getPendingTransaction();
        final SurfaceControl navSurfaceControl = navWindow.mToken.getSurfaceControl();
        if (shouldTranslateNavBar) {
@@ -653,7 +654,8 @@ public class RecentsAnimationController implements DeathRecipient {
        }
    }

    private void restoreNavigationBarFromApp(boolean animate) {
    @VisibleForTesting
    void restoreNavigationBarFromApp(boolean animate) {
        if (!mNavigationBarAttachedToApp) {
            return;
        }
@@ -676,23 +678,9 @@ public class RecentsAnimationController implements DeathRecipient {
        t.setLayer(navToken.getSurfaceControl(), navToken.getLastLayer());

        if (animate) {
            final NavBarFadeAnimationController navBarFadeAnimationController =
                    mDisplayContent.getDisplayPolicy().getNavBarFadeAnimationController();
            final Runnable fadeInAnim = () -> {
                // Reparent the SurfaceControl of nav bar token back.
                t.reparent(navToken.getSurfaceControl(), parent.getSurfaceControl());
                // Run fade-in animation to show navigation bar back to bottom of the display.
                if (navBarFadeAnimationController != null) {
                    navBarFadeAnimationController.fadeWindowToken(true);
                }
            };
            final FadeRotationAnimationController fadeRotationAnimationController =
                    mDisplayContent.getFadeRotationAnimationController();
            if (fadeRotationAnimationController != null) {
                fadeRotationAnimationController.setOnShowRunnable(fadeInAnim);
            } else {
                fadeInAnim.run();
            }
            final NavBarFadeAnimationController controller =
                        new NavBarFadeAnimationController(mDisplayContent);
            controller.fadeWindowToken(true);
        } else {
            // Reparent the SurfaceControl of nav bar token back.
            t.reparent(navToken.getSurfaceControl(), parent.getSurfaceControl());
Loading