Merge "Fix bug where folder open animation gets finished early" into jb-mr2-dev
diff --git a/src/com/android/launcher2/FirstFrameAnimatorHelper.java b/src/com/android/launcher2/FirstFrameAnimatorHelper.java
index 2351dd0..8eac7cd 100644
--- a/src/com/android/launcher2/FirstFrameAnimatorHelper.java
+++ b/src/com/android/launcher2/FirstFrameAnimatorHelper.java
@@ -16,20 +16,24 @@
 
 package com.android.launcher2;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.animation.Animator.AnimatorListener;
 import android.util.Log;
 import android.view.ViewTreeObserver;
 import android.view.View;
+import android.view.ViewPropertyAnimator;
 
 /*
  *  This is a helper class that listens to updates from the corresponding animation.
  *  For the first two frames, it adjusts the current play time of the animation to
  *  prevent jank at the beginning of the animation
  */
-public class FirstFrameAnimatorHelper implements ValueAnimator.AnimatorUpdateListener {
+public class FirstFrameAnimatorHelper extends AnimatorListenerAdapter
+    implements ValueAnimator.AnimatorUpdateListener {
     private static final boolean DEBUG = false;
-    private static final int MAX_FIRST_FRAME_DELAY = 200;
+    private static final int MAX_DELAY = 1000;
     private static final int IDEAL_FRAME_DURATION = 16;
     private View mTarget;
     private long mStartFrame;
@@ -45,43 +49,65 @@
         animator.addUpdateListener(this);
     }
 
+    public FirstFrameAnimatorHelper(ViewPropertyAnimator vpa, View target) {
+        mTarget = target;
+        vpa.setListener(this);
+    }
+
+    // only used for ViewPropertyAnimators
+    public void onAnimationStart(Animator animation) {
+        final ValueAnimator va = (ValueAnimator) animation;
+        va.addUpdateListener(FirstFrameAnimatorHelper.this);
+        onAnimationUpdate(va);
+    }
+
     public static void initializeDrawListener(View view) {
+        if (sGlobalDrawListener != null) {
+            view.getViewTreeObserver().removeOnDrawListener(sGlobalDrawListener);
+        }
         sGlobalDrawListener = new ViewTreeObserver.OnDrawListener() {
                 private long mTime = System.currentTimeMillis();
                 public void onDraw() {
                     sGlobalFrameCounter++;
-                    long newTime = System.currentTimeMillis();
                     if (DEBUG) {
+                        long newTime = System.currentTimeMillis();
                         Log.d("FirstFrameAnimatorHelper", "TICK " + (newTime - mTime));
+                        mTime = newTime;
                     }
-                    mTime = newTime;
                 }
             };
         view.getViewTreeObserver().addOnDrawListener(sGlobalDrawListener);
     }
 
     public void onAnimationUpdate(final ValueAnimator animation) {
+        final long currentTime = System.currentTimeMillis();
         if (mStartTime == -1) {
             mStartFrame = sGlobalFrameCounter;
-            mStartTime = System.currentTimeMillis();
+            mStartTime = currentTime;
         }
 
-        if (!mHandlingOnAnimationUpdate) {
+        if (!mHandlingOnAnimationUpdate &&
+            // If the current play time exceeds the duration, the animation
+            // will get finished, even if we call setCurrentPlayTime -- therefore
+            // don't adjust the animation in that case
+            animation.getCurrentPlayTime() < animation.getDuration()) {
             mHandlingOnAnimationUpdate = true;
             long frameNum = sGlobalFrameCounter - mStartFrame;
-
             // If we haven't drawn our first frame, reset the time to t = 0
-            // (give up after 200ms of waiting though - might happen, for example, if we are no
-            // longer in the foreground and no frames are being rendered ever)
-            if (frameNum == 0 && System.currentTimeMillis() < mStartTime + MAX_FIRST_FRAME_DELAY) {
-                mTarget.getRootView().invalidate(); // make sure we'll do a draw
+            // (give up after MAX_DELAY ms of waiting though - might happen, for example, if we
+            // are no longer in the foreground and no frames are being rendered ever)
+            if (frameNum == 0 && currentTime < mStartTime + MAX_DELAY) {
+                // The first frame on animations doesn't always trigger an invalidate...
+                // force an invalidate here to make sure the animation continues to advance
+                mTarget.getRootView().invalidate();
                 animation.setCurrentPlayTime(0);
 
             // For the second frame, if the first frame took more than 16ms,
             // adjust the start time and pretend it took only 16ms anyway. This
             // prevents a large jump in the animation due to an expensive first frame
-            } else if (frameNum == 1 && !mAdjustedSecondFrameTime &&
-                       System.currentTimeMillis() > mStartTime + 16) {
+            } else if (frameNum == 1 && currentTime < mStartTime + MAX_DELAY &&
+                       !mAdjustedSecondFrameTime &&
+                       currentTime > mStartTime + IDEAL_FRAME_DURATION) {
                 animation.setCurrentPlayTime(IDEAL_FRAME_DURATION);
                 mAdjustedSecondFrameTime = true;
             } else {
diff --git a/src/com/android/launcher2/LauncherViewPropertyAnimator.java b/src/com/android/launcher2/LauncherViewPropertyAnimator.java
index 274ff80..83b745e 100644
--- a/src/com/android/launcher2/LauncherViewPropertyAnimator.java
+++ b/src/com/android/launcher2/LauncherViewPropertyAnimator.java
@@ -53,6 +53,7 @@
     TimeInterpolator mInterpolator;
     ArrayList<Animator.AnimatorListener> mListeners;
     boolean mRunning = false;
+    FirstFrameAnimatorHelper mFirstFrameHelper;
 
     public LauncherViewPropertyAnimator(View target) {
         mTarget = target;
@@ -124,12 +125,10 @@
 
     @Override
     public void onAnimationStart(Animator animation) {
-	// This is the first time we get a handle to the internal ValueAnimator
-	// used by the ViewPropertyAnimator.
-	// FirstFrameAnimatorHelper hooks itself up to the updates on the animator,
-	// and then adjusts the play time to keep the first two frames jank-free
-        new FirstFrameAnimatorHelper((ValueAnimator) animation, mTarget)
-                .onAnimationUpdate((ValueAnimator) 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);
@@ -193,6 +192,11 @@
     @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);
         }