Merge "Fix janky icon fade-in animation in Recents" into jb-mr2-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
104ca45a04
@ -0,0 +1,129 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.android.systemui.recent;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.animation.Animator.AnimatorListener;
|
||||
import android.util.Log;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.view.View;
|
||||
import android.view.ViewPropertyAnimator;
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
public class FirstFrameAnimatorHelper implements ValueAnimator.AnimatorUpdateListener {
|
||||
private static final boolean DEBUG = false;
|
||||
private static final int MAX_DELAY = 1000;
|
||||
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;
|
||||
|
||||
public FirstFrameAnimatorHelper(ValueAnimator animator, View target) {
|
||||
mTarget = target;
|
||||
animator.addUpdateListener(this);
|
||||
}
|
||||
|
||||
public FirstFrameAnimatorHelper(ViewPropertyAnimator vpa, View target) {
|
||||
mTarget = target;
|
||||
vpa.setListener(new AnimatorListenerAdapter() {
|
||||
public void onAnimationStart (Animator animation) {
|
||||
final ValueAnimator va = (ValueAnimator) animation;
|
||||
va.addUpdateListener(FirstFrameAnimatorHelper.this);
|
||||
onAnimationUpdate(va);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void initializeDrawListener(View view) {
|
||||
if (sGlobalDrawListener != null) {
|
||||
view.getViewTreeObserver().removeOnDrawListener(sGlobalDrawListener);
|
||||
}
|
||||
sGlobalDrawListener = new ViewTreeObserver.OnDrawListener() {
|
||||
private long mTime = System.currentTimeMillis();
|
||||
public void onDraw() {
|
||||
sGlobalFrameCounter++;
|
||||
if (DEBUG) {
|
||||
long newTime = System.currentTimeMillis();
|
||||
Log.d("FirstFrameAnimatorHelper", "TICK " + (newTime - mTime));
|
||||
mTime = newTime;
|
||||
}
|
||||
}
|
||||
};
|
||||
view.getViewTreeObserver().addOnDrawListener(sGlobalDrawListener);
|
||||
}
|
||||
|
||||
public void onAnimationUpdate(final ValueAnimator animation) {
|
||||
final long currentTime = System.currentTimeMillis();
|
||||
if (mStartTime == -1) {
|
||||
mStartFrame = sGlobalFrameCounter;
|
||||
mStartTime = currentTime;
|
||||
}
|
||||
|
||||
if (!mHandlingOnAnimationUpdate) {
|
||||
mHandlingOnAnimationUpdate = true;
|
||||
long frameNum = sGlobalFrameCounter - mStartFrame;
|
||||
// If we haven't drawn our first frame, reset the time to t = 0
|
||||
// (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)
|
||||
if (frameNum == 0 && currentTime < mStartTime + MAX_DELAY) {
|
||||
// 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();
|
||||
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
|
||||
} else if (frameNum == 1 && currentTime < mStartTime + MAX_DELAY &&
|
||||
!mAdjustedSecondFrameTime &&
|
||||
currentTime > mStartTime + IDEAL_FRAME_DURATION) {
|
||||
animation.setCurrentPlayTime(IDEAL_FRAME_DURATION);
|
||||
mAdjustedSecondFrameTime = true;
|
||||
} else {
|
||||
if (frameNum > 1) {
|
||||
mTarget.post(new Runnable() {
|
||||
public void run() {
|
||||
animation.removeUpdateListener(FirstFrameAnimatorHelper.this);
|
||||
}
|
||||
});
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
@ -45,8 +45,7 @@ import android.view.MenuItem;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
|
||||
import android.view.ViewPropertyAnimator;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
@ -181,25 +180,6 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener
|
||||
}
|
||||
if (index == 0) {
|
||||
if (mAnimateIconOfFirstTask) {
|
||||
ViewHolder oldHolder = mItemToAnimateInWhenWindowAnimationIsFinished;
|
||||
if (oldHolder != null) {
|
||||
oldHolder.iconView.setAlpha(1f);
|
||||
oldHolder.iconView.setTranslationX(0f);
|
||||
oldHolder.iconView.setTranslationY(0f);
|
||||
oldHolder.labelView.setAlpha(1f);
|
||||
oldHolder.labelView.setTranslationX(0f);
|
||||
oldHolder.labelView.setTranslationY(0f);
|
||||
if (oldHolder.calloutLine != null) {
|
||||
oldHolder.calloutLine.setAlpha(1f);
|
||||
oldHolder.calloutLine.setTranslationX(0f);
|
||||
oldHolder.calloutLine.setTranslationY(0f);
|
||||
}
|
||||
}
|
||||
mItemToAnimateInWhenWindowAnimationIsFinished = null;
|
||||
|
||||
final ViewTreeObserver observer = getViewTreeObserver();
|
||||
final OnGlobalLayoutListener animateFirstIcon = new OnGlobalLayoutListener() {
|
||||
public void onGlobalLayout() {
|
||||
ViewHolder oldHolder = mItemToAnimateInWhenWindowAnimationIsFinished;
|
||||
if (oldHolder != null) {
|
||||
oldHolder.iconView.setAlpha(1f);
|
||||
@ -235,10 +215,6 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener
|
||||
if (!mWaitingForWindowAnimation) {
|
||||
animateInIconOfFirstTask();
|
||||
}
|
||||
getViewTreeObserver().removeOnGlobalLayoutListener(this);
|
||||
}
|
||||
};
|
||||
observer.addOnGlobalLayoutListener(animateFirstIcon);
|
||||
}
|
||||
}
|
||||
|
||||
@ -586,17 +562,20 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener
|
||||
!mRecentTasksLoader.isFirstScreenful()) {
|
||||
int timeSinceWindowAnimation =
|
||||
(int) (System.currentTimeMillis() - mWindowAnimationStartTime);
|
||||
final int minStartDelay = 150;
|
||||
final int minStartDelay = 125;
|
||||
final int startDelay = Math.max(0, Math.min(
|
||||
minStartDelay - timeSinceWindowAnimation, minStartDelay));
|
||||
final int duration = 250;
|
||||
final ViewHolder holder = mItemToAnimateInWhenWindowAnimationIsFinished;
|
||||
final TimeInterpolator cubic = new DecelerateInterpolator(1.5f);
|
||||
FirstFrameAnimatorHelper.initializeDrawListener(holder.iconView);
|
||||
for (View v :
|
||||
new View[] { holder.iconView, holder.labelView, holder.calloutLine }) {
|
||||
if (v != null) {
|
||||
v.animate().translationX(0).translationY(0).alpha(1f).setStartDelay(startDelay)
|
||||
ViewPropertyAnimator vpa = v.animate().translationX(0).translationY(0)
|
||||
.alpha(1f).setStartDelay(startDelay)
|
||||
.setDuration(duration).setInterpolator(cubic);
|
||||
FirstFrameAnimatorHelper h = new FirstFrameAnimatorHelper(vpa, v);
|
||||
}
|
||||
}
|
||||
mItemToAnimateInWhenWindowAnimationIsFinished = null;
|
||||
|
Reference in New Issue
Block a user