Handle page offset for grid overview

- Use TaskView's actual position with grid/fullscreen translation considered for taskPosition calculation
- Shift taskPosition by midpoint scroll, and no longer assumes midpoint is on middle of the screen
- Handle situation that TaskView is on left/right of midpoint, making the calculation generic to be able to handle grid situation

Bug: 175939487
Test: Launch modal view with wide/, RTL/non-RTL, orientation/simulated landscape combinations
Change-Id: Idd0cc9c5e24f453d830e1420319a38d3d784270d
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 5d3e0b8..1df3d3c 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -270,7 +270,8 @@
                 public void setValue(RecentsView view, float scale) {
                     view.setScaleX(scale);
                     view.setScaleY(scale);
-                    view.mLastComputedTaskPushOutDistance = null;
+                    view.mLastComputedTaskStartPushOutDistance = null;
+                    view.mLastComputedTaskEndPushOutDistance = null;
                     view.mLiveTileTaskViewSimulator.recentsViewScale.value = scale;
                     view.updatePageOffsets();
                     view.setTaskViewsResistanceTranslation(view.mTaskViewsSecondaryTranslation);
@@ -308,7 +309,8 @@
     protected final Rect mLastComputedGridSize = new Rect();
     protected final Rect mLastComputedGridTaskSize = new Rect();
     // How much a task that is directly offscreen will be pushed out due to RecentsView scale/pivot.
-    protected Float mLastComputedTaskPushOutDistance = null;
+    protected Float mLastComputedTaskStartPushOutDistance = null;
+    protected Float mLastComputedTaskEndPushOutDistance = null;
     protected boolean mEnableDrawingLiveTile = false;
     protected final Rect mTempRect = new Rect();
     protected final RectF mTempRectF = new RectF();
@@ -2467,7 +2469,8 @@
         setPivotX(mTempPointF.x);
         setPivotY(mTempPointF.y);
         setTaskModalness(mTaskModalness);
-        mLastComputedTaskPushOutDistance = null;
+        mLastComputedTaskStartPushOutDistance = null;
+        mLastComputedTaskEndPushOutDistance = null;
         updatePageOffsets();
         setImportantForAccessibility(isModal() ? IMPORTANT_FOR_ACCESSIBILITY_NO
                 : IMPORTANT_FOR_ACCESSIBILITY_AUTO);
@@ -2476,10 +2479,6 @@
     private void updatePageOffsets() {
         float offset = mAdjacentPageOffset;
         float modalOffset = ACCEL_0_75.getInterpolation(mTaskModalness);
-        if (mIsRtl) {
-            offset = -offset;
-            modalOffset = -modalOffset;
-        }
         int count = getChildCount();
 
         TaskView runningTask = mRunningTaskId == -1 || !mRunningTaskTileHidden
@@ -2495,13 +2494,27 @@
                 ? getOffsetSize(midpoint + 1, midpoint, offset)
                 : 0;
 
+        boolean showAsGrid = showAsGrid();
         float modalMidpointOffsetSize = 0;
-        float modalLeftOffsetSize = modalMidpoint - 1 >= 0
-                ? -getOffsetSize(modalMidpoint - 1, modalMidpoint, modalOffset)
-                : 0;
-        float modalRightOffsetSize = modalMidpoint + 1 < count
-                ? getOffsetSize(modalMidpoint + 1, modalMidpoint, modalOffset)
-                : 0;
+        float modalLeftOffsetSize = 0;
+        float modalRightOffsetSize = 0;
+        float gridOffsetSize = 0;
+
+        if (showAsGrid) {
+            // In grid, we only focus the task on the side. The reference index used for offset
+            // calculation is the task directly next to the focus task in the grid.
+            int referenceIndex = modalMidpoint == 0 ? 1 : 0;
+            gridOffsetSize = referenceIndex < count
+                    ? getOffsetSize(referenceIndex, modalMidpoint, modalOffset)
+                    : 0;
+        } else {
+            modalLeftOffsetSize = modalMidpoint - 1 >= 0
+                    ? getOffsetSize(modalMidpoint - 1, modalMidpoint, modalOffset)
+                    : 0;
+            modalRightOffsetSize = modalMidpoint + 1 < count
+                    ? getOffsetSize(modalMidpoint + 1, modalMidpoint, modalOffset)
+                    : 0;
+        }
 
         for (int i = 0; i < count; i++) {
             float translation = i == midpoint
@@ -2511,21 +2524,37 @@
                             : rightOffsetSize;
             float modalTranslation = i == modalMidpoint
                     ? modalMidpointOffsetSize
-                    : i < modalMidpoint
-                            ? modalLeftOffsetSize
-                            : modalRightOffsetSize;
+                    : showAsGrid
+                            ? gridOffsetSize
+                            : i < modalMidpoint ? modalLeftOffsetSize : modalRightOffsetSize;
             float totalTranslation = translation + modalTranslation;
             View child = getChildAt(i);
             FloatProperty translationProperty = child instanceof TaskView
                     ? ((TaskView) child).getPrimaryTaskOffsetTranslationProperty()
                     : mOrientationHandler.getPrimaryViewTranslate();
-            translationProperty.set(child,
-                    totalTranslation * mOrientationHandler.getPrimaryTranslationDirectionFactor());
+            translationProperty.set(child, totalTranslation);
         }
         updateCurveProperties();
     }
 
     /**
+     * Computes the child position with persistent translation considered (see
+     * {@link TaskView#getPersistentTranslationX()}.
+     */
+    private void getPersistentChildPosition(int childIndex, int midPointScroll, RectF outRect) {
+        View child = getChildAt(childIndex);
+        outRect.set(child.getLeft(), child.getTop(), child.getRight(), child.getBottom());
+        if (child instanceof TaskView) {
+            TaskView taskView = (TaskView) child;
+            outRect.offset(taskView.getPersistentTranslationX(),
+                    taskView.getPersistentTranslationY());
+            outRect.top += mActivity.getDeviceProfile().overviewTaskThumbnailTopMarginPx;
+        }
+        outRect.offset(mOrientationHandler.getPrimaryValue(-midPointScroll, 0),
+                mOrientationHandler.getSecondaryValue(-midPointScroll, 0));
+    }
+
+    /**
      * Computes the distance to offset the given child such that it is completely offscreen when
      * translating away from the given midpoint.
      * @param offsetProgress From 0 to 1 where 0 means no offset and 1 means offset offscreen.
@@ -2535,33 +2564,60 @@
             // Don't bother calculating everything below if we won't offset anyway.
             return 0;
         }
+
         // First, get the position of the task relative to the midpoint. If there is no midpoint
         // then we just use the normal (centered) task position.
-        mTempRectF.set(mLastComputedTaskSize);
         RectF taskPosition = mTempRectF;
-        float desiredLeft = getWidth();
-        // Used to calculate the scale of the task view based on its new offset.
+        // Whether the task should be shifted to start direction (i.e. left edge for portrait, top
+        // edge for landscape/seascape).
+        boolean isStartShift;
         if (midpointIndex > -1) {
             // When there is a midpoint reference task, adjacent tasks have less distance to travel
             // to reach offscreen. Offset the task position to the task's starting point.
-            View child = getChildAt(childIndex);
-            View midpointChild = getChildAt(midpointIndex);
-            int distanceFromMidpoint = Math.abs(mOrientationHandler.getChildStart(child)
-                    - mOrientationHandler.getChildStart(midpointChild)
-                    + getDisplacementFromScreenCenter(midpointIndex));
-            taskPosition.offset(distanceFromMidpoint, 0);
+            int midpointScroll = getScrollForPage(midpointIndex);
+            getPersistentChildPosition(midpointIndex, midpointScroll, taskPosition);
+            float midpointStart = mOrientationHandler.getStart(taskPosition);
+
+            getPersistentChildPosition(childIndex, midpointScroll, taskPosition);
+            // Assume child does not overlap with midPointChild.
+            isStartShift = mOrientationHandler.getStart(taskPosition) < midpointStart;
+        } else {
+            // Position the task at scroll position.
+            getPersistentChildPosition(childIndex, getScrollForPage(childIndex), taskPosition);
+            isStartShift = mIsRtl;
         }
-        float distanceToOffscreen = desiredLeft - taskPosition.left;
-        // Finally, we need to account for RecentsView scale, because it moves tasks based on its
-        // pivot. To do this, we move the task position to where it would be offscreen at scale = 1
-        // (computed above), then we apply the scale via getMatrix() to determine how much that
-        // moves the task from its desired position, and adjust the computed distance accordingly.
-        if (mLastComputedTaskPushOutDistance == null) {
-            taskPosition.offsetTo(desiredLeft, 0);
-            getMatrix().mapRect(taskPosition);
-            mLastComputedTaskPushOutDistance = (taskPosition.left - desiredLeft) / getScaleX();
+
+        // Next, calculate the distance to move the task off screen. We also need to account for
+        // RecentsView scale, because it moves tasks based on its pivot. To do this, we move the
+        // task position to where it would be offscreen at scale = 1 (computed above), then we
+        // apply the scale via getMatrix() to determine how much that moves the task from its
+        // desired position, and adjust the computed distance accordingly.
+        float distanceToOffscreen;
+        if (isStartShift) {
+            float desiredStart = -mOrientationHandler.getPrimarySize(taskPosition);
+            distanceToOffscreen = -mOrientationHandler.getEnd(taskPosition);
+            if (mLastComputedTaskStartPushOutDistance == null) {
+                taskPosition.offsetTo(
+                        mOrientationHandler.getPrimaryValue(desiredStart, 0f),
+                        mOrientationHandler.getSecondaryValue(desiredStart, 0f));
+                getMatrix().mapRect(taskPosition);
+                mLastComputedTaskStartPushOutDistance = mOrientationHandler.getEnd(taskPosition)
+                        / mOrientationHandler.getPrimaryScale(this);
+            }
+            distanceToOffscreen -= mLastComputedTaskStartPushOutDistance;
+        } else {
+            float desiredStart = mOrientationHandler.getPrimarySize(this);
+            distanceToOffscreen = desiredStart - mOrientationHandler.getStart(taskPosition);
+            if (mLastComputedTaskEndPushOutDistance == null) {
+                taskPosition.offsetTo(
+                        mOrientationHandler.getPrimaryValue(desiredStart, 0f),
+                        mOrientationHandler.getSecondaryValue(desiredStart, 0f));
+                getMatrix().mapRect(taskPosition);
+                mLastComputedTaskEndPushOutDistance = (mOrientationHandler.getStart(taskPosition)
+                        - desiredStart) / mOrientationHandler.getPrimaryScale(this);
+            }
+            distanceToOffscreen -= mLastComputedTaskEndPushOutDistance;
         }
-        distanceToOffscreen -= mLastComputedTaskPushOutDistance;
         return distanceToOffscreen * offsetProgress;
     }
 
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index e6143fb..8f22622 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -987,20 +987,30 @@
 
     private void applyTranslationX() {
         setTranslationX(mDismissTranslationX + mTaskOffsetTranslationX + mTaskResistanceTranslationX
-                + getFullscreenTrans(mFullscreenTranslationX)
-                + getNonFullscreenTrans(mNonFullscreenTranslationX)
-                + getGridTrans(mGridTranslationX));
+                + getPersistentTranslationX());
     }
 
     private void applyTranslationY() {
-        setTranslationY(
-                mDismissTranslationY + mTaskOffsetTranslationY + mTaskResistanceTranslationY
-                        + getGridTrans(mGridTranslationY) + mBoxTranslationY);
+        setTranslationY(mDismissTranslationY + mTaskOffsetTranslationY + mTaskResistanceTranslationY
+                + getPersistentTranslationY());
     }
 
-    private float getGridTrans(float endTranslation) {
-        float progress = ACCEL_DEACCEL.getInterpolation(mGridProgress);
-        return Utilities.mapRange(progress, 0, endTranslation);
+    /**
+     * Returns addition of translationX that is persistent (e.g. fullscreen and grid), and does not
+     * change according to a temporary state (e.g. task offset).
+     */
+    public float getPersistentTranslationX() {
+        return getFullscreenTrans(mFullscreenTranslationX)
+                + getNonFullscreenTrans(mNonFullscreenTranslationX)
+                + getGridTrans(mGridTranslationX);
+    }
+
+    /**
+     * Returns addition of translationY that is persistent (e.g. fullscreen and grid), and does not
+     * change according to a temporary state (e.g. task offset).
+     */
+    public float getPersistentTranslationY() {
+        return getGridTrans(mGridTranslationY) + mBoxTranslationY;
     }
 
     public FloatProperty<TaskView> getPrimaryDismissTranslationProperty() {
@@ -1275,6 +1285,11 @@
         return endTranslation - getFullscreenTrans(endTranslation);
     }
 
+    private float getGridTrans(float endTranslation) {
+        float progress = ACCEL_DEACCEL.getInterpolation(mGridProgress);
+        return Utilities.mapRange(progress, 0, endTranslation);
+    }
+
     public boolean isRunningTask() {
         if (getRecentsView() == null) {
             return false;
diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
index a241e63..18e27a4 100644
--- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
@@ -122,11 +122,26 @@
     }
 
     @Override
+    public int getPrimarySize(View view) {
+        return view.getHeight();
+    }
+
+    @Override
     public float getPrimarySize(RectF rect) {
         return rect.height();
     }
 
     @Override
+    public float getStart(RectF rect) {
+        return rect.top;
+    }
+
+    @Override
+    public float getEnd(RectF rect) {
+        return rect.bottom;
+    }
+
+    @Override
     public int getClearAllSidePadding(View view, boolean isRtl) {
         return (isRtl ? view.getPaddingBottom() : - view.getPaddingTop()) / 2;
     }
diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java
index b85d08a..560df86 100644
--- a/src/com/android/launcher3/touch/PagedOrientationHandler.java
+++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java
@@ -65,7 +65,10 @@
     float getPrimaryDirection(MotionEvent event, int pointerIndex);
     float getPrimaryVelocity(VelocityTracker velocityTracker, int pointerId);
     int getMeasuredSize(View view);
+    int getPrimarySize(View view);
     float getPrimarySize(RectF rect);
+    float getStart(RectF rect);
+    float getEnd(RectF rect);
     int getClearAllSidePadding(View view, boolean isRtl);
     int getSecondaryDimension(View view);
     FloatProperty<View> getPrimaryViewTranslate();
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index 2fb5952..86508c4 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -118,11 +118,26 @@
     }
 
     @Override
+    public int getPrimarySize(View view) {
+        return view.getWidth();
+    }
+
+    @Override
     public float getPrimarySize(RectF rect) {
         return rect.width();
     }
 
     @Override
+    public float getStart(RectF rect) {
+        return rect.left;
+    }
+
+    @Override
+    public float getEnd(RectF rect) {
+        return rect.right;
+    }
+
+    @Override
     public int getClearAllSidePadding(View view, boolean isRtl) {
         return (isRtl ? view.getPaddingRight() : - view.getPaddingLeft()) / 2;
     }