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.util.Log;
23f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurkaimport android.view.View;
2439b599e0641047e5583cf717d35a4cb080049665Michael Jurkaimport android.view.ViewPropertyAnimator;
25032e6bad1ba588ed8a884fb8f28fb8596efac480Michael Jurkaimport android.view.ViewTreeObserver;
26f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka
27f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka/*
28f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka *  This is a helper class that listens to updates from the corresponding animation.
29f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka *  For the first two frames, it adjusts the current play time of the animation to
30f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka *  prevent jank at the beginning of the animation
31f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka */
3239b599e0641047e5583cf717d35a4cb080049665Michael Jurkapublic class FirstFrameAnimatorHelper extends AnimatorListenerAdapter
3339b599e0641047e5583cf717d35a4cb080049665Michael Jurka    implements ValueAnimator.AnimatorUpdateListener {
34f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka    private static final boolean DEBUG = false;
3539b599e0641047e5583cf717d35a4cb080049665Michael Jurka    private static final int MAX_DELAY = 1000;
36f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka    private static final int IDEAL_FRAME_DURATION = 16;
37f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka    private View mTarget;
38f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka    private long mStartFrame;
39f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka    private long mStartTime = -1;
40f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka    private boolean mHandlingOnAnimationUpdate;
41f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka    private boolean mAdjustedSecondFrameTime;
42f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka
43f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka    private static ViewTreeObserver.OnDrawListener sGlobalDrawListener;
44f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka    private static long sGlobalFrameCounter;
45cd496d723ca6b4b100771ae66f09007e7acf0d17Michael Jurka    private static boolean sVisible;
46f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka
47f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka    public FirstFrameAnimatorHelper(ValueAnimator animator, View target) {
48f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka        mTarget = target;
49f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka        animator.addUpdateListener(this);
50f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka    }
51f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka
5239b599e0641047e5583cf717d35a4cb080049665Michael Jurka    public FirstFrameAnimatorHelper(ViewPropertyAnimator vpa, View target) {
5339b599e0641047e5583cf717d35a4cb080049665Michael Jurka        mTarget = target;
5439b599e0641047e5583cf717d35a4cb080049665Michael Jurka        vpa.setListener(this);
5539b599e0641047e5583cf717d35a4cb080049665Michael Jurka    }
5639b599e0641047e5583cf717d35a4cb080049665Michael Jurka
5739b599e0641047e5583cf717d35a4cb080049665Michael Jurka    // only used for ViewPropertyAnimators
5839b599e0641047e5583cf717d35a4cb080049665Michael Jurka    public void onAnimationStart(Animator animation) {
5939b599e0641047e5583cf717d35a4cb080049665Michael Jurka        final ValueAnimator va = (ValueAnimator) animation;
6039b599e0641047e5583cf717d35a4cb080049665Michael Jurka        va.addUpdateListener(FirstFrameAnimatorHelper.this);
6139b599e0641047e5583cf717d35a4cb080049665Michael Jurka        onAnimationUpdate(va);
6239b599e0641047e5583cf717d35a4cb080049665Michael Jurka    }
6339b599e0641047e5583cf717d35a4cb080049665Michael Jurka
64cd496d723ca6b4b100771ae66f09007e7acf0d17Michael Jurka    public static void setIsVisible(boolean visible) {
65cd496d723ca6b4b100771ae66f09007e7acf0d17Michael Jurka        sVisible = visible;
66cd496d723ca6b4b100771ae66f09007e7acf0d17Michael Jurka    }
67cd496d723ca6b4b100771ae66f09007e7acf0d17Michael Jurka
68f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka    public static void initializeDrawListener(View view) {
6939b599e0641047e5583cf717d35a4cb080049665Michael Jurka        if (sGlobalDrawListener != null) {
7039b599e0641047e5583cf717d35a4cb080049665Michael Jurka            view.getViewTreeObserver().removeOnDrawListener(sGlobalDrawListener);
7139b599e0641047e5583cf717d35a4cb080049665Michael Jurka        }
72f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka        sGlobalDrawListener = new ViewTreeObserver.OnDrawListener() {
73f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka                private long mTime = System.currentTimeMillis();
74f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka                public void onDraw() {
75f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka                    sGlobalFrameCounter++;
76f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka                    if (DEBUG) {
7739b599e0641047e5583cf717d35a4cb080049665Michael Jurka                        long newTime = System.currentTimeMillis();
78f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka                        Log.d("FirstFrameAnimatorHelper", "TICK " + (newTime - mTime));
7939b599e0641047e5583cf717d35a4cb080049665Michael Jurka                        mTime = newTime;
80f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka                    }
81f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka                }
82f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka            };
83f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka        view.getViewTreeObserver().addOnDrawListener(sGlobalDrawListener);
84cd496d723ca6b4b100771ae66f09007e7acf0d17Michael Jurka        sVisible = true;
85f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka    }
86f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka
8798720c96ee8c1eab026903dd83507a3cc50de900Michael Jurka    public void onAnimationUpdate(final ValueAnimator animation) {
8839b599e0641047e5583cf717d35a4cb080049665Michael Jurka        final long currentTime = System.currentTimeMillis();
89f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka        if (mStartTime == -1) {
90f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka            mStartFrame = sGlobalFrameCounter;
9139b599e0641047e5583cf717d35a4cb080049665Michael Jurka            mStartTime = currentTime;
92f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka        }
93f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka
9439b599e0641047e5583cf717d35a4cb080049665Michael Jurka        if (!mHandlingOnAnimationUpdate &&
95cd496d723ca6b4b100771ae66f09007e7acf0d17Michael Jurka            sVisible &&
9639b599e0641047e5583cf717d35a4cb080049665Michael Jurka            // If the current play time exceeds the duration, the animation
9739b599e0641047e5583cf717d35a4cb080049665Michael Jurka            // will get finished, even if we call setCurrentPlayTime -- therefore
9839b599e0641047e5583cf717d35a4cb080049665Michael Jurka            // don't adjust the animation in that case
9939b599e0641047e5583cf717d35a4cb080049665Michael Jurka            animation.getCurrentPlayTime() < animation.getDuration()) {
100f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka            mHandlingOnAnimationUpdate = true;
101f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka            long frameNum = sGlobalFrameCounter - mStartFrame;
102f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka            // If we haven't drawn our first frame, reset the time to t = 0
10339b599e0641047e5583cf717d35a4cb080049665Michael Jurka            // (give up after MAX_DELAY ms of waiting though - might happen, for example, if we
10439b599e0641047e5583cf717d35a4cb080049665Michael Jurka            // are no longer in the foreground and no frames are being rendered ever)
10539b599e0641047e5583cf717d35a4cb080049665Michael Jurka            if (frameNum == 0 && currentTime < mStartTime + MAX_DELAY) {
10639b599e0641047e5583cf717d35a4cb080049665Michael Jurka                // The first frame on animations doesn't always trigger an invalidate...
10739b599e0641047e5583cf717d35a4cb080049665Michael Jurka                // force an invalidate here to make sure the animation continues to advance
10839b599e0641047e5583cf717d35a4cb080049665Michael Jurka                mTarget.getRootView().invalidate();
109f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka                animation.setCurrentPlayTime(0);
110f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka
111f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka            // For the second frame, if the first frame took more than 16ms,
112f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka            // adjust the start time and pretend it took only 16ms anyway. This
113f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka            // prevents a large jump in the animation due to an expensive first frame
11439b599e0641047e5583cf717d35a4cb080049665Michael Jurka            } else if (frameNum == 1 && currentTime < mStartTime + MAX_DELAY &&
11539b599e0641047e5583cf717d35a4cb080049665Michael Jurka                       !mAdjustedSecondFrameTime &&
11639b599e0641047e5583cf717d35a4cb080049665Michael Jurka                       currentTime > mStartTime + IDEAL_FRAME_DURATION) {
117f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka                animation.setCurrentPlayTime(IDEAL_FRAME_DURATION);
118f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka                mAdjustedSecondFrameTime = true;
119f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka            } else {
120f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka                if (frameNum > 1) {
12198720c96ee8c1eab026903dd83507a3cc50de900Michael Jurka                    mTarget.post(new Runnable() {
12298720c96ee8c1eab026903dd83507a3cc50de900Michael Jurka                            public void run() {
12398720c96ee8c1eab026903dd83507a3cc50de900Michael Jurka                                animation.removeUpdateListener(FirstFrameAnimatorHelper.this);
12498720c96ee8c1eab026903dd83507a3cc50de900Michael Jurka                            }
12598720c96ee8c1eab026903dd83507a3cc50de900Michael Jurka                        });
126f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka                }
127f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka                if (DEBUG) print(animation);
128f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka            }
129f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka            mHandlingOnAnimationUpdate = false;
130f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka        } else {
131f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka            if (DEBUG) print(animation);
132f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka        }
133f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka    }
134f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka
135f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka    public void print(ValueAnimator animation) {
136f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka        float flatFraction = animation.getCurrentPlayTime() / (float) animation.getDuration();
137f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka        Log.d("FirstFrameAnimatorHelper", sGlobalFrameCounter +
138f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka              "(" + (sGlobalFrameCounter - mStartFrame) + ") " + mTarget + " dirty? " +
139f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka              mTarget.isDirty() + " " + flatFraction + " " + this + " " + animation);
140f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka    }
141f1ad608c28c79c8e9b83d83ce9154f1b7284f412Michael Jurka}
142