Making sure the reorder animation goes back to the initial position.

The animation ReorderPreviewAnimation handles two animations, one when
the mode is MODE_HINT and the other MODE_PREVIEW. The animation is
basically the same but MODE_PREVIEW ocilates to finalDelta and back
to initDelta. MODE_HINT stays in finalDelta. When finish, we have to
go back to the initial position.

This behaviour is currently done using logic, making sure the view stays
in finalDelta no matter the value of the animation, the bug is in this
logic. I think is better to do this with the current animation API by
setting setRepeatCount to 0 on the HINT case and reverting the animation
when finish so it goes back to the initial position.

This seems to have been there for a long time, at least since the
creating of git_main, but it became more aparent now. In a follow up cl
I will move this class to it's own file and into kotlin to help if there
are issues here again.

Fix: 294473336
Test: manual testing
Flag: NA
Change-Id: I4ac5de3ce8a8170944a072c6ce41c65d0963a780
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();
         }
     }