Merge "Attach nav bar to app during transition for non-remote animation case" into sc-dev
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index d5a7619..c1b287f 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 @@
 
         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 37e15c7..30151c3 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 @@
      * 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 @@
                 break;
             case TYPE_NAVIGATION_BAR:
                 mNavigationBar = win;
-                updateNavBarFadeController();
                 mDisplayContent.setInsetProvider(ITYPE_NAVIGATION_BAR, win,
                         (displayFrames, windowState, inOutFrame) -> {
 
@@ -1234,7 +1232,6 @@
             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 @@
                 res.getBoolean(R.bool.config_attachNavBarToAppDuringTransition);
         if (mShouldAttachNavBarToAppDuringTransition != shouldAttach) {
             mShouldAttachNavBarToAppDuringTransition = shouldAttach;
-            updateNavBarFadeController();
         }
     }
 
@@ -3062,19 +3058,4 @@
     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 17d20ae..2f3ad40 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 @@
  */
 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 @@
             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 @@
                 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 @@
         };
     }
 
-    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 30861eb..e50dc51 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 @@
 
 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 @@
     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 @@
         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 53e3987..b44a980 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 @@
             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 @@
         }
     }
 
-    private void restoreNavigationBarFromApp(boolean animate) {
+    @VisibleForTesting
+    void restoreNavigationBarFromApp(boolean animate) {
         if (!mNavigationBarAttachedToApp) {
             return;
         }
@@ -671,23 +673,9 @@
         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 23d57b8..3082a5c 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 @@
         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 @@
         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
@@ -571,39 +564,6 @@
     }
 
     @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();
         final ActivityRecord primary = createActivityRecordWithParentTask(mDefaultDisplay,
@@ -619,20 +579,18 @@
         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 @@
         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,