From 39b599e0641047e5583cf717d35a4cb080049665 Mon Sep 17 00:00:00 2001 From: Michael Jurka Date: Mon, 8 Apr 2013 18:28:15 -0700 Subject: [PATCH] Fix bug where folder open animation gets finished early Also, sync FirstFrameAnimatorHelper with changes made in Recents Bug: 8530231 Change-Id: Ibf1a43f67d91df89a2338f0f0c0fb52ee5496e57 --- .../launcher2/FirstFrameAnimatorHelper.java | 52 ++++++++++++++++------ .../launcher2/LauncherViewPropertyAnimator.java | 16 ++++--- 2 files changed, 49 insertions(+), 19 deletions(-) diff --git a/src/com/android/launcher2/FirstFrameAnimatorHelper.java b/src/com/android/launcher2/FirstFrameAnimatorHelper.java index 2351dd074..8eac7cd2c 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 @@ 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 { diff --git a/src/com/android/launcher2/LauncherViewPropertyAnimator.java b/src/com/android/launcher2/LauncherViewPropertyAnimator.java index 274ff80b3..83b745eb5 100644 --- a/src/com/android/launcher2/LauncherViewPropertyAnimator.java +++ b/src/com/android/launcher2/LauncherViewPropertyAnimator.java @@ -53,6 +53,7 @@ public class LauncherViewPropertyAnimator extends Animator implements AnimatorLi TimeInterpolator mInterpolator; ArrayList 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); } -- 2.11.0