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
This commit is contained in:
shawnlin
2021-04-26 15:45:30 +08:00
parent 68667bdd3a
commit 9aee2dc7e6
6 changed files with 146 additions and 105 deletions

View File

@@ -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;
}

View File

@@ -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;
}
}
}

View File

@@ -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,

View File

@@ -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);
}
}
}
}

View File

@@ -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());

View File

@@ -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,