FirstFrameAnimatorHelper.java revision e0523f7c803506090b8cb45dca2a8bd5e36af456
1e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka/* 2e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka * Copyright (C) 2013 The Android Open Source Project 3e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka * 4e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka * Licensed under the Apache License, Version 2.0 (the "License"); 5e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka * you may not use this file except in compliance with the License. 6e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka * You may obtain a copy of the License at 7e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka * 8e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka * http://www.apache.org/licenses/LICENSE-2.0 9e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka * 10e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka * Unless required by applicable law or agreed to in writing, software 11e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka * distributed under the License is distributed on an "AS IS" BASIS, 12e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka * See the License for the specific language governing permissions and 14e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka * limitations under the License. 15e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka */ 16e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka 17e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurkapackage com.android.systemui.recent; 18e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka 19e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurkaimport android.animation.Animator; 20e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurkaimport android.animation.AnimatorListenerAdapter; 21e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurkaimport android.animation.ValueAnimator; 22e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurkaimport android.animation.Animator.AnimatorListener; 23e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurkaimport android.util.Log; 24e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurkaimport android.view.ViewTreeObserver; 25e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurkaimport android.view.View; 26e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurkaimport android.view.ViewPropertyAnimator; 27e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka 28e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka/* 29e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka * This is a helper class that listens to updates from the corresponding animation. 30e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka * For the first two frames, it adjusts the current play time of the animation to 31e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka * prevent jank at the beginning of the animation 32e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka */ 33e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurkapublic class FirstFrameAnimatorHelper implements ValueAnimator.AnimatorUpdateListener { 34e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka private static final boolean DEBUG = false; 35e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka private static final int MAX_DELAY = 1000; 36e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka private static final int IDEAL_FRAME_DURATION = 16; 37e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka private View mTarget; 38e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka private long mStartFrame; 39e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka private long mStartTime = -1; 40e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka private boolean mHandlingOnAnimationUpdate; 41e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka private boolean mAdjustedSecondFrameTime; 42e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka 43e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka private static ViewTreeObserver.OnDrawListener sGlobalDrawListener; 44e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka private static long sGlobalFrameCounter; 45e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka 46e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka public FirstFrameAnimatorHelper(ValueAnimator animator, View target) { 47e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka mTarget = target; 48e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka animator.addUpdateListener(this); 49e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka } 50e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka 51e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka public FirstFrameAnimatorHelper(ViewPropertyAnimator vpa, View target) { 52e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka mTarget = target; 53e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka vpa.setListener(new AnimatorListenerAdapter() { 54e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka public void onAnimationStart (Animator animation) { 55e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka final ValueAnimator va = (ValueAnimator) animation; 56e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka va.addUpdateListener(FirstFrameAnimatorHelper.this); 57e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka onAnimationUpdate(va); 58e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka } 59e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka }); 60e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka } 61e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka 62e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka public static void initializeDrawListener(View view) { 63e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka if (sGlobalDrawListener != null) { 64e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka view.getViewTreeObserver().removeOnDrawListener(sGlobalDrawListener); 65e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka } 66e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka sGlobalDrawListener = new ViewTreeObserver.OnDrawListener() { 67e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka private long mTime = System.currentTimeMillis(); 68e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka public void onDraw() { 69e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka sGlobalFrameCounter++; 70e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka if (DEBUG) { 71e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka long newTime = System.currentTimeMillis(); 72e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka Log.d("FirstFrameAnimatorHelper", "TICK " + (newTime - mTime)); 73e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka mTime = newTime; 74e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka } 75e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka } 76e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka }; 77e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka view.getViewTreeObserver().addOnDrawListener(sGlobalDrawListener); 78e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka } 79e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka 80e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka public void onAnimationUpdate(final ValueAnimator animation) { 81e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka final long currentTime = System.currentTimeMillis(); 82e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka if (mStartTime == -1) { 83e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka mStartFrame = sGlobalFrameCounter; 84e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka mStartTime = currentTime; 85e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka } 86e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka 87e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka if (!mHandlingOnAnimationUpdate) { 88e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka mHandlingOnAnimationUpdate = true; 89e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka long frameNum = sGlobalFrameCounter - mStartFrame; 90e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka // If we haven't drawn our first frame, reset the time to t = 0 91e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka // (give up after MAX_DELAY ms of waiting though - might happen, for example, if we 92e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka // are no longer in the foreground and no frames are being rendered ever) 93e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka if (frameNum == 0 && currentTime < mStartTime + MAX_DELAY) { 94e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka // The first frame on animations doesn't always trigger an invalidate... 95e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka // force an invalidate here to make sure the animation continues to advance 96e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka mTarget.getRootView().invalidate(); 97e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka animation.setCurrentPlayTime(0); 98e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka 99e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka // For the second frame, if the first frame took more than 16ms, 100e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka // adjust the start time and pretend it took only 16ms anyway. This 101e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka // prevents a large jump in the animation due to an expensive first frame 102e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka } else if (frameNum == 1 && currentTime < mStartTime + MAX_DELAY && 103e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka !mAdjustedSecondFrameTime && 104e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka currentTime > mStartTime + IDEAL_FRAME_DURATION) { 105e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka animation.setCurrentPlayTime(IDEAL_FRAME_DURATION); 106e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka mAdjustedSecondFrameTime = true; 107e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka } else { 108e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka if (frameNum > 1) { 109e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka mTarget.post(new Runnable() { 110e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka public void run() { 111e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka animation.removeUpdateListener(FirstFrameAnimatorHelper.this); 112e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka } 113e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka }); 114e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka } 115e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka if (DEBUG) print(animation); 116e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka } 117e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka mHandlingOnAnimationUpdate = false; 118e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka } else { 119e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka if (DEBUG) print(animation); 120e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka } 121e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka } 122e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka 123e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka public void print(ValueAnimator animation) { 124e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka float flatFraction = animation.getCurrentPlayTime() / (float) animation.getDuration(); 125e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka Log.d("FirstFrameAnimatorHelper", sGlobalFrameCounter + 126e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka "(" + (sGlobalFrameCounter - mStartFrame) + ") " + mTarget + " dirty? " + 127e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka mTarget.isDirty() + " " + flatFraction + " " + this + " " + animation); 128e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka } 129e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka} 130