diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java index d5a76199f7b81..c1b287ff8077a 100644 --- a/services/core/java/com/android/server/wm/AppTransition.java +++ b/services/core/java/com/android/server/wm/AppTransition.java @@ -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; } diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 37e15c71c9249..30151c3cb9c28 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -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; - } - } } diff --git a/services/core/java/com/android/server/wm/FadeAnimationController.java b/services/core/java/com/android/server/wm/FadeAnimationController.java index 17d20ae4f92bb..2f3ad4080ec93 100644 --- a/services/core/java/com/android/server/wm/FadeAnimationController.java +++ b/services/core/java/com/android/server/wm/FadeAnimationController.java @@ -37,7 +37,7 @@ import java.io.PrintWriter; */ public class FadeAnimationController { protected final Context mContext; - private final ArrayMap mDeferredFinishCallbacks = new ArrayMap<>(); + protected final ArrayMap 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, diff --git a/services/core/java/com/android/server/wm/NavBarFadeAnimationController.java b/services/core/java/com/android/server/wm/NavBarFadeAnimationController.java index 30861eb3a32db..e50dc51c402d0 100644 --- a/services/core/java/com/android/server/wm/NavBarFadeAnimationController.java +++ b/services/core/java/com/android/server/wm/NavBarFadeAnimationController.java @@ -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); + } + } } } diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index 53e39872af507..b44a9808023d4 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -628,6 +628,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) { @@ -648,7 +649,8 @@ public class RecentsAnimationController implements DeathRecipient { } } - private void restoreNavigationBarFromApp(boolean animate) { + @VisibleForTesting + void restoreNavigationBarFromApp(boolean animate) { if (!mNavigationBarAttachedToApp) { return; } @@ -671,23 +673,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()); diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java index 23d57b89cf1ca..3082a5cc61147 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java @@ -516,20 +516,15 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { final SurfaceControl.Transaction transaction = navToken.getPendingTransaction(); verify(mController.mStatusBar).setNavigationBarLumaSamplingEnabled( - mDefaultDisplay.mDisplayId, false); + eq(mDefaultDisplay.mDisplayId), eq(false)); verify(transaction).reparent(navToken.getSurfaceControl(), activity.getSurfaceControl()); verify(transaction).setLayer(navToken.getSurfaceControl(), Integer.MAX_VALUE); - final WindowContainer parent = navToken.getParent(); - final NavBarFadeAnimationController navBarFadeAnimationController = - mDefaultDisplay.getDisplayPolicy().getNavBarFadeAnimationController(); - mController.cleanupAnimation(REORDER_MOVE_TO_TOP); + verify(mController).restoreNavigationBarFromApp(eq(true)); verify(mController.mStatusBar).setNavigationBarLumaSamplingEnabled( - mDefaultDisplay.mDisplayId, true); + eq(mDefaultDisplay.mDisplayId), eq(true)); verify(transaction).setLayer(navToken.getSurfaceControl(), 0); - verify(transaction).reparent(navToken.getSurfaceControl(), parent.getSurfaceControl()); - verify(navBarFadeAnimationController).fadeWindowToken(true); } @Test @@ -543,20 +538,18 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { final SurfaceControl.Transaction transaction = navToken.getPendingTransaction(); verify(mController.mStatusBar).setNavigationBarLumaSamplingEnabled( - mDefaultDisplay.mDisplayId, false); + eq(mDefaultDisplay.mDisplayId), eq(false)); verify(transaction).reparent(navToken.getSurfaceControl(), activity.getSurfaceControl()); verify(transaction).setLayer(navToken.getSurfaceControl(), Integer.MAX_VALUE); final WindowContainer parent = navToken.getParent(); - final NavBarFadeAnimationController navBarFadeAnimationController = - mDefaultDisplay.getDisplayPolicy().getNavBarFadeAnimationController(); mController.cleanupAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION); + verify(mController).restoreNavigationBarFromApp(eq(false)); verify(mController.mStatusBar).setNavigationBarLumaSamplingEnabled( - mDefaultDisplay.mDisplayId, true); + eq(mDefaultDisplay.mDisplayId), eq(true)); verify(transaction).setLayer(navToken.getSurfaceControl(), 0); verify(transaction).reparent(navToken.getSurfaceControl(), parent.getSurfaceControl()); - verify(navBarFadeAnimationController, never()).fadeWindowToken(anyBoolean()); } @Test @@ -570,39 +563,6 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { assertFalse(mController.isNavigationBarAttachedToApp()); } - @Test - public void testFadeRotationAfterAttachAndBeforeRestore_notRestoreNavImmediately() { - setupForShouldAttachNavBarDuringTransition(); - final ActivityRecord activity = createActivityRecord(mDefaultDisplay); - final ActivityRecord homeActivity = createHomeActivity(); - initializeRecentsAnimationController(mController, homeActivity); - - final WindowToken navToken = mDefaultDisplay.getDisplayPolicy().getNavigationBar().mToken; - final SurfaceControl.Transaction transaction = navToken.getPendingTransaction(); - - verify(mController.mStatusBar).setNavigationBarLumaSamplingEnabled( - mDefaultDisplay.mDisplayId, false); - verify(transaction).reparent(navToken.getSurfaceControl(), activity.getSurfaceControl()); - verify(transaction).setLayer(navToken.getSurfaceControl(), Integer.MAX_VALUE); - - final WindowContainer parent = navToken.getParent(); - final NavBarFadeAnimationController navBarFadeAnimationController = - mDefaultDisplay.getDisplayPolicy().getNavBarFadeAnimationController(); - - FadeRotationAnimationController mockController = - mock(FadeRotationAnimationController.class); - doReturn(mockController).when(mDefaultDisplay).getFadeRotationAnimationController(); - - mController.cleanupAnimation(REORDER_MOVE_TO_TOP); - verify(mController.mStatusBar).setNavigationBarLumaSamplingEnabled( - mDefaultDisplay.mDisplayId, true); - verify(transaction).setLayer(navToken.getSurfaceControl(), 0); - verify(mockController).setOnShowRunnable(any()); - verify(transaction, times(0)).reparent(navToken.getSurfaceControl(), - parent.getSurfaceControl()); - verify(navBarFadeAnimationController, times(0)).fadeWindowToken(true); - } - @Test public void testAttachNavBarInSplitScreenMode() { setupForShouldAttachNavBarDuringTransition(); @@ -619,20 +579,18 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { final SurfaceControl.Transaction transaction = navToken.getPendingTransaction(); verify(mController.mStatusBar).setNavigationBarLumaSamplingEnabled( - mDefaultDisplay.mDisplayId, false); + eq(mDefaultDisplay.mDisplayId), eq(false)); verify(navWindow).setSurfaceTranslationY(-secondary.getBounds().top); verify(transaction).reparent(navToken.getSurfaceControl(), secondary.getSurfaceControl()); reset(navWindow); mController.cleanupAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION); final WindowContainer parent = navToken.getParent(); - final NavBarFadeAnimationController navBarFadeAnimationController = - mDefaultDisplay.getDisplayPolicy().getNavBarFadeAnimationController(); verify(mController.mStatusBar).setNavigationBarLumaSamplingEnabled( - mDefaultDisplay.mDisplayId, true); + eq(mDefaultDisplay.mDisplayId), eq(true)); verify(navWindow).setSurfaceTranslationY(0); verify(transaction).reparent(navToken.getSurfaceControl(), parent.getSurfaceControl()); - verify(navBarFadeAnimationController, never()).fadeWindowToken(anyBoolean()); + verify(mController).restoreNavigationBarFromApp(eq(false)); } @Test @@ -696,12 +654,8 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { mDefaultDisplay.getDisplayPolicy().addWindowLw(navBar, navBar.mAttrs); mWm.setRecentsAnimationController(mController); doReturn(navBar).when(mController).getNavigationBarWindow(); - final NavBarFadeAnimationController mockNavBarFadeAnimationController = - mock(NavBarFadeAnimationController.class); final DisplayPolicy displayPolicy = spy(mDefaultDisplay.getDisplayPolicy()); doReturn(displayPolicy).when(mDefaultDisplay).getDisplayPolicy(); - doReturn(mockNavBarFadeAnimationController).when(displayPolicy) - .getNavBarFadeAnimationController(); } private static void initializeRecentsAnimationController(RecentsAnimationController controller,