Synchronize the app launch and taskbar stash animations.

Introduce FLAG_IGNORE_IN_APP to prevent taskbar from stashing
prematurely due to HomeTransitionListener signal.

Fixes leak by adding event callback to clear runnable when launcher is
destroyed.

Bug: 319162553
Test: Launch an app, note the two animations are synced
      Verified by locally introducing a startDelay (since otherwise it is hard to tell)
Flag: com.android.launcher3.sync_app_launch_with_taskbar_stash
Change-Id: I684c714bb84fb1717b0e26620f2aa938671eb19a
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index aae8a56..3fe25b9 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -40,12 +40,14 @@
 import static com.android.app.animation.Interpolators.DECELERATE_1_7;
 import static com.android.app.animation.Interpolators.EXAGGERATED_EASE;
 import static com.android.app.animation.Interpolators.LINEAR;
+import static com.android.launcher3.BaseActivity.EVENT_DESTROYED;
 import static com.android.launcher3.BaseActivity.INVISIBLE_ALL;
 import static com.android.launcher3.BaseActivity.INVISIBLE_BY_APP_TRANSITIONS;
 import static com.android.launcher3.BaseActivity.INVISIBLE_BY_PENDING_FLAGS;
 import static com.android.launcher3.BaseActivity.PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION;
 import static com.android.launcher3.Flags.enableContainerReturnAnimations;
 import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
+import static com.android.launcher3.Flags.syncAppLaunchWithTaskbarStash;
 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
 import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.BACKGROUND_APP;
@@ -356,6 +358,20 @@
         options.setOnAnimationAbortListener(endCallback);
         options.setOnAnimationFinishedListener(endCallback);
         options.setLaunchCookie(StableViewInfo.toLaunchCookie(itemInfo));
+
+        // Prepare taskbar for animation synchronization. This needs to happen here before any
+        // app transition is created.
+        LauncherTaskbarUIController taskbarController = mLauncher.getTaskbarUIController();
+        if (syncAppLaunchWithTaskbarStash()
+                && enableScalingRevealHomeAnimation()
+                && taskbarController != null) {
+            taskbarController.setIgnoreInAppFlagForSync(true);
+            mLauncher.addEventCallback(EVENT_DESTROYED, onEndCallback::executeAllAndDestroy);
+            onEndCallback.add(() -> {
+                taskbarController.setIgnoreInAppFlagForSync(false);
+            });
+        }
+
         return new ActivityOptionsWrapper(options, onEndCallback);
     }
 
@@ -1903,6 +1919,21 @@
                 anim.addListener(mForceInvisibleListener);
             }
 
+            // Syncs the app launch animation and taskbar stash animation (if exists).
+            if (syncAppLaunchWithTaskbarStash() && enableScalingRevealHomeAnimation()) {
+                LauncherTaskbarUIController taskbarController = mLauncher.getTaskbarUIController();
+                if (taskbarController != null) {
+                    taskbarController.setIgnoreInAppFlagForSync(false);
+
+                    if (launcherClosing) {
+                        Animator taskbar = taskbarController.createAnimToApp();
+                        if (taskbar != null) {
+                            anim.play(taskbar);
+                        }
+                    }
+                }
+            }
+
             result.setAnimation(anim, mLauncher, mOnEndCallback::executeAllAndDestroy,
                     skipFirstFrame);
         }
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index e998388..6e210e3 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -17,12 +17,14 @@
 
 import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY;
 
+import static com.android.launcher3.Flags.syncAppLaunchWithTaskbarStash;
 import static com.android.launcher3.QuickstepTransitionManager.TASKBAR_TO_APP_DURATION;
 import static com.android.launcher3.QuickstepTransitionManager.TRANSIENT_TASKBAR_TRANSITION_DURATION;
 import static com.android.launcher3.QuickstepTransitionManager.getTaskbarToHomeDuration;
 import static com.android.launcher3.statemanager.BaseState.FLAG_NON_INTERACTIVE;
 import static com.android.launcher3.taskbar.TaskbarEduTooltipControllerKt.TOOLTIP_STEP_FEATURES;
 import static com.android.launcher3.taskbar.TaskbarLauncherStateController.FLAG_VISIBLE;
+import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IGNORE_IN_APP;
 
 import android.animation.Animator;
 import android.animation.AnimatorSet;
@@ -301,6 +303,29 @@
         return mTaskbarLauncherStateController.createAnimToLauncher(toState, callbacks, duration);
     }
 
+    /**
+     * Create Taskbar animation to be played alongside the Launcher app launch animation.
+     */
+    public @Nullable Animator createAnimToApp() {
+        if (!syncAppLaunchWithTaskbarStash()) {
+            return null;
+        }
+        TaskbarStashController stashController = mControllers.taskbarStashController;
+        stashController.updateStateForFlag(TaskbarStashController.FLAG_IN_APP, true);
+        return stashController.createApplyStateAnimator(stashController.getStashDuration());
+    }
+
+    /**
+     * Temporarily ignore FLAG_IN_APP for app launches to prevent premature taskbar stashing.
+     * This is needed because taskbar gets a signal to stash before we actually start the
+     * app launch animation.
+     */
+    public void setIgnoreInAppFlagForSync(boolean enabled) {
+        if (syncAppLaunchWithTaskbarStash()) {
+            mControllers.taskbarStashController.updateStateForFlag(FLAG_IGNORE_IN_APP, enabled);
+        }
+    }
+
     public void updateTaskbarLauncherStateGoingHome() {
         mTaskbarLauncherStateController.updateStateForFlag(FLAG_VISIBLE, true);
         mTaskbarLauncherStateController.applyState();
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 5284edd..ad59c2e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -23,6 +23,7 @@
 import static com.android.app.animation.Interpolators.LINEAR;
 import static com.android.internal.jank.InteractionJankMonitor.Configuration;
 import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
+import static com.android.launcher3.Flags.syncAppLaunchWithTaskbarStash;
 import static com.android.launcher3.QuickstepTransitionManager.PINNED_TASKBAR_TRANSITION_DURATION;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NAVBAR_UNIFICATION;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TRANSIENT_TASKBAR_HIDE;
@@ -107,6 +108,8 @@
     // taskbar should always be stashed for bubble bar on phone
     public static final int FLAG_STASHED_BUBBLE_BAR_ON_PHONE = 1 << 15;
 
+    public static final int FLAG_IGNORE_IN_APP = 1 << 16; // used to sync with app launch animation
+
     // If any of these flags are enabled, isInApp should return true.
     private static final int FLAGS_IN_APP = FLAG_IN_APP | FLAG_IN_SETUP;
 
@@ -1412,6 +1415,13 @@
          */
         @Nullable
         public Animator createSetStateAnimator(long flags, long duration) {
+            // We do this when we want to synchronize the app launch and taskbar stash animations.
+            if (syncAppLaunchWithTaskbarStash()
+                    && hasAnyFlag(FLAG_IGNORE_IN_APP)
+                    && hasAnyFlag(flags, FLAG_IN_APP)) {
+                flags = flags & ~FLAG_IN_APP;
+            }
+
             boolean isStashed = mStashCondition.test(flags);
 
             if (DEBUG) {