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.util.Log; 23e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurkaimport android.view.View; 24e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurkaimport android.view.ViewPropertyAnimator; 2598a7174d0ae87e2dbc184cc9cfb2670075bb8611Michael Jurkaimport android.view.ViewTreeObserver; 26e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka 27e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka/* 28e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka * This is a helper class that listens to updates from the corresponding animation. 29e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka * For the first two frames, it adjusts the current play time of the animation to 30e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka * prevent jank at the beginning of the animation 31e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka */ 3298a7174d0ae87e2dbc184cc9cfb2670075bb8611Michael Jurkapublic class FirstFrameAnimatorHelper extends AnimatorListenerAdapter 3398a7174d0ae87e2dbc184cc9cfb2670075bb8611Michael Jurka 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; 5398a7174d0ae87e2dbc184cc9cfb2670075bb8611Michael Jurka vpa.setListener(this); 5498a7174d0ae87e2dbc184cc9cfb2670075bb8611Michael Jurka } 5598a7174d0ae87e2dbc184cc9cfb2670075bb8611Michael Jurka 5698a7174d0ae87e2dbc184cc9cfb2670075bb8611Michael Jurka // only used for ViewPropertyAnimators 5798a7174d0ae87e2dbc184cc9cfb2670075bb8611Michael Jurka public void onAnimationStart(Animator animation) { 5898a7174d0ae87e2dbc184cc9cfb2670075bb8611Michael Jurka final ValueAnimator va = (ValueAnimator) animation; 5998a7174d0ae87e2dbc184cc9cfb2670075bb8611Michael Jurka va.addUpdateListener(FirstFrameAnimatorHelper.this); 6098a7174d0ae87e2dbc184cc9cfb2670075bb8611Michael Jurka onAnimationUpdate(va); 61e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka } 62e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka 63e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka public static void initializeDrawListener(View view) { 64e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka if (sGlobalDrawListener != null) { 65e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka view.getViewTreeObserver().removeOnDrawListener(sGlobalDrawListener); 66e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka } 67e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka sGlobalDrawListener = new ViewTreeObserver.OnDrawListener() { 68e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka private long mTime = System.currentTimeMillis(); 69e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka public void onDraw() { 70e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka sGlobalFrameCounter++; 71e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka if (DEBUG) { 72e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka long newTime = System.currentTimeMillis(); 73e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka Log.d("FirstFrameAnimatorHelper", "TICK " + (newTime - mTime)); 74e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka mTime = newTime; 75e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka } 76e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka } 77e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka }; 78e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka view.getViewTreeObserver().addOnDrawListener(sGlobalDrawListener); 79e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka } 80e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka 81e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka public void onAnimationUpdate(final ValueAnimator animation) { 82e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka final long currentTime = System.currentTimeMillis(); 83e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka if (mStartTime == -1) { 84e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka mStartFrame = sGlobalFrameCounter; 85e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka mStartTime = currentTime; 86e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka } 87e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka 8898a7174d0ae87e2dbc184cc9cfb2670075bb8611Michael Jurka if (!mHandlingOnAnimationUpdate && 8998a7174d0ae87e2dbc184cc9cfb2670075bb8611Michael Jurka // If the current play time exceeds the duration, the animation 9098a7174d0ae87e2dbc184cc9cfb2670075bb8611Michael Jurka // will get finished, even if we call setCurrentPlayTime -- therefore 9198a7174d0ae87e2dbc184cc9cfb2670075bb8611Michael Jurka // don't adjust the animation in that case 9298a7174d0ae87e2dbc184cc9cfb2670075bb8611Michael Jurka animation.getCurrentPlayTime() < animation.getDuration()) { 93e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka mHandlingOnAnimationUpdate = true; 94e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka long frameNum = sGlobalFrameCounter - mStartFrame; 95e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka // If we haven't drawn our first frame, reset the time to t = 0 96e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka // (give up after MAX_DELAY ms of waiting though - might happen, for example, if we 97e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka // are no longer in the foreground and no frames are being rendered ever) 98e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka if (frameNum == 0 && currentTime < mStartTime + MAX_DELAY) { 99e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka // The first frame on animations doesn't always trigger an invalidate... 100e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka // force an invalidate here to make sure the animation continues to advance 101e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka mTarget.getRootView().invalidate(); 102e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka animation.setCurrentPlayTime(0); 103e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka 104e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka // For the second frame, if the first frame took more than 16ms, 105e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka // adjust the start time and pretend it took only 16ms anyway. This 106e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka // prevents a large jump in the animation due to an expensive first frame 107e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka } else if (frameNum == 1 && currentTime < mStartTime + MAX_DELAY && 108e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka !mAdjustedSecondFrameTime && 109e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka currentTime > mStartTime + IDEAL_FRAME_DURATION) { 110e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka animation.setCurrentPlayTime(IDEAL_FRAME_DURATION); 111e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka mAdjustedSecondFrameTime = true; 112e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka } else { 113e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka if (frameNum > 1) { 114e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka mTarget.post(new Runnable() { 115e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka public void run() { 116e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka animation.removeUpdateListener(FirstFrameAnimatorHelper.this); 117e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka } 118e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka }); 119e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka } 120e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka if (DEBUG) print(animation); 121e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka } 122e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka mHandlingOnAnimationUpdate = false; 123e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka } else { 124e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka if (DEBUG) print(animation); 125e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka } 126e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka } 127e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka 128e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka public void print(ValueAnimator animation) { 129e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka float flatFraction = animation.getCurrentPlayTime() / (float) animation.getDuration(); 130e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka Log.d("FirstFrameAnimatorHelper", sGlobalFrameCounter + 131e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka "(" + (sGlobalFrameCounter - mStartFrame) + ") " + mTarget + " dirty? " + 132e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka mTarget.isDirty() + " " + flatFraction + " " + this + " " + animation); 133e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka } 134e0523f7c803506090b8cb45dca2a8bd5e36af456Michael Jurka} 135