Revert "Revert "Sync hotseat/taskbar handoff""

This reverts commit efca5c8655fdc4282f8825607f62c495cc7fe96d.

Reason for revert: ANR no longer happening

Bug: 223789074
Bug: 223443781
Bug: 202507555
Change-Id: I03bfe8642ef4ed0f9e8fbd543141d39c66375604
diff --git a/Android.bp b/Android.bp
index f5a38b4..c980a2e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -31,6 +31,7 @@
         "androidx.test.uiautomator_uiautomator",
         "androidx.preference_preference",
         "SystemUISharedLib",
+        "SystemUIAnimationLib",
     ],
     srcs: [
         "tests/tapl/**/*.java",
@@ -196,6 +197,7 @@
         "lottie",
         "SystemUISharedLib",
         "SystemUI-statsd",
+        "SystemUIAnimationLib",
     ],
     manifest: "quickstep/AndroidManifest.xml",
     min_sdk_version: "current",
@@ -287,6 +289,7 @@
         "SystemUISharedLib",
         "Launcher3CommonDepsLib",
         "QuickstepResLib",
+        "SystemUIAnimationLib",
     ],
     manifest: "quickstep/AndroidManifest.xml",
     platform_apis: true,
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index ebe6a04..235a156 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -29,13 +29,16 @@
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.LauncherState;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.statemanager.StateManager;
 import com.android.launcher3.util.MultiValueAlpha;
 import com.android.quickstep.AnimatedFloat;
 import com.android.quickstep.RecentsAnimationCallbacks;
 import com.android.quickstep.RecentsAnimationController;
 import com.android.quickstep.views.RecentsView;
+import com.android.systemui.animation.ViewRootSync;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 
 import java.util.HashMap;
@@ -76,6 +79,9 @@
 
     private boolean mShouldDelayLauncherStateAnim;
 
+    // We skip any view synchronizations during init/destroy.
+    private boolean mCanSyncViews;
+
     private final StateManager.StateListener<LauncherState> mStateListener =
             new StateManager.StateListener<LauncherState>() {
 
@@ -102,6 +108,8 @@
             };
 
     public void init(TaskbarControllers controllers, BaseQuickstepLauncher launcher) {
+        mCanSyncViews = false;
+
         mControllers = controllers;
         mLauncher = launcher;
 
@@ -121,9 +129,13 @@
         updateStateForFlag(FLAG_RESUMED, launcher.hasBeenResumed());
         mLauncherState = launcher.getStateManager().getState();
         applyState(0);
+
+        mCanSyncViews = true;
     }
 
     public void onDestroy() {
+        mCanSyncViews = false;
+
         mIconAlignmentForResumedState.finishAnimation();
         mIconAlignmentForGestureState.finishAnimation();
         mIconAlignmentForLauncherState.finishAnimation();
@@ -131,6 +143,8 @@
         mIconAlphaForHome.setConsumer(null);
         mLauncher.getHotseat().setIconsAlpha(1f);
         mLauncher.getStateManager().removeStateListener(mStateListener);
+
+        mCanSyncViews = true;
     }
 
     public Animator createAnimToLauncher(@NonNull LauncherState toState,
@@ -380,6 +394,27 @@
             return;
         }
         float alignment = alignmentSupplier.get();
+        float currentValue = mIconAlphaForHome.getValue();
+        boolean taskbarWillBeVisible = alignment < 1;
+        boolean firstFrameVisChanged = (taskbarWillBeVisible && Float.compare(currentValue, 1) != 0)
+                || (!taskbarWillBeVisible && Float.compare(currentValue, 0) != 0);
+
+        // Sync the first frame where we swap taskbar and hotseat.
+        if (firstFrameVisChanged && mCanSyncViews && !Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+            DeviceProfile dp = mLauncher.getDeviceProfile();
+
+            // Do all the heavy work before the sync.
+            mControllers.taskbarViewController.createIconAlignmentControllerIfNotExists(dp);
+
+            ViewRootSync.synchronizeNextDraw(mLauncher.getHotseat(),
+                    mControllers.taskbarActivityContext.getDragLayer(),
+                    () -> updateIconAlignment(alignment));
+        } else {
+            updateIconAlignment(alignment);
+        }
+    }
+
+    private void updateIconAlignment(float alignment) {
         mControllers.taskbarViewController.setLauncherIconAlignment(
                 alignment, mLauncher.getDeviceProfile());
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 6e34ee0..f354b2c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -192,15 +192,23 @@
     }
 
     /**
+     * Creates the icon alignment controller if it does not already exist.
+     * @param launcherDp Launcher device profile.
+     */
+    public void createIconAlignmentControllerIfNotExists(DeviceProfile launcherDp) {
+        if (mIconAlignControllerLazy == null) {
+            mIconAlignControllerLazy = createIconAlignmentController(launcherDp);
+        }
+    }
+
+    /**
      * Sets the taskbar icon alignment relative to Launcher hotseat icons
      * @param alignmentRatio [0, 1]
      *                       0 => not aligned
      *                       1 => fully aligned
      */
     public void setLauncherIconAlignment(float alignmentRatio, DeviceProfile launcherDp) {
-        if (mIconAlignControllerLazy == null) {
-            mIconAlignControllerLazy = createIconAlignmentController(launcherDp);
-        }
+        createIconAlignmentControllerIfNotExists(launcherDp);
         mIconAlignControllerLazy.setPlayFraction(alignmentRatio);
         if (alignmentRatio <= 0 || alignmentRatio >= 1) {
             // Cleanup lazy controller so that it is created again in next animation