Changing LauncherViewPropertyAnimator to use ValueAnimator

Using ValueAnimator allows better support for changes in Animator
APIs.

Bug: 35218222
Change-Id: I4efa470b4bb686328539d35d8e23e297a4ac885a
diff --git a/src/com/android/launcher3/LauncherViewPropertyAnimator.java b/src/com/android/launcher3/LauncherViewPropertyAnimator.java
index 4406a2c..9bafc2a 100644
--- a/src/com/android/launcher3/LauncherViewPropertyAnimator.java
+++ b/src/com/android/launcher3/LauncherViewPropertyAnimator.java
@@ -17,259 +17,90 @@
 package com.android.launcher3;
 
 import android.animation.Animator;
-import android.animation.Animator.AnimatorListener;
-import android.animation.TimeInterpolator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.PropertyValuesHolder;
+import android.animation.ValueAnimator;
 import android.view.View;
-import android.view.ViewPropertyAnimator;
+
+import com.android.launcher3.anim.AnimationLayerSet;
 
 import java.util.ArrayList;
-import java.util.EnumSet;
 
-public class LauncherViewPropertyAnimator extends Animator implements AnimatorListener {
+/**
+ * Extension of {@link ValueAnimator} to provide an interface similar to
+ * {@link android.view.ViewPropertyAnimator}.
+ */
+public class LauncherViewPropertyAnimator extends ValueAnimator {
 
-    enum Properties {
-            TRANSLATION_X,
-            TRANSLATION_Y,
-            SCALE_X,
-            SCALE_Y,
-            ROTATION_Y,
-            ALPHA,
-            START_DELAY,
-            DURATION,
-            INTERPOLATOR,
-            WITH_LAYER
-    }
-    EnumSet<Properties> mPropertiesToSet = EnumSet.noneOf(Properties.class);
-    ViewPropertyAnimator mViewPropertyAnimator;
-    View mTarget;
+    private final View mTarget;
+    private final ArrayList<PropertyValuesHolder> mProperties;
 
-    float mTranslationX;
-    float mTranslationY;
-    float mScaleX;
-    float mScaleY;
-    float mRotationY;
-    float mAlpha;
-    long mStartDelay;
-    long mDuration;
-    TimeInterpolator mInterpolator;
-    ArrayList<Animator.AnimatorListener> mListeners = new ArrayList<>();
-    boolean mRunning = false;
-    FirstFrameAnimatorHelper mFirstFrameHelper;
+    private boolean mPrepared = false;
 
-    public LauncherViewPropertyAnimator(View target) {
-        mTarget = target;
-    }
-
-    @Override
-    public void addListener(Animator.AnimatorListener listener) {
-        mListeners.add(listener);
-    }
-
-    @Override
-    public void cancel() {
-        if (mViewPropertyAnimator != null) {
-            mViewPropertyAnimator.cancel();
-        }
-    }
-
-    @Override
-    public Animator clone() {
-        throw new RuntimeException("Not implemented");
-    }
-
-    @Override
-    public void end() {
-        throw new RuntimeException("Not implemented");
-    }
-
-    @Override
-    public long getDuration() {
-        return mDuration;
-    }
-
-    @Override
-    public ArrayList<Animator.AnimatorListener> getListeners() {
-        return mListeners;
-    }
-
-    @Override
-    public long getStartDelay() {
-        return mStartDelay;
-    }
-
-    @Override
-    public void onAnimationCancel(Animator animation) {
-        for (int i = 0; i < mListeners.size(); i++) {
-            Animator.AnimatorListener listener = mListeners.get(i);
-            listener.onAnimationCancel(this);
-        }
-        mRunning = false;
-    }
-
-    @Override
-    public void onAnimationEnd(Animator animation) {
-        for (int i = 0; i < mListeners.size(); i++) {
-            Animator.AnimatorListener listener = mListeners.get(i);
-            listener.onAnimationEnd(this);
-        }
-        mRunning = false;
-    }
-
-    @Override
-    public void onAnimationRepeat(Animator animation) {
-        for (int i = 0; i < mListeners.size(); i++) {
-            Animator.AnimatorListener listener = mListeners.get(i);
-            listener.onAnimationRepeat(this);
-        }
-    }
-
-    @Override
-    public void onAnimationStart(Animator animation) {
-        // This is the first time we get a handle to the internal ValueAnimator
-        // used by the ViewPropertyAnimator.
-        mFirstFrameHelper.onAnimationStart(animation);
-
-        for (int i = 0; i < mListeners.size(); i++) {
-            Animator.AnimatorListener listener = mListeners.get(i);
-            listener.onAnimationStart(this);
-        }
-        mRunning = true;
-    }
-
-    @Override
-    public boolean isRunning() {
-        return mRunning;
-    }
-
-    @Override
-    public boolean isStarted() {
-        return mViewPropertyAnimator != null;
-    }
-
-    @Override
-    public void removeAllListeners() {
-        mListeners.clear();
-    }
-
-    @Override
-    public void removeListener(Animator.AnimatorListener listener) {
-        mListeners.remove(listener);
-    }
-
-    @Override
-    public Animator setDuration(long duration) {
-        mPropertiesToSet.add(Properties.DURATION);
-        mDuration = duration;
-        return this;
-    }
-
-    @Override
-    public void setInterpolator(TimeInterpolator value) {
-        mPropertiesToSet.add(Properties.INTERPOLATOR);
-        mInterpolator = value;
-    }
-
-    @Override
-    public void setStartDelay(long startDelay) {
-        mPropertiesToSet.add(Properties.START_DELAY);
-        mStartDelay = startDelay;
-    }
-
-    @Override
-    public void setTarget(Object target) {
-        throw new RuntimeException("Not implemented");
-    }
-
-    @Override
-    public void setupEndValues() {
-
-    }
-
-    @Override
-    public void setupStartValues() {
+    public LauncherViewPropertyAnimator(View view) {
+        mTarget = view;
+        mProperties = new ArrayList<>();
+        setTarget(mTarget);
+        addListener(new TransientStateUpdater(mTarget));
     }
 
     @Override
     public void start() {
-        mViewPropertyAnimator = mTarget.animate();
-
-        // FirstFrameAnimatorHelper hooks itself up to the updates on the animator,
-        // and then adjusts the play time to keep the first two frames jank-free
-        mFirstFrameHelper = new FirstFrameAnimatorHelper(mViewPropertyAnimator, mTarget);
-
-        if (mPropertiesToSet.contains(Properties.TRANSLATION_X)) {
-            mViewPropertyAnimator.translationX(mTranslationX);
+        if (!mPrepared) {
+            mPrepared = true;
+            setValues(mProperties.toArray(new PropertyValuesHolder[mProperties.size()]));
         }
-        if (mPropertiesToSet.contains(Properties.TRANSLATION_Y)) {
-            mViewPropertyAnimator.translationY(mTranslationY);
-        }
-        if (mPropertiesToSet.contains(Properties.SCALE_X)) {
-            mViewPropertyAnimator.scaleX(mScaleX);
-        }
-        if (mPropertiesToSet.contains(Properties.ROTATION_Y)) {
-            mViewPropertyAnimator.rotationY(mRotationY);
-        }
-        if (mPropertiesToSet.contains(Properties.SCALE_Y)) {
-            mViewPropertyAnimator.scaleY(mScaleY);
-        }
-        if (mPropertiesToSet.contains(Properties.ALPHA)) {
-            mViewPropertyAnimator.alpha(mAlpha);
-        }
-        if (mPropertiesToSet.contains(Properties.START_DELAY)) {
-            mViewPropertyAnimator.setStartDelay(mStartDelay);
-        }
-        if (mPropertiesToSet.contains(Properties.DURATION)) {
-            mViewPropertyAnimator.setDuration(mDuration);
-        }
-        if (mPropertiesToSet.contains(Properties.INTERPOLATOR)) {
-            mViewPropertyAnimator.setInterpolator(mInterpolator);
-        }
-        if (mPropertiesToSet.contains(Properties.WITH_LAYER)) {
-            mViewPropertyAnimator.withLayer();
-        }
-        mViewPropertyAnimator.setListener(this);
-        mViewPropertyAnimator.start();
         LauncherAnimUtils.cancelOnDestroyActivity(this);
+        super.start();
     }
 
     public LauncherViewPropertyAnimator translationX(float value) {
-        mPropertiesToSet.add(Properties.TRANSLATION_X);
-        mTranslationX = value;
+        mProperties.add(PropertyValuesHolder.ofFloat(View.TRANSLATION_X, value));
         return this;
     }
 
     public LauncherViewPropertyAnimator translationY(float value) {
-        mPropertiesToSet.add(Properties.TRANSLATION_Y);
-        mTranslationY = value;
+        mProperties.add(PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, value));
         return this;
     }
 
     public LauncherViewPropertyAnimator scaleX(float value) {
-        mPropertiesToSet.add(Properties.SCALE_X);
-        mScaleX = value;
+        mProperties.add(PropertyValuesHolder.ofFloat(View.SCALE_X, value));
         return this;
     }
 
     public LauncherViewPropertyAnimator scaleY(float value) {
-        mPropertiesToSet.add(Properties.SCALE_Y);
-        mScaleY = value;
-        return this;
-    }
-
-    public LauncherViewPropertyAnimator rotationY(float value) {
-        mPropertiesToSet.add(Properties.ROTATION_Y);
-        mRotationY = value;
+        mProperties.add(PropertyValuesHolder.ofFloat(View.SCALE_Y, value));
         return this;
     }
 
     public LauncherViewPropertyAnimator alpha(float value) {
-        mPropertiesToSet.add(Properties.ALPHA);
-        mAlpha = value;
+        mProperties.add(PropertyValuesHolder.ofFloat(View.ALPHA, value));
         return this;
     }
 
     public LauncherViewPropertyAnimator withLayer() {
-        mPropertiesToSet.add(Properties.WITH_LAYER);
+        AnimationLayerSet listener = new AnimationLayerSet();
+        listener.addView(mTarget);
+        addListener(listener);
         return this;
     }
+
+    private static class TransientStateUpdater extends AnimatorListenerAdapter {
+        private final View mView;
+
+        TransientStateUpdater(View v) {
+            mView = v;
+        }
+
+        @Override
+        public void onAnimationStart(Animator animation) {
+            mView.setHasTransientState(true);
+        }
+
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            mView.setHasTransientState(false);
+        }
+    }
 }
diff --git a/src/com/android/launcher3/anim/AnimationLayerSet.java b/src/com/android/launcher3/anim/AnimationLayerSet.java
index 42706ff..d2f5e78 100644
--- a/src/com/android/launcher3/anim/AnimationLayerSet.java
+++ b/src/com/android/launcher3/anim/AnimationLayerSet.java
@@ -20,23 +20,29 @@
 import android.animation.AnimatorListenerAdapter;
 import android.view.View;
 
-import java.util.HashSet;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
 
 /**
  * Helper class to automatically build view hardware layers for the duration of an animation.
  */
 public class AnimationLayerSet extends AnimatorListenerAdapter {
 
-    private final HashSet<View> mViews = new HashSet<>();
+    private final HashMap<View, Integer> mViewsToLayerTypeMap = new HashMap<>();
 
     public void addView(View v) {
-        mViews.add(v);
+        mViewsToLayerTypeMap.put(v, v.getLayerType());
     }
 
     @Override
     public void onAnimationStart(Animator animation) {
         // Enable all necessary layers
-        for (View v : mViews) {
+        Iterator<Map.Entry<View, Integer>> itr = mViewsToLayerTypeMap.entrySet().iterator();
+        while (itr.hasNext()) {
+            Map.Entry<View, Integer> entry = itr.next();
+            View v = entry.getKey();
+            entry.setValue(v.getLayerType());
             v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
             if (v.isAttachedToWindow() && v.getVisibility() == View.VISIBLE) {
                 v.buildLayer();
@@ -46,8 +52,10 @@
 
     @Override
     public void onAnimationEnd(Animator animation) {
-        for (View v : mViews) {
-            v.setLayerType(View.LAYER_TYPE_NONE, null);
+        Iterator<Map.Entry<View, Integer>> itr = mViewsToLayerTypeMap.entrySet().iterator();
+        while (itr.hasNext()) {
+            Map.Entry<View, Integer> entry = itr.next();
+            entry.getKey().setLayerType(entry.getValue(), null);
         }
     }
 }