Fade in/out taskbar when launching apps from or back to AllApps/-1

- Added isHotseatIconTopWhenAligned to control both iconAlignment and stash animation to just fade in if hotseat icon isn't on top of the screen in the aligned state

Fix: 257355864
Fix: 213455090
Test: Launch apps from/back to home, taskbar animate from/to hotseat
Test: Launch apps from/back to AllApps/-1, taskbar fade in/out
Test: Repeat aboth with transient or persistent taskbar
Change-Id: I6bdae615ff9e199d23cbfe2d26c8d46a08fbc436
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 59dbd4b..2aa0af4 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -482,6 +482,10 @@
             final View appsView = mLauncher.getAppsView();
             final float startAlpha = appsView.getAlpha();
             final float startScale = SCALE_PROPERTY.get(appsView);
+            if (mDeviceProfile.isTablet) {
+                // AllApps should not fade at all in tablets.
+                alphas = new float[]{1, 1};
+            }
             appsView.setAlpha(alphas[0]);
 
             ObjectAnimator alpha = ObjectAnimator.ofFloat(appsView, View.ALPHA, alphas);
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 8775359..02206ce 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -356,6 +356,12 @@
     }
 
     @Override
+    public boolean isHotseatIconOnTopWhenAligned() {
+        return mTaskbarLauncherStateController.isInHotseatOnTopStates()
+                && getInAppDisplayProgress(MINUS_ONE_PAGE_PROGRESS_INDEX) == 0;
+    }
+
+    @Override
     public void dumpLogs(String prefix, PrintWriter pw) {
         super.dumpLogs(prefix, pw);
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index d790b4b..b74dd21 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -385,6 +385,13 @@
         }
     }
 
+    /**
+     * Returns if the current Launcher state has hotseat on top of other elemnets.
+     */
+    public boolean isInHotseatOnTopStates() {
+        return mLauncherState != LauncherState.ALL_APPS;
+    }
+
     private void playStateTransitionAnim(AnimatorSet animatorSet, long duration,
             boolean committed) {
         boolean isInStashedState = mLauncherState.isTaskbarStashed(mLauncher);
@@ -438,14 +445,16 @@
 
     private void updateIconAlphaForHome(float alpha) {
         mIconAlphaForHome.setValue(alpha);
-
+        boolean hotseatVisible = alpha == 0
+                || (!mControllers.uiController.isHotseatIconOnTopWhenAligned()
+                && mIconAlignment.value > 0);
         /*
          * Hide Launcher Hotseat icons when Taskbar icons have opacity. Both icon sets
          * should not be visible at the same time.
          */
-        mLauncher.getHotseat().setIconsAlpha(alpha > 0 ? 0 : 1);
+        mLauncher.getHotseat().setIconsAlpha(hotseatVisible ? 1 : 0);
         mLauncher.getHotseat().setQsbAlpha(
-                mLauncher.getDeviceProfile().isQsbInline && alpha > 0 ? 0 : 1);
+                mLauncher.getDeviceProfile().isQsbInline && !hotseatVisible ? 0 : 1);
     }
 
     private final class TaskBarRecentsAnimationListener implements
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index e62e533..a7e45d1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -17,6 +17,8 @@
 
 import static android.view.HapticFeedbackConstants.LONG_PRESS;
 
+import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
+import static com.android.launcher3.anim.Interpolators.INSTANT;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_LONGPRESS_HIDE;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_LONGPRESS_SHOW;
 import static com.android.launcher3.taskbar.Utilities.appendFlag;
@@ -535,6 +537,8 @@
         final float firstHalfDurationScale;
         final float secondHalfDurationScale;
 
+        boolean isHotseatIconOnTopWhenAligned =
+                mControllers.uiController.isHotseatIconOnTopWhenAligned();
         if (isStashed) {
             firstHalfDurationScale = 0.75f;
             secondHalfDurationScale = 0.5f;
@@ -555,6 +559,12 @@
             secondHalfAnimatorSet.playTogether(
                     mTaskbarStashedHandleAlpha.animateToValue(1)
             );
+
+            // If Hotseat is not the top element, an already stashed Taskbar should fade in.
+            if (!isHotseatIconOnTopWhenAligned) {
+                fullLengthAnimatorSet.setInterpolator(INSTANT);
+                firstHalfAnimatorSet.setInterpolator(INSTANT);
+            }
         } else  {
             firstHalfDurationScale = 0.5f;
             secondHalfDurationScale = 0.75f;
@@ -575,6 +585,13 @@
             secondHalfAnimatorSet.playTogether(
                     mIconAlphaForStash.animateToValue(1)
             );
+
+            // If Hotseat is not the top element, the stashed Taskbar should fade out without
+            // unstashing.
+            if (!isHotseatIconOnTopWhenAligned) {
+                fullLengthAnimatorSet.setInterpolator(FINAL_FRAME);
+                secondHalfAnimatorSet.setInterpolator(FINAL_FRAME);
+            }
         }
 
         fullLengthAnimatorSet.play(mControllers.stashedHandleViewController
@@ -916,6 +933,7 @@
         private final IntPredicate mStashCondition;
 
         private boolean mIsStashed;
+        private boolean mIsHotseatIconOnTopWhenAligned;
         private int mPrevFlags;
 
         StatePropertyHolder(IntPredicate stashCondition) {
@@ -945,7 +963,13 @@
                 mPrevFlags = flags;
             }
             boolean isStashed = mStashCondition.test(flags);
-            if (mIsStashed != isStashed) {
+            boolean isHotseatIconOnTopWhenAligned =
+                    mControllers.uiController.isHotseatIconOnTopWhenAligned();
+            // If an animation has started and mIsHotseatIconOnTopWhenAligned is changed, we need
+            // to restart the animation with new parameters.
+            if (mIsStashed != isStashed
+                    || (mIsHotseatIconOnTopWhenAligned != isHotseatIconOnTopWhenAligned
+                    && mAnimator != null && mAnimator.isStarted())) {
                 if (TestProtocol.sDebugTracing) {
                     Log.d(TestProtocol.TASKBAR_IN_APP_STATE, String.format(
                             "setState: mIsStashed=%b, isStashed=%b, duration=%d, start=:%b",
@@ -955,6 +979,7 @@
                             start));
                 }
                 mIsStashed = isStashed;
+                mIsHotseatIconOnTopWhenAligned = isHotseatIconOnTopWhenAligned;
 
                 // This sets mAnimator.
                 createAnimToIsStashed(mIsStashed, duration, startDelay, /* animateBg= */ true);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index 6c6b002..a059295 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -128,6 +128,13 @@
         return false;
     }
 
+    /**
+     * Returns true if hotseat icons are on top of view hierarchy when aligned in the current state.
+     */
+    public boolean isHotseatIconOnTopWhenAligned() {
+        return true;
+    }
+
     @CallSuper
     protected void dumpLogs(String prefix, PrintWriter pw) {
         pw.println(String.format(
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index b5e6fac..d14eeab 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -19,6 +19,7 @@
 import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
 import static com.android.launcher3.Utilities.squaredHypot;
+import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_ALLAPPS_BUTTON_TAP;
 import static com.android.launcher3.taskbar.TaskbarManager.isPhoneMode;
@@ -32,6 +33,7 @@
 import android.util.Log;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.animation.Interpolator;
 
 import androidx.annotation.Nullable;
 import androidx.core.graphics.ColorUtils;
@@ -112,6 +114,7 @@
     private Runnable mOnControllerPreCreateCallback = NO_OP;
 
     private int mThemeIconsColor;
+    private boolean mIsHotseatIconOnTopWhenAligned;
 
     private final DeviceProfile.OnDeviceProfileChangeListener mDeviceProfileChangeListener =
             dp -> commitRunningAppsToUI();
@@ -293,7 +296,12 @@
      *                       1 => fully aligned
      */
     public void setLauncherIconAlignment(float alignmentRatio, DeviceProfile launcherDp) {
-        if (mIconAlignControllerLazy == null) {
+        boolean isHotseatIconOnTopWhenAligned =
+                mControllers.uiController.isHotseatIconOnTopWhenAligned();
+        // When mIsHotseatIconOnTopWhenAligned changes, animation needs to be re-created.
+        if (mIconAlignControllerLazy == null
+                || mIsHotseatIconOnTopWhenAligned != isHotseatIconOnTopWhenAligned) {
+            mIsHotseatIconOnTopWhenAligned = isHotseatIconOnTopWhenAligned;
             mIconAlignControllerLazy = createIconAlignmentController(launcherDp);
         }
         mIconAlignControllerLazy.setPlayFraction(alignmentRatio);
@@ -318,10 +326,15 @@
                 borderSpacing,
                 launcherDp.numShownHotseatIcons);
 
+        boolean isToHome = mControllers.uiController.isIconAlignedWithHotseat();
+        // If Hotseat is not the top element, Taskbar should maintain in-app state as it fades out,
+        // or fade in while already in in-app state.
+        Interpolator interpolator = mIsHotseatIconOnTopWhenAligned ? LINEAR : FINAL_FRAME;
+
         int offsetY = launcherDp.getTaskbarOffsetY();
-        setter.setFloat(mTaskbarIconTranslationYForHome, VALUE, -offsetY, LINEAR);
-        setter.setFloat(mTaskbarNavButtonTranslationY, VALUE, -offsetY, LINEAR);
-        setter.setFloat(mTaskbarNavButtonTranslationYForInAppDisplay, VALUE, offsetY, LINEAR);
+        setter.setFloat(mTaskbarIconTranslationYForHome, VALUE, -offsetY, interpolator);
+        setter.setFloat(mTaskbarNavButtonTranslationY, VALUE, -offsetY, interpolator);
+        setter.setFloat(mTaskbarNavButtonTranslationYForInAppDisplay, VALUE, offsetY, interpolator);
 
         if (Utilities.isDarkTheme(mTaskbarView.getContext())) {
             setter.addFloat(mThemeIconsBackground, VALUE, 0f, 1f, LINEAR);
@@ -332,27 +345,24 @@
         setter.addOnFrameListener(anim -> mActivity.setTaskbarWindowHeight(
                 anim.getAnimatedFraction() > 0 ? expandedHeight : collapsedHeight));
 
-        boolean isToHome = mControllers.uiController.isIconAlignedWithHotseat();
         for (int i = 0; i < mTaskbarView.getChildCount(); i++) {
             View child = mTaskbarView.getChildAt(i);
             int positionInHotseat;
-            if (FeatureFlags.ENABLE_ALL_APPS_IN_TASKBAR.get()
-                    && child == mTaskbarView.getAllAppsButtonView()) {
-                // Note that there is no All Apps button in the hotseat, this position is only used
-                // as its convenient for animation purposes.
-                positionInHotseat = Utilities.isRtl(child.getResources())
-                        ? -1
-                        : taskbarDp.numShownHotseatIcons;
+            boolean isAllAppsButton = FeatureFlags.ENABLE_ALL_APPS_IN_TASKBAR.get()
+                    && child == mTaskbarView.getAllAppsButtonView();
+            if (!mIsHotseatIconOnTopWhenAligned) {
+                // When going to home, the EMPHASIZED interpolator in TaskbarLauncherStateController
+                // plays iconAlignment to 1 really fast, therefore moving the fading towards the end
+                // to avoid icons disappearing rather than fading out visually.
+                setter.setViewAlpha(child, 0, Interpolators.clampToProgress(LINEAR, 0.8f, 1f));
+            } else if ((isAllAppsButton && !FeatureFlags.ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT.get())) {
+                setter.setViewAlpha(child, 0,
+                        isToHome
+                                ? Interpolators.clampToProgress(LINEAR, 0f, 0.17f)
+                                : Interpolators.clampToProgress(LINEAR, 0.72f, 0.84f));
+            }
 
-                if (!FeatureFlags.ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT.get()) {
-                    setter.setViewAlpha(child, 0,
-                            isToHome
-                                    ? Interpolators.clampToProgress(LINEAR, 0f, 0.17f)
-                                    : Interpolators.clampToProgress(LINEAR, 0.72f, 0.84f));
-                }
-            } else if (child.getTag() instanceof ItemInfo) {
-                positionInHotseat = ((ItemInfo) child.getTag()).screenId;
-            } else if (child == mTaskbarView.getQsb()) {
+            if (child == mTaskbarView.getQsb()) {
                 boolean isRtl = Utilities.isRtl(child.getResources());
                 float hotseatIconCenter = isRtl
                         ? launcherDp.widthPx - hotseatPadding.right + borderSpacing
@@ -363,26 +373,38 @@
                         (launcherDp.hotseatQsbWidth - taskbarDp.iconSizePx) / 2f;
                 setter.addFloat(child, ICON_TRANSLATE_X,
                         isRtl ? -halfQsbIconWidthDiff : halfQsbIconWidthDiff,
-                        hotseatIconCenter - childCenter, LINEAR);
+                        hotseatIconCenter - childCenter, interpolator);
 
                 float scale = ((float) taskbarDp.iconSizePx) / launcherDp.hotseatQsbVisualHeight;
-                setter.addFloat(child, SCALE_PROPERTY, scale, 1f, LINEAR);
+                setter.addFloat(child, SCALE_PROPERTY, scale, 1f, interpolator);
 
-                setter.setFloat(child, VIEW_TRANSLATE_Y, mTaskbarBottomMargin, LINEAR);
+                setter.setFloat(child, VIEW_TRANSLATE_Y, mTaskbarBottomMargin, interpolator);
 
-                setter.addFloat(child, VIEW_ALPHA, 0f, 1f,
-                        isToHome
-                                ? Interpolators.clampToProgress(LINEAR, 0f, 0.35f)
-                                : Interpolators.clampToProgress(LINEAR, 0.84f, 1f));
+                if (mIsHotseatIconOnTopWhenAligned) {
+                    setter.addFloat(child, VIEW_ALPHA, 0f, 1f,
+                            isToHome
+                                    ? Interpolators.clampToProgress(LINEAR, 0f, 0.35f)
+                                    : Interpolators.clampToProgress(LINEAR, 0.84f, 1f));
+                }
                 setter.addOnFrameListener(animator -> AlphaUpdateListener.updateVisibility(child));
 
                 float qsbInsetFraction = halfQsbIconWidthDiff / launcherDp.hotseatQsbWidth;
-                if (child instanceof  HorizontalInsettableView) {
+                if (child instanceof HorizontalInsettableView) {
                     setter.addFloat((HorizontalInsettableView) child,
                             HorizontalInsettableView.HORIZONTAL_INSETS, qsbInsetFraction, 0,
-                            LINEAR);
+                            interpolator);
                 }
                 continue;
+            }
+
+            if (isAllAppsButton) {
+                // Note that there is no All Apps button in the hotseat, this position is only used
+                // as its convenient for animation purposes.
+                positionInHotseat = Utilities.isRtl(child.getResources())
+                        ? -1
+                        : taskbarDp.numShownHotseatIcons;
+            } else if (child.getTag() instanceof ItemInfo) {
+                positionInHotseat = ((ItemInfo) child.getTag()).screenId;
             } else {
                 Log.w(TAG, "Unsupported view found in createIconAlignmentController, v=" + child);
                 continue;
@@ -393,11 +415,11 @@
                     + hotseatCellSize / 2f;
 
             float childCenter = (child.getLeft() + child.getRight()) / 2f;
-            setter.setFloat(child, ICON_TRANSLATE_X, hotseatIconCenter - childCenter, LINEAR);
+            setter.setFloat(child, ICON_TRANSLATE_X, hotseatIconCenter - childCenter, interpolator);
 
-            setter.setFloat(child, VIEW_TRANSLATE_Y, mTaskbarBottomMargin, LINEAR);
+            setter.setFloat(child, VIEW_TRANSLATE_Y, mTaskbarBottomMargin, interpolator);
 
-            setter.setFloat(child, SCALE_PROPERTY, scaleUp, LINEAR);
+            setter.setFloat(child, SCALE_PROPERTY, scaleUp, interpolator);
         }
 
         AnimatorPlaybackController controller = setter.createPlaybackController();