1f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka/* 2f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka * Copyright (C) 2013 The Android Open Source Project 3f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka * 4f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka * Licensed under the Apache License, Version 2.0 (the "License"); 5f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka * you may not use this file except in compliance with the License. 6f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka * You may obtain a copy of the License at 7f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka * 8f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka * http://www.apache.org/licenses/LICENSE-2.0 9f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka * 10f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka * Unless required by applicable law or agreed to in writing, software 11f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka * distributed under the License is distributed on an "AS IS" BASIS, 12f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka * See the License for the specific language governing permissions and 14f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka * limitations under the License. 15f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka */ 16f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka 17f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurkapackage com.android.launcher2; 18f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka 1939b599e0641047e5583cf717d35a4cb080049665Michael Jurkaimport android.animation.Animator; 2039b599e0641047e5583cf717d35a4cb080049665Michael Jurkaimport android.animation.AnimatorListenerAdapter; 21f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurkaimport android.animation.ValueAnimator; 22f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurkaimport android.animation.Animator.AnimatorListener; 23f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurkaimport android.util.Log; 24f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurkaimport android.view.ViewTreeObserver; 25f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurkaimport android.view.View; 2639b599e0641047e5583cf717d35a4cb080049665Michael Jurkaimport android.view.ViewPropertyAnimator; 27f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka 28f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka/* 29f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka * This is a helper class that listens to updates from the corresponding animation. 30f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka * For the first two frames, it adjusts the current play time of the animation to 31f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka * prevent jank at the beginning of the animation 32f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka */ 3339b599e0641047e5583cf717d35a4cb080049665Michael Jurkapublic class FirstFrameAnimatorHelper extends AnimatorListenerAdapter 3439b599e0641047e5583cf717d35a4cb080049665Michael Jurka implements ValueAnimator.AnimatorUpdateListener { 35f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka private static final boolean DEBUG = false; 3639b599e0641047e5583cf717d35a4cb080049665Michael Jurka private static final int MAX_DELAY = 1000; 37f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka private static final int IDEAL_FRAME_DURATION = 16; 38f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka private View mTarget; 39f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka private long mStartFrame; 40f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka private long mStartTime = -1; 41f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka private boolean mHandlingOnAnimationUpdate; 42f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka private boolean mAdjustedSecondFrameTime; 43f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka 44f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka private static ViewTreeObserver.OnDrawListener sGlobalDrawListener; 45f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka private static long sGlobalFrameCounter; 46cd496d723ca6b4b100771ae66f09007e7acf0d17Michael Jurka private static boolean sVisible; 47f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka 48f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka public FirstFrameAnimatorHelper(ValueAnimator animator, View target) { 49f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka mTarget = target; 50f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka animator.addUpdateListener(this); 51f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka } 52f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka 5339b599e0641047e5583cf717d35a4cb080049665Michael Jurka public FirstFrameAnimatorHelper(ViewPropertyAnimator vpa, View target) { 5439b599e0641047e5583cf717d35a4cb080049665Michael Jurka mTarget = target; 5539b599e0641047e5583cf717d35a4cb080049665Michael Jurka vpa.setListener(this); 5639b599e0641047e5583cf717d35a4cb080049665Michael Jurka } 5739b599e0641047e5583cf717d35a4cb080049665Michael Jurka 5839b599e0641047e5583cf717d35a4cb080049665Michael Jurka // only used for ViewPropertyAnimators 5939b599e0641047e5583cf717d35a4cb080049665Michael Jurka public void onAnimationStart(Animator animation) { 6039b599e0641047e5583cf717d35a4cb080049665Michael Jurka final ValueAnimator va = (ValueAnimator) animation; 6139b599e0641047e5583cf717d35a4cb080049665Michael Jurka va.addUpdateListener(FirstFrameAnimatorHelper.this); 6239b599e0641047e5583cf717d35a4cb080049665Michael Jurka onAnimationUpdate(va); 6339b599e0641047e5583cf717d35a4cb080049665Michael Jurka } 6439b599e0641047e5583cf717d35a4cb080049665Michael Jurka 65cd496d723ca6b4b100771ae66f09007e7acf0d17Michael Jurka public static void setIsVisible(boolean visible) { 66cd496d723ca6b4b100771ae66f09007e7acf0d17Michael Jurka sVisible = visible; 67cd496d723ca6b4b100771ae66f09007e7acf0d17Michael Jurka } 68cd496d723ca6b4b100771ae66f09007e7acf0d17Michael Jurka 69f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka public static void initializeDrawListener(View view) { 7039b599e0641047e5583cf717d35a4cb080049665Michael Jurka if (sGlobalDrawListener != null) { 7139b599e0641047e5583cf717d35a4cb080049665Michael Jurka view.getViewTreeObserver().removeOnDrawListener(sGlobalDrawListener); 7239b599e0641047e5583cf717d35a4cb080049665Michael Jurka } 73f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka sGlobalDrawListener = new ViewTreeObserver.OnDrawListener() { 74f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka private long mTime = System.currentTimeMillis(); 75f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka public void onDraw() { 76f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka sGlobalFrameCounter++; 77f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka if (DEBUG) { 7839b599e0641047e5583cf717d35a4cb080049665Michael Jurka long newTime = System.currentTimeMillis(); 79f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka Log.d("FirstFrameAnimatorHelper", "TICK " + (newTime - mTime)); 8039b599e0641047e5583cf717d35a4cb080049665Michael Jurka mTime = newTime; 81f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka } 82f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka } 83f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka }; 84f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka view.getViewTreeObserver().addOnDrawListener(sGlobalDrawListener); 85cd496d723ca6b4b100771ae66f09007e7acf0d17Michael Jurka sVisible = true; 86f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka } 87f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka 8898720c96ee8c1eab026903dd83507a3cc50de900Michael Jurka public void onAnimationUpdate(final ValueAnimator animation) { 8939b599e0641047e5583cf717d35a4cb080049665Michael Jurka final long currentTime = System.currentTimeMillis(); 90f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka if (mStartTime == -1) { 91f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka mStartFrame = sGlobalFrameCounter; 9239b599e0641047e5583cf717d35a4cb080049665Michael Jurka mStartTime = currentTime; 93f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka } 94f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka 9539b599e0641047e5583cf717d35a4cb080049665Michael Jurka if (!mHandlingOnAnimationUpdate && 96cd496d723ca6b4b100771ae66f09007e7acf0d17Michael Jurka sVisible && 9739b599e0641047e5583cf717d35a4cb080049665Michael Jurka // If the current play time exceeds the duration, the animation 9839b599e0641047e5583cf717d35a4cb080049665Michael Jurka // will get finished, even if we call setCurrentPlayTime -- therefore 9939b599e0641047e5583cf717d35a4cb080049665Michael Jurka // don't adjust the animation in that case 10039b599e0641047e5583cf717d35a4cb080049665Michael Jurka animation.getCurrentPlayTime() < animation.getDuration()) { 101f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka mHandlingOnAnimationUpdate = true; 102f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka long frameNum = sGlobalFrameCounter - mStartFrame; 103f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka // If we haven't drawn our first frame, reset the time to t = 0 10439b599e0641047e5583cf717d35a4cb080049665Michael Jurka // (give up after MAX_DELAY ms of waiting though - might happen, for example, if we 10539b599e0641047e5583cf717d35a4cb080049665Michael Jurka // are no longer in the foreground and no frames are being rendered ever) 10639b599e0641047e5583cf717d35a4cb080049665Michael Jurka if (frameNum == 0 && currentTime < mStartTime + MAX_DELAY) { 10739b599e0641047e5583cf717d35a4cb080049665Michael Jurka // The first frame on animations doesn't always trigger an invalidate... 10839b599e0641047e5583cf717d35a4cb080049665Michael Jurka // force an invalidate here to make sure the animation continues to advance 10939b599e0641047e5583cf717d35a4cb080049665Michael Jurka mTarget.getRootView().invalidate(); 110f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka animation.setCurrentPlayTime(0); 111f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka 112f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka // For the second frame, if the first frame took more than 16ms, 113f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka // adjust the start time and pretend it took only 16ms anyway. This 114f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka // prevents a large jump in the animation due to an expensive first frame 11539b599e0641047e5583cf717d35a4cb080049665Michael Jurka } else if (frameNum == 1 && currentTime < mStartTime + MAX_DELAY && 11639b599e0641047e5583cf717d35a4cb080049665Michael Jurka !mAdjustedSecondFrameTime && 11739b599e0641047e5583cf717d35a4cb080049665Michael Jurka currentTime > mStartTime + IDEAL_FRAME_DURATION) { 118f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka animation.setCurrentPlayTime(IDEAL_FRAME_DURATION); 119f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka mAdjustedSecondFrameTime = true; 120f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka } else { 121f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka if (frameNum > 1) { 12298720c96ee8c1eab026903dd83507a3cc50de900Michael Jurka mTarget.post(new Runnable() { 12398720c96ee8c1eab026903dd83507a3cc50de900Michael Jurka public void run() { 12498720c96ee8c1eab026903dd83507a3cc50de900Michael Jurka animation.removeUpdateListener(FirstFrameAnimatorHelper.this); 12598720c96ee8c1eab026903dd83507a3cc50de900Michael Jurka } 12698720c96ee8c1eab026903dd83507a3cc50de900Michael Jurka }); 127f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka } 128f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka if (DEBUG) print(animation); 129f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka } 130f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka mHandlingOnAnimationUpdate = false; 131f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka } else { 132f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka if (DEBUG) print(animation); 133f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka } 134f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka } 135f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka 136f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka public void print(ValueAnimator animation) { 137f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka float flatFraction = animation.getCurrentPlayTime() / (float) animation.getDuration(); 138f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka Log.d("FirstFrameAnimatorHelper", sGlobalFrameCounter + 139f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka "(" + (sGlobalFrameCounter - mStartFrame) + ") " + mTarget + " dirty? " + 140f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka mTarget.isDirty() + " " + flatFraction + " " + this + " " + animation); 141f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka } 142f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka} 143