2013-03-13 12:55:46 +01:00
|
|
|
/*
|
|
|
|
|
* Copyright (C) 2013 The Android Open Source Project
|
|
|
|
|
*
|
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
|
*
|
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
*
|
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
|
* limitations under the License.
|
|
|
|
|
*/
|
|
|
|
|
|
2013-06-05 22:57:57 -04:00
|
|
|
package com.android.launcher3;
|
2013-03-13 12:55:46 +01:00
|
|
|
|
2013-04-08 18:28:15 -07:00
|
|
|
import android.animation.Animator;
|
|
|
|
|
import android.animation.AnimatorListenerAdapter;
|
2013-03-13 12:55:46 +01:00
|
|
|
import android.animation.ValueAnimator;
|
|
|
|
|
import android.util.Log;
|
|
|
|
|
import android.view.View;
|
2013-04-08 18:28:15 -07:00
|
|
|
import android.view.ViewPropertyAnimator;
|
2013-04-22 15:08:42 +02:00
|
|
|
import android.view.ViewTreeObserver;
|
2013-03-13 12:55:46 +01:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* This is a helper class that listens to updates from the corresponding animation.
|
|
|
|
|
* For the first two frames, it adjusts the current play time of the animation to
|
|
|
|
|
* prevent jank at the beginning of the animation
|
|
|
|
|
*/
|
2013-04-08 18:28:15 -07:00
|
|
|
public class FirstFrameAnimatorHelper extends AnimatorListenerAdapter
|
|
|
|
|
implements ValueAnimator.AnimatorUpdateListener {
|
2013-03-13 12:55:46 +01:00
|
|
|
private static final boolean DEBUG = false;
|
2013-04-08 18:28:15 -07:00
|
|
|
private static final int MAX_DELAY = 1000;
|
2013-03-13 12:55:46 +01:00
|
|
|
private static final int IDEAL_FRAME_DURATION = 16;
|
|
|
|
|
private View mTarget;
|
|
|
|
|
private long mStartFrame;
|
|
|
|
|
private long mStartTime = -1;
|
|
|
|
|
private boolean mHandlingOnAnimationUpdate;
|
|
|
|
|
private boolean mAdjustedSecondFrameTime;
|
|
|
|
|
|
|
|
|
|
private static ViewTreeObserver.OnDrawListener sGlobalDrawListener;
|
|
|
|
|
private static long sGlobalFrameCounter;
|
2013-04-11 11:32:45 -07:00
|
|
|
private static boolean sVisible;
|
2013-03-13 12:55:46 +01:00
|
|
|
|
|
|
|
|
public FirstFrameAnimatorHelper(ValueAnimator animator, View target) {
|
|
|
|
|
mTarget = target;
|
|
|
|
|
animator.addUpdateListener(this);
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-08 18:28:15 -07:00
|
|
|
public FirstFrameAnimatorHelper(ViewPropertyAnimator vpa, View target) {
|
|
|
|
|
mTarget = target;
|
|
|
|
|
vpa.setListener(this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// only used for ViewPropertyAnimators
|
|
|
|
|
public void onAnimationStart(Animator animation) {
|
|
|
|
|
final ValueAnimator va = (ValueAnimator) animation;
|
|
|
|
|
va.addUpdateListener(FirstFrameAnimatorHelper.this);
|
|
|
|
|
onAnimationUpdate(va);
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-11 11:32:45 -07:00
|
|
|
public static void setIsVisible(boolean visible) {
|
|
|
|
|
sVisible = visible;
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-13 12:55:46 +01:00
|
|
|
public static void initializeDrawListener(View view) {
|
2013-04-08 18:28:15 -07:00
|
|
|
if (sGlobalDrawListener != null) {
|
|
|
|
|
view.getViewTreeObserver().removeOnDrawListener(sGlobalDrawListener);
|
|
|
|
|
}
|
2013-03-13 12:55:46 +01:00
|
|
|
sGlobalDrawListener = new ViewTreeObserver.OnDrawListener() {
|
|
|
|
|
private long mTime = System.currentTimeMillis();
|
|
|
|
|
public void onDraw() {
|
|
|
|
|
sGlobalFrameCounter++;
|
|
|
|
|
if (DEBUG) {
|
2013-04-08 18:28:15 -07:00
|
|
|
long newTime = System.currentTimeMillis();
|
2013-03-13 12:55:46 +01:00
|
|
|
Log.d("FirstFrameAnimatorHelper", "TICK " + (newTime - mTime));
|
2013-04-08 18:28:15 -07:00
|
|
|
mTime = newTime;
|
2013-03-13 12:55:46 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
view.getViewTreeObserver().addOnDrawListener(sGlobalDrawListener);
|
2013-04-11 11:32:45 -07:00
|
|
|
sVisible = true;
|
2013-03-13 12:55:46 +01:00
|
|
|
}
|
|
|
|
|
|
2013-03-28 13:39:35 -07:00
|
|
|
public void onAnimationUpdate(final ValueAnimator animation) {
|
2013-04-08 18:28:15 -07:00
|
|
|
final long currentTime = System.currentTimeMillis();
|
2013-03-13 12:55:46 +01:00
|
|
|
if (mStartTime == -1) {
|
|
|
|
|
mStartFrame = sGlobalFrameCounter;
|
2013-04-08 18:28:15 -07:00
|
|
|
mStartTime = currentTime;
|
2013-03-13 12:55:46 +01:00
|
|
|
}
|
|
|
|
|
|
2013-11-06 17:06:57 +01:00
|
|
|
final long currentPlayTime = animation.getCurrentPlayTime();
|
2014-12-02 15:24:52 -08:00
|
|
|
boolean isFinalFrame = Float.compare(1f, animation.getAnimatedFraction()) == 0;
|
|
|
|
|
|
2013-04-08 18:28:15 -07:00
|
|
|
if (!mHandlingOnAnimationUpdate &&
|
2013-04-11 11:32:45 -07:00
|
|
|
sVisible &&
|
2014-12-02 15:24:52 -08:00
|
|
|
// If the current play time exceeds the duration, or the animated fraction is 1,
|
|
|
|
|
// the animation will get finished, even if we call setCurrentPlayTime -- therefore
|
2013-04-08 18:28:15 -07:00
|
|
|
// don't adjust the animation in that case
|
2014-12-02 15:24:52 -08:00
|
|
|
currentPlayTime < animation.getDuration() && !isFinalFrame) {
|
2013-03-13 12:55:46 +01:00
|
|
|
mHandlingOnAnimationUpdate = true;
|
|
|
|
|
long frameNum = sGlobalFrameCounter - mStartFrame;
|
|
|
|
|
// If we haven't drawn our first frame, reset the time to t = 0
|
2013-04-08 18:28:15 -07:00
|
|
|
// (give up after MAX_DELAY ms of waiting though - might happen, for example, if we
|
|
|
|
|
// are no longer in the foreground and no frames are being rendered ever)
|
2013-11-06 17:06:57 +01:00
|
|
|
if (frameNum == 0 && currentTime < mStartTime + MAX_DELAY && currentPlayTime > 0) {
|
2013-04-08 18:28:15 -07:00
|
|
|
// The first frame on animations doesn't always trigger an invalidate...
|
|
|
|
|
// force an invalidate here to make sure the animation continues to advance
|
|
|
|
|
mTarget.getRootView().invalidate();
|
2013-03-13 12:55:46 +01:00
|
|
|
animation.setCurrentPlayTime(0);
|
|
|
|
|
// For the second frame, if the first frame took more than 16ms,
|
|
|
|
|
// adjust the start time and pretend it took only 16ms anyway. This
|
|
|
|
|
// prevents a large jump in the animation due to an expensive first frame
|
2013-04-08 18:28:15 -07:00
|
|
|
} else if (frameNum == 1 && currentTime < mStartTime + MAX_DELAY &&
|
|
|
|
|
!mAdjustedSecondFrameTime &&
|
2013-11-06 17:06:57 +01:00
|
|
|
currentTime > mStartTime + IDEAL_FRAME_DURATION &&
|
|
|
|
|
currentPlayTime > IDEAL_FRAME_DURATION) {
|
2013-03-13 12:55:46 +01:00
|
|
|
animation.setCurrentPlayTime(IDEAL_FRAME_DURATION);
|
|
|
|
|
mAdjustedSecondFrameTime = true;
|
|
|
|
|
} else {
|
|
|
|
|
if (frameNum > 1) {
|
2013-03-28 13:39:35 -07:00
|
|
|
mTarget.post(new Runnable() {
|
|
|
|
|
public void run() {
|
|
|
|
|
animation.removeUpdateListener(FirstFrameAnimatorHelper.this);
|
|
|
|
|
}
|
|
|
|
|
});
|
2013-03-13 12:55:46 +01:00
|
|
|
}
|
|
|
|
|
if (DEBUG) print(animation);
|
|
|
|
|
}
|
|
|
|
|
mHandlingOnAnimationUpdate = false;
|
|
|
|
|
} else {
|
|
|
|
|
if (DEBUG) print(animation);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void print(ValueAnimator animation) {
|
|
|
|
|
float flatFraction = animation.getCurrentPlayTime() / (float) animation.getDuration();
|
|
|
|
|
Log.d("FirstFrameAnimatorHelper", sGlobalFrameCounter +
|
|
|
|
|
"(" + (sGlobalFrameCounter - mStartFrame) + ") " + mTarget + " dirty? " +
|
|
|
|
|
mTarget.isDirty() + " " + flatFraction + " " + this + " " + animation);
|
|
|
|
|
}
|
|
|
|
|
}
|