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