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