Merge "Use nearest region for all the nav buttons in 3-button folded mode" into main
diff --git a/quickstep/res/layout-sw600dp-land/gesture_tutorial_step_menu.xml b/quickstep/res/layout-sw600dp-land/gesture_tutorial_step_menu.xml
index 672440f..e4942ae 100644
--- a/quickstep/res/layout-sw600dp-land/gesture_tutorial_step_menu.xml
+++ b/quickstep/res/layout-sw600dp-land/gesture_tutorial_step_menu.xml
@@ -159,7 +159,7 @@
             style="@style/TextAppearance.GestureTutorial.ButtonLabel"
             android:id="@+id/gesture_tutorial_menu_done_button"
             android:layout_width="wrap_content"
-            android:layout_height="40dp"
+            android:layout_height="48dp"
             android:layout_marginVertical="16dp"
             android:text="@string/gesture_tutorial_action_button_label"
             android:background="@drawable/gesture_tutorial_action_button_background"
diff --git a/quickstep/res/layout/gesture_tutorial_step_menu.xml b/quickstep/res/layout/gesture_tutorial_step_menu.xml
index c8ee6e9..668a2e1 100644
--- a/quickstep/res/layout/gesture_tutorial_step_menu.xml
+++ b/quickstep/res/layout/gesture_tutorial_step_menu.xml
@@ -157,7 +157,7 @@
             style="@style/TextAppearance.GestureTutorial.ButtonLabel"
             android:id="@+id/gesture_tutorial_menu_done_button"
             android:layout_width="wrap_content"
-            android:layout_height="40dp"
+            android:layout_height="48dp"
             android:layout_marginVertical="16dp"
             android:text="@string/gesture_tutorial_action_button_label"
             android:background="@drawable/gesture_tutorial_action_button_background"
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
index 1e7b3e2..fbc7da1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
@@ -199,8 +199,10 @@
 
     @Override
     public void onRtlPropertiesChanged(int layoutDirection) {
-        // TODO(b/273310265): set this based on bubble bar position and not LTR or RTL
-        mBubbleBarBackground.setAnchorLeft(layoutDirection == LAYOUT_DIRECTION_RTL);
+        // TODO(b/313661121): set this based on bubble bar position and not LTR or RTL
+        boolean onLeft = layoutDirection == LAYOUT_DIRECTION_RTL;
+        mBubbleBarBackground.setAnchorLeft(onLeft);
+        mRelativePivotX = onLeft ? 0f : 1f;
     }
 
     private boolean isOnLeft() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java
index c998d97..f88460f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java
@@ -16,6 +16,7 @@
 package com.android.launcher3.taskbar.bubbles;
 
 import static android.view.View.INVISIBLE;
+import static android.view.View.LAYOUT_DIRECTION_RTL;
 import static android.view.View.VISIBLE;
 
 import android.animation.Animator;
@@ -124,22 +125,35 @@
     private void updateBounds() {
         // As more bubbles get added, the icon bounds become larger. To ensure a consistent
         // handle bar position, we pin it to the edge of the screen.
-        final int right =
-                mActivity.getDeviceProfile().widthPx - mBarViewController.getHorizontalMargin();
-
         final int stashedCenterY = mStashedHandleView.getHeight() - mStashedTaskbarHeight / 2;
+        if (isOnLeft()) {
+            final int left = mBarViewController.getHorizontalMargin();
+            mStashedHandleBounds.set(
+                    left,
+                    stashedCenterY - mStashedHandleHeight / 2,
+                    left + mStashedHandleWidth,
+                    stashedCenterY + mStashedHandleHeight / 2);
+            mStashedHandleView.setPivotX(0);
+        } else {
+            final int right =
+                    mActivity.getDeviceProfile().widthPx - mBarViewController.getHorizontalMargin();
+            mStashedHandleBounds.set(
+                    right - mStashedHandleWidth,
+                    stashedCenterY - mStashedHandleHeight / 2,
+                    right,
+                    stashedCenterY + mStashedHandleHeight / 2);
+            mStashedHandleView.setPivotX(mStashedHandleView.getWidth());
+        }
 
-        mStashedHandleBounds.set(
-                right - mStashedHandleWidth,
-                stashedCenterY - mStashedHandleHeight / 2,
-                right,
-                stashedCenterY + mStashedHandleHeight / 2);
         mStashedHandleView.updateSampledRegion(mStashedHandleBounds);
-
-        mStashedHandleView.setPivotX(mStashedHandleView.getWidth());
         mStashedHandleView.setPivotY(mStashedHandleView.getHeight() - mStashedTaskbarHeight / 2f);
     }
 
+    private boolean isOnLeft() {
+        // TODO(b/313661121): set this based on bubble bar position and not LTR or RTL
+        return mStashedHandleView.getLayoutDirection() == LAYOUT_DIRECTION_RTL;
+    }
+
     public void onDestroy() {
         mRegionSamplingHelper.stopAndDestroy();
         mRegionSamplingHelper = null;
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 5a51d8e..6d2fbb4 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -48,7 +48,6 @@
 import android.util.AttributeSet;
 import android.util.FloatProperty;
 import android.util.Log;
-import android.util.Property;
 import android.util.SparseArray;
 import android.view.MotionEvent;
 import android.view.View;
@@ -1446,22 +1445,10 @@
         }
     }
 
-    private static final Property<ReorderPreviewAnimation, Float> ANIMATION_PROGRESS =
-            new Property<ReorderPreviewAnimation, Float>(float.class, "animationProgress") {
-                @Override
-                public Float get(ReorderPreviewAnimation anim) {
-                    return anim.animationProgress;
-                }
-
-                @Override
-                public void set(ReorderPreviewAnimation anim, Float progress) {
-                    anim.setAnimationProgress(progress);
-                }
-            };
-
     // Class which represents the reorder preview animations. These animations show that an item is
     // in a temporary state, and hint at where the item will return to.
-    class ReorderPreviewAnimation<T extends View & Reorderable> {
+    class ReorderPreviewAnimation<T extends View & Reorderable> implements
+            ValueAnimator.AnimatorUpdateListener {
         final T child;
         float finalDeltaX;
         float finalDeltaY;
@@ -1469,8 +1456,6 @@
         float initDeltaY;
         final float finalScale;
         float initScale;
-        final int mode;
-        boolean repeating = false;
         private static final int PREVIEW_DURATION = 300;
         private static final int HINT_DURATION = Workspace.REORDER_TIMEOUT;
 
@@ -1479,8 +1464,7 @@
         public static final int MODE_HINT = 0;
         public static final int MODE_PREVIEW = 1;
 
-        float animationProgress = 0;
-        ValueAnimator a;
+        ValueAnimator mAnimator;
 
         ReorderPreviewAnimation(View childView, int mode, int cellX0, int cellY0,
                 int cellX1, int cellY1, int spanX, int spanY) {
@@ -1494,7 +1478,6 @@
             final int dY = y1 - y0;
 
             this.child = (T) childView;
-            this.mode = mode;
             finalDeltaX = 0;
             finalDeltaY = 0;
 
@@ -1504,6 +1487,20 @@
             initScale = child.getReorderBounceScale();
             finalScale = mChildScale - (CHILD_DIVIDEND / child.getWidth()) * initScale;
 
+            mAnimator = ObjectAnimator.ofFloat(0, 1);
+            mAnimator.addUpdateListener(this);
+
+            // Animations are disabled in power save mode, causing the repeated animation to jump
+            // spastically between beginning and end states. Since this looks bad, we don't repeat
+            // the animation in power save mode.
+            if (areAnimatorsEnabled() && mode == MODE_PREVIEW) {
+                mAnimator.setRepeatCount(ValueAnimator.INFINITE);
+                mAnimator.setRepeatMode(ValueAnimator.REVERSE);
+            }
+
+            mAnimator.setDuration(mode == MODE_HINT ? HINT_DURATION : PREVIEW_DURATION);
+            mAnimator.setStartDelay((int) (Math.random() * 60));
+
             int dir = mode == MODE_HINT ? -1 : 1;
             if (dX == dY && dX == 0) {
             } else {
@@ -1549,61 +1546,36 @@
                 return;
             }
 
-            ValueAnimator va = ObjectAnimator.ofFloat(this, ANIMATION_PROGRESS, 0, 1);
-            a = va;
-
-            // Animations are disabled in power save mode, causing the repeated animation to jump
-            // spastically between beginning and end states. Since this looks bad, we don't repeat
-            // the animation in power save mode.
-            if (areAnimatorsEnabled()) {
-                va.setRepeatMode(ValueAnimator.REVERSE);
-                va.setRepeatCount(ValueAnimator.INFINITE);
-            }
-
-            va.setDuration(mode == MODE_HINT ? HINT_DURATION : PREVIEW_DURATION);
-            va.setStartDelay((int) (Math.random() * 60));
-            va.addListener(new AnimatorListenerAdapter() {
-                public void onAnimationRepeat(Animator animation) {
-                    // We make sure to end only after a full period
-                    setInitialAnimationValuesToBaseline();
-                    repeating = true;
-                }
-            });
             mShakeAnimators.put(child, this);
-            va.start();
+            mAnimator.start();
         }
 
-        private void setAnimationProgress(float progress) {
-            animationProgress = progress;
-            float r1 = (mode == MODE_HINT && repeating) ? 1.0f : animationProgress;
-            float x = r1 * finalDeltaX + (1 - r1) * initDeltaX;
-            float y = r1 * finalDeltaY + (1 - r1) * initDeltaY;
-            child.getTranslateDelegate().setTranslation(INDEX_REORDER_BOUNCE_OFFSET, x, y);
-            float s = animationProgress * finalScale + (1 - animationProgress) * initScale;
-            child.setReorderBounceScale(s);
+        @Override
+        public void onAnimationUpdate(ValueAnimator updatedAnimation) {
+            float progress = (float) updatedAnimation.getAnimatedValue();
+            child.getTranslateDelegate().setTranslation(
+                    INDEX_REORDER_BOUNCE_OFFSET,
+                    /* dx = */ progress * finalDeltaX + (1 - progress) * initDeltaX,
+                    /* dy = */ progress * finalDeltaY + (1 - progress) * initDeltaY
+            );
+            child.setReorderBounceScale(progress * finalScale + (1 - progress) * initScale);
         }
 
         private void cancel() {
-            if (a != null) {
-                a.cancel();
-            }
+            mAnimator.cancel();
         }
 
         /**
          * Smoothly returns the item to its baseline position / scale
          */
         @Thunk void finishAnimation() {
-            if (a != null) {
-                a.cancel();
-            }
-
+            mAnimator.cancel();
             setInitialAnimationValuesToBaseline();
-            ValueAnimator va = ObjectAnimator.ofFloat(this, ANIMATION_PROGRESS,
-                    animationProgress, 0);
-            a = va;
-            a.setInterpolator(DECELERATE_1_5);
-            a.setDuration(REORDER_ANIMATION_DURATION);
-            a.start();
+            mAnimator = ObjectAnimator.ofFloat((Float) mAnimator.getAnimatedValue(), 0);
+            mAnimator.addUpdateListener(this);
+            mAnimator.setInterpolator(DECELERATE_1_5);
+            mAnimator.setDuration(REORDER_ANIMATION_DURATION);
+            mAnimator.start();
         }
     }