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