Using ViewPropertyAnimator for animations

Replacing some use of fastInvalidate/setFast*
methods with ViewPropertyAnimator animations

Change-Id: Id5a8934b38d9ae3a95b6dccb431f9787839d2927
diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java
index 5abfa28..45a9e31 100644
--- a/src/com/android/launcher2/Launcher.java
+++ b/src/com/android/launcher2/Launcher.java
@@ -2226,14 +2226,13 @@
                 mWorkspace.getChangeStateAnimation(Workspace.State.SMALL, animated);
 
         if (animated) {
-            final ValueAnimator scaleAnim = ValueAnimator.ofFloat(0f, 1f).setDuration(duration);
-            scaleAnim.setInterpolator(new Workspace.ZoomOutInterpolator());
-            scaleAnim.addUpdateListener(new LauncherAnimatorUpdateListener() {
-                public void onAnimationUpdate(float a, float b) {
-                    toView.setScaleX(a * scale + b * 1f);
-                    toView.setScaleY(a * scale + b * 1f);
-                }
-            });
+            toView.setScaleX(scale);
+            toView.setScaleY(scale);
+            final LauncherViewPropertyAnimator scaleAnim = new LauncherViewPropertyAnimator(toView);
+            scaleAnim.
+                scaleX(1f).scaleY(1f).
+                setDuration(duration).
+                setInterpolator(new Workspace.ZoomOutInterpolator());
 
             toView.setVisibility(View.VISIBLE);
             toView.setAlpha(0f);
@@ -2248,8 +2247,8 @@
             // toView should appear right at the end of the workspace shrink
             // animation
             mStateAnimation = new AnimatorSet();
-            mStateAnimation.play(alphaAnim).after(startDelay);
             mStateAnimation.play(scaleAnim).after(startDelay);
+            mStateAnimation.play(alphaAnim).after(startDelay);
 
             mStateAnimation.addListener(new AnimatorListenerAdapter() {
                 boolean animationCancelled = false;
@@ -2579,8 +2578,10 @@
     void showHotseat(boolean animated) {
         if (!LauncherApplication.isScreenLarge()) {
             if (animated) {
-                int duration = mSearchDropTargetBar.getTransitionInDuration();
-                mHotseat.animate().alpha(1f).setDuration(duration);
+                if (mHotseat.getAlpha() != 1f) {
+                    int duration = mSearchDropTargetBar.getTransitionInDuration();
+                    mHotseat.animate().alpha(1f).setDuration(duration);
+                }
             } else {
                 mHotseat.setAlpha(1f);
             }
@@ -2593,8 +2594,10 @@
     void hideHotseat(boolean animated) {
         if (!LauncherApplication.isScreenLarge()) {
             if (animated) {
-                int duration = mSearchDropTargetBar.getTransitionOutDuration();
-                mHotseat.animate().alpha(0f).setDuration(duration);
+                if (mHotseat.getAlpha() != 0f) {
+                    int duration = mSearchDropTargetBar.getTransitionOutDuration();
+                    mHotseat.animate().alpha(0f).setDuration(duration);
+                }
             } else {
                 mHotseat.setAlpha(0f);
             }
diff --git a/src/com/android/launcher2/LauncherViewPropertyAnimator.java b/src/com/android/launcher2/LauncherViewPropertyAnimator.java
new file mode 100644
index 0000000..f5becdc
--- /dev/null
+++ b/src/com/android/launcher2/LauncherViewPropertyAnimator.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher2;
+
+import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
+import android.animation.TimeInterpolator;
+import android.view.ViewPropertyAnimator;
+import android.view.View;
+
+import java.util.ArrayList;
+import java.util.EnumSet;
+
+public class LauncherViewPropertyAnimator extends Animator implements AnimatorListener {
+    enum Properties {
+            TRANSLATION_X,
+            TRANSLATION_Y,
+            SCALE_X,
+            SCALE_Y,
+            ROTATION_Y,
+            ALPHA,
+            START_DELAY,
+            DURATION,
+            INTERPOLATOR
+    }
+    EnumSet<Properties> mPropertiesToSet = EnumSet.noneOf(Properties.class);
+    ViewPropertyAnimator mViewPropertyAnimator;
+    View mTarget;
+
+    float mTranslationX;
+    float mTranslationY;
+    float mScaleX;
+    float mScaleY;
+    float mRotationY;
+    float mAlpha;
+    long mStartDelay;
+    long mDuration;
+    TimeInterpolator mInterpolator;
+    Animator.AnimatorListener mListener;
+    boolean mRunning = false;
+
+    public LauncherViewPropertyAnimator(View target) {
+        mTarget = target;
+    }
+
+    @Override
+    public void addListener(Animator.AnimatorListener listener) {
+        if (mListener != null) {
+            throw new RuntimeException("Only one listener supported");
+        }
+        mListener = listener;
+    }
+
+    @Override
+    public void cancel() {
+        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 mViewPropertyAnimator.getDuration();
+    }
+
+    @Override
+    public ArrayList<Animator.AnimatorListener> getListeners() {
+        return null;
+    }
+
+    @Override
+    public long getStartDelay() {
+        return mViewPropertyAnimator.getStartDelay();
+    }
+
+    @Override
+    public void onAnimationCancel(Animator animation) {
+        if (mListener != null) {
+            mListener.onAnimationCancel(this);
+        }
+        mRunning = false;
+    }
+
+    @Override
+    public void onAnimationEnd(Animator animation) {
+        if (mListener != null) {
+            mListener.onAnimationEnd(this);
+        }
+        mRunning = false;
+    }
+
+    @Override
+    public void onAnimationRepeat(Animator animation) {
+        if (mListener != null) {
+            mListener.onAnimationRepeat(this);
+        }
+    }
+
+    @Override
+    public void onAnimationStart(Animator animation) {
+        if (mListener != null) {
+            mListener.onAnimationStart(this);
+        }
+        mRunning = true;
+    }
+
+    @Override
+    public boolean isRunning() {
+        return mRunning;
+    }
+
+    @Override
+    public boolean isStarted() {
+        return mViewPropertyAnimator != null;
+    }
+
+    @Override
+    public void removeAllListeners() {
+        mListener = null;
+    }
+
+    @Override
+    public void removeListener(Animator.AnimatorListener listener) {
+        if (mListener == listener) {
+            mListener = null;
+        } else {
+            throw new RuntimeException("Removing listener that wasn't set");
+        }
+    }
+
+    @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() {
+    }
+
+    @Override
+    public void start() {
+        mViewPropertyAnimator = mTarget.animate();
+        if (mPropertiesToSet.contains(Properties.TRANSLATION_X)) {
+            mViewPropertyAnimator.translationX(mTranslationX);
+        }
+        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);
+        }
+        mViewPropertyAnimator.setListener(this);
+        mViewPropertyAnimator.start();
+    }
+
+    public LauncherViewPropertyAnimator translationX(float value) {
+        mPropertiesToSet.add(Properties.TRANSLATION_X);
+        mTranslationX = value;
+        return this;
+    }
+
+    public LauncherViewPropertyAnimator translationY(float value) {
+        mPropertiesToSet.add(Properties.TRANSLATION_Y);
+        mTranslationY = value;
+        return this;
+    }
+
+    public LauncherViewPropertyAnimator scaleX(float value) {
+        mPropertiesToSet.add(Properties.SCALE_X);
+        mScaleX = 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;
+        return this;
+    }
+
+    public LauncherViewPropertyAnimator alpha(float value) {
+        mPropertiesToSet.add(Properties.ALPHA);
+        mAlpha = value;
+        return this;
+    }
+}
diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java
index 148c1ba..7d61f62 100644
--- a/src/com/android/launcher2/Workspace.java
+++ b/src/com/android/launcher2/Workspace.java
@@ -57,7 +57,6 @@
 import android.view.DragEvent;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.ViewConfiguration;
 import android.view.ViewGroup;
 import android.view.animation.DecelerateInterpolator;
 import android.widget.ImageView;
@@ -1635,14 +1634,7 @@
         }
 
         if (animated) {
-            ValueAnimator animWithInterpolator =
-                ValueAnimator.ofFloat(0f, 1f).setDuration(duration);
-
-            if (zoomIn) {
-                animWithInterpolator.setInterpolator(mZoomInInterpolator);
-            }
-
-            animWithInterpolator.addListener(new AnimatorListenerAdapter() {
+            anim.addListener(new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(android.animation.Animator animation) {
                     // The above code to determine initialAlpha and finalAlpha will ensure that only
@@ -1657,10 +1649,11 @@
                     }
                 }
             });
-            for (int i = 0; i < getChildCount(); i++) {
+            for (int index = 0; index < getChildCount(); index++) {
+                final int i = index;
+                final CellLayout cl = (CellLayout) getChildAt(i);
                 invalidate();
                 if (mOldAlphas[i] == 0 && mNewAlphas[i] == 0) {
-                    final CellLayout cl = (CellLayout) getChildAt(i);
                     cl.fastInvalidate();
                     cl.setFastTranslationX(mNewTranslationXs[i]);
                     cl.setFastTranslationY(mNewTranslationYs[i]);
@@ -1669,60 +1662,48 @@
                     cl.setFastBackgroundAlpha(mNewBackgroundAlphas[i]);
                     cl.setBackgroundAlphaMultiplier(mNewBackgroundAlphaMultipliers[i]);
                     cl.setFastAlpha(mNewAlphas[i]);
+                } else {
+                    LauncherViewPropertyAnimator a = new LauncherViewPropertyAnimator(cl);
+                    a.translationX(mNewTranslationXs[i])
+                        .translationY(mNewTranslationYs[i])
+                        .scaleX(mNewScaleXs[i])
+                        .scaleY(mNewScaleYs[i])
+                        .setDuration(duration)
+                        .setInterpolator(mZoomInInterpolator);
+                    if (mOldAlphas[i] != mNewAlphas[i]) {
+                        a.alpha(mNewAlphas[i]);
+                    }
+                    anim.play(a);
+                    if (mOldRotationYs[i] != 0 || mNewRotationYs[i] != 0) {
+                        ValueAnimator rotate = ValueAnimator.ofFloat(0f, 1f).setDuration(duration);
+                        rotate.setInterpolator(new DecelerateInterpolator(2.0f));
+                        rotate.addUpdateListener(new LauncherAnimatorUpdateListener() {
+                                public void onAnimationUpdate(float a, float b) {
+                                    cl.setRotationY(a * 0f + b * 1f);
+                                }
+                            });
+                        anim.play(rotate);
+                    }
+                    if (mOldBackgroundAlphas[i] != 0 ||
+                        mNewBackgroundAlphas[i] != 0 ||
+                        mOldBackgroundAlphaMultipliers[i] != 0 ||
+                        mNewBackgroundAlphaMultipliers[i] != 0) {
+                        ValueAnimator bgAnim = ValueAnimator.ofFloat(0f, 1f).setDuration(duration);
+                        bgAnim.setInterpolator(mZoomInInterpolator);
+                        bgAnim.addUpdateListener(new LauncherAnimatorUpdateListener() {
+                                public void onAnimationUpdate(float a, float b) {
+                                    cl.setFastBackgroundAlpha(
+                                            a * mOldBackgroundAlphas[i] +
+                                            b * mNewBackgroundAlphas[i]);
+                                    cl.setBackgroundAlphaMultiplier(
+                                            a * mOldBackgroundAlphaMultipliers[i] +
+                                            b * mNewBackgroundAlphaMultipliers[i]);
+                                }
+                            });
+                        anim.play(bgAnim);
+                    }
                 }
             }
-
-            animWithInterpolator.addUpdateListener(new LauncherAnimatorUpdateListener() {
-                public void onAnimationUpdate(float a, float b) {
-                    mTransitionProgress = b;
-                    if (b == 0f) {
-                        // an optimization, but not required
-                        return;
-                    }
-                    invalidate();
-                    for (int i = 0; i < getChildCount(); i++) {
-                        if (mOldAlphas[i] != 0 || mNewAlphas[i] != 0) {
-                            final CellLayout cl = (CellLayout) getChildAt(i);
-                            cl.fastInvalidate();
-                            cl.setFastTranslationX(
-                                    a * mOldTranslationXs[i] + b * mNewTranslationXs[i]);
-                            cl.setFastTranslationY(
-                                    a * mOldTranslationYs[i] + b * mNewTranslationYs[i]);
-                            cl.setFastScaleX(a * mOldScaleXs[i] + b * mNewScaleXs[i]);
-                            cl.setFastScaleY(a * mOldScaleYs[i] + b * mNewScaleYs[i]);
-                            cl.setFastBackgroundAlpha(
-                                    a * mOldBackgroundAlphas[i] + b * mNewBackgroundAlphas[i]);
-                            cl.setBackgroundAlphaMultiplier(a * mOldBackgroundAlphaMultipliers[i] +
-                                    b * mNewBackgroundAlphaMultipliers[i]);
-                            cl.setFastAlpha(a * mOldAlphas[i] + b * mNewAlphas[i]);
-                            if (mOldAlphas[i] != mNewAlphas[i]) {
-                                cl.setAlpha(a * mOldAlphas[i] + b * mNewAlphas[i]);
-                            }
-                        }
-                    }
-                }
-            });
-
-            ValueAnimator rotationAnim =
-                ValueAnimator.ofFloat(0f, 1f).setDuration(duration);
-            rotationAnim.setInterpolator(new DecelerateInterpolator(2.0f));
-            rotationAnim.addUpdateListener(new LauncherAnimatorUpdateListener() {
-                public void onAnimationUpdate(float a, float b) {
-                    if (b == 0f) {
-                        // an optimization, but not required
-                        return;
-                    }
-                    for (int i = 0; i < getChildCount(); i++) {
-                        if (mOldAlphas[i] != 0 || mNewAlphas[i] != 0 ||
-                                mOldRotationYs[i] != 0 || mNewRotationYs[i] != 0) {
-                            final CellLayout cl = (CellLayout) getChildAt(i);
-                            cl.setFastRotationY(a * mOldRotationYs[i] + b * mNewRotationYs[i]);
-                        }
-                    }
-                }
-            });
-
-            anim.playTogether(animWithInterpolator, rotationAnim);
             anim.setStartDelay(delay);
             // If we call this when we're not animated, onAnimationEnd is never called on
             // the listener; make sure we only use the listener when we're actually animating