Play workspace reveal animation for predictive back-to-home

Bug: 382453424
Test: Manual, i.e. tested back-to-home and back-to-allapps animation on phones, tablets and foldables, including interruptions.
Flag: com.android.launcher3.predictive_back_to_home_polish
Change-Id: I092a74ab2340828e18067ca15c7019c44d30f40b
diff --git a/aconfig/launcher.aconfig b/aconfig/launcher.aconfig
index bc49146..f0a9cba 100644
--- a/aconfig/launcher.aconfig
+++ b/aconfig/launcher.aconfig
@@ -543,4 +543,14 @@
   namespace: "launcher"
   description: "Enable launcher icon shape customizations"
   bug: "348708061"
-}
\ No newline at end of file
+}
+
+flag {
+  name: "predictive_back_to_home_polish"
+  namespace: "launcher"
+  description: "Enables workspace reveal animation for predictive back-to-home"
+  bug: "382453424"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 2759816..f38693d 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -1670,7 +1670,10 @@
                 || mLauncher.getWorkspace().isOverlayShown()
                 || shouldPlayFallbackClosingAnimation(appTargets);
 
-        boolean playWorkspaceReveal = !fromPredictiveBack;
+        boolean playWorkspaceReveal = true;
+        if (!Flags.predictiveBackToHomePolish()) {
+            playWorkspaceReveal = !fromPredictiveBack;
+        }
         boolean skipAllAppsScale = false;
         if (!playFallBackAnimation) {
             PointF velocity;
@@ -1689,12 +1692,12 @@
                 // Skip scaling all apps, otherwise FloatingIconView will get wrong
                 // layout bounds.
                 skipAllAppsScale = true;
-            } else if (!fromPredictiveBack) {
+            } else if (Flags.predictiveBackToHomePolish() || !fromPredictiveBack) {
                 if (enableScalingRevealHomeAnimation()) {
                     anim.play(
-                            new ScalingWorkspaceRevealAnim(
-                                    mLauncher, rectFSpringAnim,
-                                    rectFSpringAnim.getTargetRect()).getAnimators());
+                            new ScalingWorkspaceRevealAnim(mLauncher, rectFSpringAnim,
+                                    rectFSpringAnim.getTargetRect(),
+                                    !fromPredictiveBack /* playAlphaReveal */).getAnimators());
                 } else {
                     anim.play(new StaggeredWorkspaceAnim(mLauncher, velocity.y,
                             true /* animateOverviewScrim */, launcherView).getAnimators());
@@ -1713,15 +1716,7 @@
             anim.play(getFallbackClosingWindowAnimators(appTargets));
         }
 
-        // Normally, we run the launcher content animation when we are transitioning
-        // home, but if home is already visible, then we don't want to animate the
-        // contents of launcher unless we know that we are animating home as a result
-        // of the home button press with quickstep, which will result in launcher being
-        // started on touch down, prior to the animation home (and won't be in the
-        // targets list because it is already visible). In that case, we force
-        // invisibility on touch down, and only reset it after the animation to home
-        // is initialized.
-        if (launcherIsForceInvisibleOrOpening || fromPredictiveBack) {
+        if (Flags.predictiveBackToHomePolish()) {
             AnimatorListenerAdapter endListener = new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(Animator animation) {
@@ -1730,7 +1725,24 @@
                             mLauncher, WALLPAPER_OPEN_ANIMATION_FINISHED_MESSAGE);
                 }
             };
+            if (rectFSpringAnim != null) {
+                rectFSpringAnim.addAnimatorListener(endListener);
+            } else {
+                anim.addListener(endListener);
+            }
+        }
 
+        // Normally, we run the launcher content animation when we are transitioning
+        // home, but if home is already visible, then we don't want to animate the
+        // contents of launcher unless we know that we are animating home as a result
+        // of the home button press with quickstep, which will result in launcher being
+        // started on touch down, prior to the animation home (and won't be in the
+        // targets list because it is already visible). In that case, we force
+        // invisibility on touch down, and only reset it after the animation to home
+        // is initialized.
+        boolean legacyFromPredictiveBack =
+                !Flags.predictiveBackToHomePolish() && fromPredictiveBack;
+        if (launcherIsForceInvisibleOrOpening || legacyFromPredictiveBack) {
             if (rectFSpringAnim != null && anim.getChildAnimations().isEmpty()) {
                 addCujInstrumentation(rectFSpringAnim, Cuj.CUJ_LAUNCHER_APP_CLOSE_TO_HOME);
             } else {
@@ -1738,17 +1750,26 @@
                         ? Cuj.CUJ_LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK
                         : Cuj.CUJ_LAUNCHER_APP_CLOSE_TO_HOME);
             }
-
-            if (fromPredictiveBack && rectFSpringAnim != null) {
-                rectFSpringAnim.addAnimatorListener(endListener);
-            } else {
-                anim.addListener(endListener);
+            if (!Flags.predictiveBackToHomePolish()) {
+                AnimatorListenerAdapter endListener = new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        super.onAnimationEnd(animation);
+                        AccessibilityManagerCompat.sendTestProtocolEventToTest(
+                                mLauncher, WALLPAPER_OPEN_ANIMATION_FINISHED_MESSAGE);
+                    }
+                };
+                if (fromPredictiveBack && rectFSpringAnim != null) {
+                    rectFSpringAnim.addAnimatorListener(endListener);
+                } else {
+                    anim.addListener(endListener);
+                }
             }
 
             // Only register the content animation for cancellation when state changes
             mLauncher.getStateManager().setCurrentAnimation(anim);
 
-            if (mLauncher.isInState(LauncherState.ALL_APPS) && !fromPredictiveBack) {
+            if (mLauncher.isInState(LauncherState.ALL_APPS) && !legacyFromPredictiveBack) {
                 Pair<AnimatorSet, Runnable> contentAnimator =
                         getLauncherContentAnimator(false, LAUNCHER_RESUME_START_DELAY,
                                 skipAllAppsScale);
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 7d75286..ea42d77 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -217,8 +217,10 @@
 
     private int getTaskbarAnimationDuration(boolean isVisible) {
         // fast animation duration since we will not be playing workspace reveal animation.
-        boolean shouldOverrideToFastAnimation =
-                !isHotseatIconOnTopWhenAligned() || mLauncher.getPredictiveBackToHomeInProgress();
+        boolean shouldOverrideToFastAnimation = !isHotseatIconOnTopWhenAligned();
+        if (!Flags.predictiveBackToHomePolish()) {
+            shouldOverrideToFastAnimation |= mLauncher.getPredictiveBackToHomeInProgress();
+        }
         boolean isPinnedTaskbar = DisplayController.isPinnedTaskbar(mLauncher);
         if (isVisible || isPinnedTaskbar) {
             return getTaskbarToHomeDuration(shouldOverrideToFastAnimation, isPinnedTaskbar);
diff --git a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
index 4bd9ffb..25b5531 100644
--- a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
+++ b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
@@ -51,11 +51,15 @@
 import android.window.BackProgressAnimator;
 import android.window.IBackAnimationHandoffHandler;
 import android.window.IOnBackInvokedCallback;
+
+import com.android.app.animation.Animations;
 import com.android.app.animation.Interpolators;
 import com.android.internal.policy.SystemBarUtils;
 import com.android.internal.view.AppearanceRegion;
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.Flags;
+import com.android.launcher3.LauncherState;
 import com.android.launcher3.QuickstepTransitionManager;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
@@ -65,6 +69,7 @@
 import com.android.launcher3.util.NavigationMode;
 import com.android.launcher3.widget.LauncherAppWidgetHostView;
 import com.android.quickstep.util.BackAnimState;
+import com.android.quickstep.util.ScalingWorkspaceRevealAnim;
 import com.android.systemui.shared.system.QuickStepContract;
 
 import java.lang.ref.WeakReference;
@@ -87,7 +92,8 @@
  */
 public class LauncherBackAnimationController {
     private static final int SCRIM_FADE_DURATION = 233;
-    private static final float MIN_WINDOW_SCALE = 0.85f;
+    private static final float MIN_WINDOW_SCALE =
+            Flags.predictiveBackToHomePolish() ? 0.75f : 0.85f;
     private static final float MAX_SCRIM_ALPHA_DARK = 0.8f;
     private static final float MAX_SCRIM_ALPHA_LIGHT = 0.2f;
 
@@ -314,6 +320,12 @@
                 new RemoteAnimationTarget[]{ mBackTarget });
         setLauncherTargetViewVisible(false);
         mCurrentRect.set(mStartRect);
+        if (Flags.predictiveBackToHomePolish() && !mLauncher.getWorkspace().isOverlayShown()
+                && !mLauncher.isInState(LauncherState.ALL_APPS)) {
+            Animations.cancelOngoingAnimation(mLauncher.getWorkspace());
+            Animations.cancelOngoingAnimation(mLauncher.getHotseat());
+            setLauncherScale(ScalingWorkspaceRevealAnim.MIN_SIZE);
+        }
         if (mScrimLayer == null) {
             addScrimLayer();
         }
@@ -328,6 +340,13 @@
         }
     }
 
+    private void setLauncherScale(float scale) {
+        mLauncher.getWorkspace().setScaleX(scale);
+        mLauncher.getWorkspace().setScaleY(scale);
+        mLauncher.getHotseat().setScaleX(scale);
+        mLauncher.getHotseat().setScaleY(scale);
+    }
+
     void addScrimLayer() {
         SurfaceControl parent = mLauncherTarget != null ? mLauncherTarget.leash : null;
         if (parent == null || !parent.isValid()) {
@@ -500,6 +519,10 @@
         if (mScrimLayer != null) {
             removeScrimLayer();
         }
+        if (Flags.predictiveBackToHomePolish() && !mLauncher.getWorkspace().isOverlayShown()
+                && !mLauncher.isInState(LauncherState.ALL_APPS)) {
+            setLauncherScale(ScalingWorkspaceRevealAnim.MAX_SIZE);
+        }
     }
 
     private void startTransitionAnimations(BackAnimState backAnim) {
diff --git a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
index 0ddd87b..7dd2f2e 100644
--- a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
+++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
@@ -331,7 +331,7 @@
         protected void playScalingRevealAnimation() {
             if (mContainer != null) {
                 new ScalingWorkspaceRevealAnim(mContainer, mSiblingAnimation,
-                        getWindowTargetRect()).start();
+                        getWindowTargetRect(), true /* playAlphaReveal */).start();
             }
         }
 
@@ -381,7 +381,7 @@
             if (mContainer != null) {
                 new ScalingWorkspaceRevealAnim(
                         mContainer, null /* siblingAnimation */,
-                        null /* windowTargetRect */).start();
+                        null /* windowTargetRect */, true /* playAlphaReveal */).start();
             }
         }
     }
diff --git a/quickstep/src/com/android/quickstep/util/BackAnimState.kt b/quickstep/src/com/android/quickstep/util/BackAnimState.kt
index 9009eaa..4c1e1ff 100644
--- a/quickstep/src/com/android/quickstep/util/BackAnimState.kt
+++ b/quickstep/src/com/android/quickstep/util/BackAnimState.kt
@@ -18,6 +18,7 @@
 
 import android.animation.AnimatorSet
 import android.content.Context
+import com.android.launcher3.Flags
 import com.android.launcher3.LauncherAnimationRunner.AnimationResult
 import com.android.launcher3.anim.AnimatorListeners.forEndCallback
 import com.android.launcher3.util.RunnableList
@@ -36,14 +37,20 @@
     BackAnimState {
 
     override fun addOnAnimCompleteCallback(r: Runnable) {
-        val springAnimWait = RunnableList()
-        springAnim?.addAnimatorListener(forEndCallback(springAnimWait::executeAllAndDestroy))
-            ?: springAnimWait.executeAllAndDestroy()
-
         val animWait = RunnableList()
-        anim?.addListener(
-            forEndCallback(Runnable { springAnimWait.add(animWait::executeAllAndDestroy) })
-        ) ?: springAnimWait.add(animWait::executeAllAndDestroy)
+        if (Flags.predictiveBackToHomePolish()) {
+            springAnim?.addAnimatorListener(forEndCallback(animWait::executeAllAndDestroy))
+                ?: anim?.addListener(forEndCallback(animWait::executeAllAndDestroy))
+                ?: animWait.executeAllAndDestroy()
+        } else {
+            val springAnimWait = RunnableList()
+            springAnim?.addAnimatorListener(forEndCallback(springAnimWait::executeAllAndDestroy))
+                ?: springAnimWait.executeAllAndDestroy()
+
+            anim?.addListener(
+                forEndCallback(Runnable { springAnimWait.add(animWait::executeAllAndDestroy) })
+            ) ?: springAnimWait.add(animWait::executeAllAndDestroy)
+        }
         animWait.add(r)
     }
 
diff --git a/quickstep/src/com/android/quickstep/util/ScalingWorkspaceRevealAnim.kt b/quickstep/src/com/android/quickstep/util/ScalingWorkspaceRevealAnim.kt
index f719bed..63eae92 100644
--- a/quickstep/src/com/android/quickstep/util/ScalingWorkspaceRevealAnim.kt
+++ b/quickstep/src/com/android/quickstep/util/ScalingWorkspaceRevealAnim.kt
@@ -54,14 +54,15 @@
     private val launcher: QuickstepLauncher,
     siblingAnimation: RectFSpringAnim?,
     windowTargetRect: RectF?,
+    playAlphaReveal: Boolean = true,
 ) {
     companion object {
         private const val FADE_DURATION_MS = 200L
         private const val SCALE_DURATION_MS = 1000L
         private const val MAX_ALPHA = 1f
         private const val MIN_ALPHA = 0f
-        private const val MAX_SIZE = 1f
-        private const val MIN_SIZE = 0.85f
+        internal const val MAX_SIZE = 1f
+        internal const val MIN_SIZE = 0.85f
 
         /**
          * Custom interpolator for both the home and wallpaper scaling. Necessary because EMPHASIZED
@@ -132,21 +133,23 @@
             SCALE_INTERPOLATOR,
         )
 
-        // Fade in quickly at the beginning of the animation, so the content doesn't look like it's
-        // popping into existence out of nowhere.
-        val fadeClamp = FADE_DURATION_MS.toFloat() / SCALE_DURATION_MS
-        workspace.alpha = MIN_ALPHA
-        animation.setViewAlpha(
-            workspace,
-            MAX_ALPHA,
-            Interpolators.clampToProgress(LINEAR, 0f, fadeClamp),
-        )
-        hotseat.alpha = MIN_ALPHA
-        animation.setViewAlpha(
-            hotseat,
-            MAX_ALPHA,
-            Interpolators.clampToProgress(LINEAR, 0f, fadeClamp),
-        )
+        if (playAlphaReveal) {
+            // Fade in quickly at the beginning of the animation, so the content doesn't look like
+            // it's popping into existence out of nowhere.
+            val fadeClamp = FADE_DURATION_MS.toFloat() / SCALE_DURATION_MS
+            workspace.alpha = MIN_ALPHA
+            animation.setViewAlpha(
+                workspace,
+                MAX_ALPHA,
+                Interpolators.clampToProgress(LINEAR, 0f, fadeClamp),
+            )
+            hotseat.alpha = MIN_ALPHA
+            animation.setViewAlpha(
+                hotseat,
+                MAX_ALPHA,
+                Interpolators.clampToProgress(LINEAR, 0f, fadeClamp),
+            )
+        }
 
         val transitionConfig = StateAnimationConfig()