Move window to align with first task during quick scrub

Bug: 70180755
Change-Id: I5932462bd1af08ec141518a344c60605b8730d9d
diff --git a/quickstep/src/com/android/quickstep/QuickScrubController.java b/quickstep/src/com/android/quickstep/QuickScrubController.java
index 7f9d3a1..d1ce820 100644
--- a/quickstep/src/com/android/quickstep/QuickScrubController.java
+++ b/quickstep/src/com/android/quickstep/QuickScrubController.java
@@ -93,7 +93,7 @@
     }
 
     public void snapToPageForCurrentQuickScrubSection() {
-        goToPageWithHaptic(mRecentsView.getCurrentPage() + mQuickScrubSection);
+        goToPageWithHaptic(mRecentsView.getFirstTaskIndex() + mQuickScrubSection);
     }
 
     private void goToPageWithHaptic(int pageToGoTo) {
diff --git a/quickstep/src/com/android/quickstep/RecentsView.java b/quickstep/src/com/android/quickstep/RecentsView.java
index 6b1f3d3..343922c 100644
--- a/quickstep/src/com/android/quickstep/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/RecentsView.java
@@ -135,6 +135,7 @@
             final TaskView taskView = (TaskView) getChildAt(i);
             if (taskView.getTask().key.id == taskId) {
                 taskView.onTaskDataLoaded(taskView.getTask(), thumbnailData);
+                taskView.setAlpha(1);
                 return;
             }
         }
@@ -467,7 +468,9 @@
         mRunningTaskId = runningTaskId;
         setCurrentPage(mFirstTaskIndex);
         if (mCurrentPage >= mFirstTaskIndex) {
-            ((TaskView) getPageAt(mCurrentPage)).setIconScale(0);
+            TaskView currentTask = (TaskView) getPageAt(mCurrentPage);
+            currentTask.setIconScale(0);
+            currentTask.setAlpha(0);
         }
     }
 
diff --git a/quickstep/src/com/android/quickstep/TaskView.java b/quickstep/src/com/android/quickstep/TaskView.java
index 6a70e2d..8865a42 100644
--- a/quickstep/src/com/android/quickstep/TaskView.java
+++ b/quickstep/src/com/android/quickstep/TaskView.java
@@ -212,7 +212,6 @@
 
         float translation =
                 scrollState.distanceFromScreenCenter * curveInterpolation * CURVE_FACTOR;
-        setTranslationX(translation);
 
         if (scrollState.lastScrollType == SCROLL_TYPE_WORKSPACE) {
             // Make sure that the task cards do not overlap with the workspace card
diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
index dd0892b..7247af4 100644
--- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -124,6 +124,8 @@
     private final Rect mHomeStackBounds = new Rect();
     // The bounds of the task view in launcher window coordinates
     private final Rect mTargetRect = new Rect();
+    // Doesn't change after initialized, used as an anchor when changing mTargetRect
+    private final Rect mInitialTargetRect = new Rect();
     // The interpolated rect from the source app rect to the target rect
     private final Rect mCurrentRect = new Rect();
     // The clip rect in source app window coordinates
@@ -224,6 +226,7 @@
         RecentsView.getPageRect(dp, mContext, mTargetRect);
         mTargetRect.offset(mHomeStackBounds.left - mSourceStackBounds.left,
                 mHomeStackBounds.top - mSourceStackBounds.top);
+        mInitialTargetRect.set(mTargetRect);
 
         // Calculate the clip based on the target rect (since the content insets and the
         // launcher insets may differ, so the aspect ratio of the target rect can differ
@@ -436,7 +439,9 @@
 
         synchronized (mRecentsAnimationWrapper) {
             if (mRecentsAnimationWrapper.controller != null) {
-                mRectEvaluator.evaluate(shift, mSourceRect, mTargetRect);
+                synchronized (mTargetRect) {
+                    mRectEvaluator.evaluate(shift, mSourceRect, mTargetRect);
+                }
                 float scale = (float) mCurrentRect.width() / mSourceRect.width();
 
                 mClipRect.left = (int) (mSourceWindowClipInsets.left * shift);
@@ -460,11 +465,28 @@
         }
 
         if (mLauncherTransitionController != null) {
-            if (Looper.getMainLooper() == Looper.myLooper()) {
+            Runnable runOnUi = () -> {
                 mLauncherTransitionController.setPlayFraction(shift);
+
+                // Make sure the window follows the first task if it moves, e.g. during quick scrub.
+                int firstTaskIndex = mRecentsView.getFirstTaskIndex();
+                View firstTask = mRecentsView.getPageAt(firstTaskIndex);
+                int scrollForFirstTask = mRecentsView.getScrollForPage(firstTaskIndex);
+                int offsetFromFirstTask = (scrollForFirstTask - mRecentsView.getScrollX());
+                if (offsetFromFirstTask != 0) {
+                    synchronized (mTargetRect) {
+                        mTargetRect.set(mInitialTargetRect);
+                        Utilities.scaleRectAboutCenter(mTargetRect, firstTask.getScaleX());
+                        int offsetX = (int) (offsetFromFirstTask + firstTask.getTranslationX());
+                        mTargetRect.offset(offsetX, 0);
+                    }
+                }
+            };
+            if (Looper.getMainLooper() == Looper.myLooper()) {
+                runOnUi.run();
             } else {
                 // The fling operation completed even before the launcher was drawn
-                mMainExecutor.execute(() -> mLauncherTransitionController.setPlayFraction(shift));
+                mMainExecutor.execute(runOnUi);
             }
         }
     }