Merge "Fix bug where folder open animation gets finished early" into jb-mr2-dev

This commit is contained in:
Michael Jurka
2013-04-09 22:23:54 +00:00
committed by Android (Google) Code Review
2 changed files with 49 additions and 19 deletions
@@ -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 @@ public class FirstFrameAnimatorHelper implements ValueAnimator.AnimatorUpdateLis
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 {
@@ -53,6 +53,7 @@ public class LauncherViewPropertyAnimator extends Animator implements AnimatorLi
TimeInterpolator mInterpolator;
ArrayList<Animator.AnimatorListener> mListeners;
boolean mRunning = false;
FirstFrameAnimatorHelper mFirstFrameHelper;
public LauncherViewPropertyAnimator(View target) {
mTarget = target;
@@ -124,12 +125,10 @@ public class LauncherViewPropertyAnimator extends Animator implements AnimatorLi
@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 @@ public class LauncherViewPropertyAnimator extends Animator implements AnimatorLi
@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);
}