Merge "Make notification panel delete-all animation smoother" into ics-mr0
This commit is contained in:
@@ -108,6 +108,7 @@ public final class ViewRootImpl extends Handler implements ViewParent,
|
|||||||
private static final boolean DEBUG_TRACKBALL = false || LOCAL_LOGV;
|
private static final boolean DEBUG_TRACKBALL = false || LOCAL_LOGV;
|
||||||
private static final boolean DEBUG_IMF = false || LOCAL_LOGV;
|
private static final boolean DEBUG_IMF = false || LOCAL_LOGV;
|
||||||
private static final boolean DEBUG_CONFIGURATION = false || LOCAL_LOGV;
|
private static final boolean DEBUG_CONFIGURATION = false || LOCAL_LOGV;
|
||||||
|
private static final boolean DEBUG_FPS = false;
|
||||||
private static final boolean WATCH_POINTER = false;
|
private static final boolean WATCH_POINTER = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -274,6 +275,11 @@ public final class ViewRootImpl extends Handler implements ViewParent,
|
|||||||
private Thread mRenderProfiler;
|
private Thread mRenderProfiler;
|
||||||
private volatile boolean mRenderProfilingEnabled;
|
private volatile boolean mRenderProfilingEnabled;
|
||||||
|
|
||||||
|
// Variables to track frames per second, enabled via DEBUG_FPS flag
|
||||||
|
private long mFpsStartTime = -1;
|
||||||
|
private long mFpsPrevTime = -1;
|
||||||
|
private int mFpsNumFrames;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* see {@link #playSoundEffect(int)}
|
* see {@link #playSoundEffect(int)}
|
||||||
*/
|
*/
|
||||||
@@ -1766,12 +1772,42 @@ public final class ViewRootImpl extends Handler implements ViewParent,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called from draw() when DEBUG_FPS is enabled
|
||||||
|
*/
|
||||||
|
private void trackFPS() {
|
||||||
|
// Tracks frames per second drawn. First value in a series of draws may be bogus
|
||||||
|
// because it down not account for the intervening idle time
|
||||||
|
long nowTime = System.currentTimeMillis();
|
||||||
|
if (mFpsStartTime < 0) {
|
||||||
|
mFpsStartTime = mFpsPrevTime = nowTime;
|
||||||
|
mFpsNumFrames = 0;
|
||||||
|
} else {
|
||||||
|
++mFpsNumFrames;
|
||||||
|
String thisHash = Integer.toHexString(System.identityHashCode(this));
|
||||||
|
long frameTime = nowTime - mFpsPrevTime;
|
||||||
|
long totalTime = nowTime - mFpsStartTime;
|
||||||
|
Log.v(TAG, "0x" + thisHash + "\tFrame time:\t" + frameTime);
|
||||||
|
mFpsPrevTime = nowTime;
|
||||||
|
if (totalTime > 1000) {
|
||||||
|
float fps = (float) mFpsNumFrames * 1000 / totalTime;
|
||||||
|
Log.v(TAG, "0x" + thisHash + "\tFPS:\t" + fps);
|
||||||
|
mFpsStartTime = nowTime;
|
||||||
|
mFpsNumFrames = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void draw(boolean fullRedrawNeeded) {
|
private void draw(boolean fullRedrawNeeded) {
|
||||||
Surface surface = mSurface;
|
Surface surface = mSurface;
|
||||||
if (surface == null || !surface.isValid()) {
|
if (surface == null || !surface.isValid()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (DEBUG_FPS) {
|
||||||
|
trackFPS();
|
||||||
|
}
|
||||||
|
|
||||||
if (!sFirstDrawComplete) {
|
if (!sFirstDrawComplete) {
|
||||||
synchronized (sFirstDrawHandlers) {
|
synchronized (sFirstDrawHandlers) {
|
||||||
sFirstDrawComplete = true;
|
sFirstDrawComplete = true;
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package com.android.systemui;
|
package com.android.systemui;
|
||||||
|
|
||||||
import android.animation.Animator;
|
import android.animation.Animator;
|
||||||
|
import android.animation.AnimatorListenerAdapter;
|
||||||
import android.animation.ObjectAnimator;
|
import android.animation.ObjectAnimator;
|
||||||
import android.animation.Animator.AnimatorListener;
|
import android.animation.Animator.AnimatorListener;
|
||||||
import android.animation.ValueAnimator;
|
import android.animation.ValueAnimator;
|
||||||
@@ -40,6 +41,8 @@ public class SwipeHelper {
|
|||||||
public static final int X = 0;
|
public static final int X = 0;
|
||||||
public static final int Y = 1;
|
public static final int Y = 1;
|
||||||
|
|
||||||
|
private static LinearInterpolator sLinearInterpolator = new LinearInterpolator();
|
||||||
|
|
||||||
private float SWIPE_ESCAPE_VELOCITY = 100f; // dp/sec
|
private float SWIPE_ESCAPE_VELOCITY = 100f; // dp/sec
|
||||||
private int DEFAULT_ESCAPE_ANIMATION_DURATION = 200; // ms
|
private int DEFAULT_ESCAPE_ANIMATION_DURATION = 200; // ms
|
||||||
private int MAX_ESCAPE_ANIMATION_DURATION = 400; // ms
|
private int MAX_ESCAPE_ANIMATION_DURATION = 400; // ms
|
||||||
@@ -199,6 +202,10 @@ public class SwipeHelper {
|
|||||||
return mDragging;
|
return mDragging;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param view The view to be dismissed
|
||||||
|
* @param velocity The desired pixels/second speed at which the view should move
|
||||||
|
*/
|
||||||
public void dismissChild(final View view, float velocity) {
|
public void dismissChild(final View view, float velocity) {
|
||||||
final View animView = mCallback.getChildContentView(view);
|
final View animView = mCallback.getChildContentView(view);
|
||||||
final boolean canAnimViewBeDismissed = mCallback.canChildBeDismissed(view);
|
final boolean canAnimViewBeDismissed = mCallback.canChildBeDismissed(view);
|
||||||
@@ -221,22 +228,14 @@ public class SwipeHelper {
|
|||||||
duration = DEFAULT_ESCAPE_ANIMATION_DURATION;
|
duration = DEFAULT_ESCAPE_ANIMATION_DURATION;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
animView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
|
||||||
ObjectAnimator anim = createTranslationAnimation(animView, newPos);
|
ObjectAnimator anim = createTranslationAnimation(animView, newPos);
|
||||||
anim.setInterpolator(new LinearInterpolator());
|
anim.setInterpolator(sLinearInterpolator);
|
||||||
anim.setDuration(duration);
|
anim.setDuration(duration);
|
||||||
anim.addListener(new AnimatorListener() {
|
anim.addListener(new AnimatorListenerAdapter() {
|
||||||
public void onAnimationStart(Animator animation) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onAnimationRepeat(Animator animation) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onAnimationEnd(Animator animation) {
|
public void onAnimationEnd(Animator animation) {
|
||||||
mCallback.onChildDismissed(view);
|
mCallback.onChildDismissed(view);
|
||||||
}
|
animView.setLayerType(View.LAYER_TYPE_NONE, null);
|
||||||
|
|
||||||
public void onAnimationCancel(Animator animation) {
|
|
||||||
mCallback.onChildDismissed(view);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
anim.addUpdateListener(new AnimatorUpdateListener() {
|
anim.addUpdateListener(new AnimatorUpdateListener() {
|
||||||
|
|||||||
@@ -50,7 +50,6 @@ import android.view.IWindowManager;
|
|||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.Surface;
|
|
||||||
import android.view.VelocityTracker;
|
import android.view.VelocityTracker;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@@ -213,6 +212,8 @@ public class PhoneStatusBar extends StatusBar {
|
|||||||
boolean mAnimatingReveal = false;
|
boolean mAnimatingReveal = false;
|
||||||
int mViewDelta;
|
int mViewDelta;
|
||||||
int[] mAbsPos = new int[2];
|
int[] mAbsPos = new int[2];
|
||||||
|
Runnable mPostCollapseCleanup = null;
|
||||||
|
|
||||||
|
|
||||||
// for disabling the status bar
|
// for disabling the status bar
|
||||||
int mDisabled = 0;
|
int mDisabled = 0;
|
||||||
@@ -1238,6 +1239,10 @@ public class PhoneStatusBar extends StatusBar {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mExpanded = false;
|
mExpanded = false;
|
||||||
|
if (mPostCollapseCleanup != null) {
|
||||||
|
mPostCollapseCleanup.run();
|
||||||
|
mPostCollapseCleanup = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void doAnimation() {
|
void doAnimation() {
|
||||||
@@ -2066,49 +2071,67 @@ public class PhoneStatusBar extends StatusBar {
|
|||||||
}
|
}
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
synchronized (mNotificationData) {
|
synchronized (mNotificationData) {
|
||||||
// let's also queue up 400ms worth of animated dismissals
|
// animate-swipe all dismissable notifications, then animate the shade closed
|
||||||
final int N = mini(5, mPile.getChildCount());
|
int numChildren = mPile.getChildCount();
|
||||||
|
|
||||||
final ArrayList<View> snapshot = new ArrayList<View>(N);
|
int scrollTop = mScrollView.getScrollY();
|
||||||
for (int i=0; i<N; i++) {
|
int scrollBottom = scrollTop + mScrollView.getHeight();
|
||||||
|
final ArrayList<View> snapshot = new ArrayList<View>(numChildren);
|
||||||
|
for (int i=0; i<numChildren; i++) {
|
||||||
final View child = mPile.getChildAt(i);
|
final View child = mPile.getChildAt(i);
|
||||||
if (mPile.canChildBeDismissed(child)) snapshot.add(child);
|
if (mPile.canChildBeDismissed(child) && child.getBottom() > scrollTop &&
|
||||||
|
child.getTop() < scrollBottom) {
|
||||||
|
snapshot.add(child);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
final int N = snapshot.size();
|
||||||
new Thread(new Runnable() {
|
new Thread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
final int ROW_DELAY = 100;
|
// Decrease the delay for every row we animate to give the sense of
|
||||||
|
// accelerating the swipes
|
||||||
|
final int ROW_DELAY_DECREMENT = 10;
|
||||||
|
int currentDelay = 140;
|
||||||
|
int totalDelay = 0;
|
||||||
|
|
||||||
mHandler.postDelayed(new Runnable() {
|
// Set the shade-animating state to avoid doing other work during
|
||||||
public void run() {
|
// all of these animations. In particular, avoid layout and
|
||||||
animateCollapse(false, 0f);
|
// redrawing when collapsing the shade.
|
||||||
}
|
mPile.setViewRemoval(false);
|
||||||
}, (N-1) * ROW_DELAY);
|
|
||||||
|
|
||||||
mHandler.postDelayed(new Runnable() {
|
mPostCollapseCleanup = new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
|
mPile.setViewRemoval(true);
|
||||||
mBarService.onClearAllNotifications();
|
mBarService.onClearAllNotifications();
|
||||||
} catch (RemoteException ex) { }
|
} catch (Exception ex) { }
|
||||||
}
|
}
|
||||||
}, N * ROW_DELAY + 500);
|
};
|
||||||
|
|
||||||
mPile.setAnimateBounds(false); // temporarily disable some re-layouts
|
|
||||||
|
|
||||||
|
View sampleView = snapshot.get(0);
|
||||||
|
int width = sampleView.getWidth();
|
||||||
|
final int velocity = (int)(width * 8); // 1000/8 = 125 ms duration
|
||||||
for (View v : snapshot) {
|
for (View v : snapshot) {
|
||||||
final View _v = v;
|
final View _v = v;
|
||||||
mHandler.post(new Runnable() {
|
mHandler.postDelayed(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
mPile.dismissRowAnimated(_v, (int)(ROW_DELAY*0.25f));
|
mPile.dismissRowAnimated(_v, velocity);
|
||||||
}
|
}
|
||||||
});
|
}, totalDelay);
|
||||||
try {
|
currentDelay = Math.max(50, currentDelay - ROW_DELAY_DECREMENT);
|
||||||
Thread.sleep(ROW_DELAY);
|
totalDelay += currentDelay;
|
||||||
} catch (InterruptedException ex) { }
|
|
||||||
}
|
}
|
||||||
|
// Delay the collapse animation until after all swipe animations have
|
||||||
mPile.setAnimateBounds(true); // reenable layout animation
|
// finished. Provide some buffer because there may be some extra delay
|
||||||
|
// before actually starting each swipe animation. Ideally, we'd
|
||||||
|
// synchronize the end of those animations with the start of the collaps
|
||||||
|
// exactly.
|
||||||
|
mHandler.postDelayed(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
animateCollapse(false);
|
||||||
|
}
|
||||||
|
}, totalDelay + 225);
|
||||||
}
|
}
|
||||||
}).start();
|
}).start();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ package com.android.systemui.statusbar.policy;
|
|||||||
|
|
||||||
import android.animation.Animator;
|
import android.animation.Animator;
|
||||||
import android.animation.AnimatorListenerAdapter;
|
import android.animation.AnimatorListenerAdapter;
|
||||||
import android.animation.AnimatorSet;
|
|
||||||
import android.animation.ObjectAnimator;
|
import android.animation.ObjectAnimator;
|
||||||
import android.animation.ValueAnimator;
|
import android.animation.ValueAnimator;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@@ -29,7 +28,6 @@ import android.util.AttributeSet;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.VelocityTracker;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewConfiguration;
|
import android.view.ViewConfiguration;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@@ -59,6 +57,10 @@ public class NotificationRowLayout extends ViewGroup implements SwipeHelper.Call
|
|||||||
|
|
||||||
private SwipeHelper mSwipeHelper;
|
private SwipeHelper mSwipeHelper;
|
||||||
|
|
||||||
|
// Flag set during notification removal animation to avoid causing too much work until
|
||||||
|
// animation is done
|
||||||
|
boolean mRemoveViews = true;
|
||||||
|
|
||||||
public NotificationRowLayout(Context context, AttributeSet attrs) {
|
public NotificationRowLayout(Context context, AttributeSet attrs) {
|
||||||
this(context, attrs, 0);
|
this(context, attrs, 0);
|
||||||
}
|
}
|
||||||
@@ -117,7 +119,7 @@ public class NotificationRowLayout extends ViewGroup implements SwipeHelper.Call
|
|||||||
|
|
||||||
public void onChildDismissed(View v) {
|
public void onChildDismissed(View v) {
|
||||||
final View veto = v.findViewById(R.id.veto);
|
final View veto = v.findViewById(R.id.veto);
|
||||||
if (veto != null && veto.getVisibility() != View.GONE) {
|
if (veto != null && veto.getVisibility() != View.GONE && mRemoveViews) {
|
||||||
veto.performClick();
|
veto.performClick();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -170,7 +172,6 @@ public class NotificationRowLayout extends ViewGroup implements SwipeHelper.Call
|
|||||||
final View childF = child;
|
final View childF = child;
|
||||||
|
|
||||||
if (mAnimateBounds) {
|
if (mAnimateBounds) {
|
||||||
child.setPivotY(0);
|
|
||||||
final ObjectAnimator alphaFade = ObjectAnimator.ofFloat(child, "alpha", 0f, 1f);
|
final ObjectAnimator alphaFade = ObjectAnimator.ofFloat(child, "alpha", 0f, 1f);
|
||||||
alphaFade.setDuration(APPEAR_ANIM_LEN);
|
alphaFade.setDuration(APPEAR_ANIM_LEN);
|
||||||
alphaFade.addListener(new AnimatorListenerAdapter() {
|
alphaFade.addListener(new AnimatorListenerAdapter() {
|
||||||
@@ -189,6 +190,16 @@ public class NotificationRowLayout extends ViewGroup implements SwipeHelper.Call
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a flag to tell us whether to actually remove views. Removal is delayed by setting this
|
||||||
|
* to false during some animations to smooth out performance. Callers should restore the
|
||||||
|
* flag to true after the animation is done, and then they should make sure that the views
|
||||||
|
* get removed properly.
|
||||||
|
*/
|
||||||
|
public void setViewRemoval(boolean removeViews) {
|
||||||
|
mRemoveViews = removeViews;
|
||||||
|
}
|
||||||
|
|
||||||
public void dismissRowAnimated(View child) {
|
public void dismissRowAnimated(View child) {
|
||||||
dismissRowAnimated(child, 0);
|
dismissRowAnimated(child, 0);
|
||||||
}
|
}
|
||||||
@@ -199,16 +210,34 @@ public class NotificationRowLayout extends ViewGroup implements SwipeHelper.Call
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeView(View child) {
|
public void removeView(View child) {
|
||||||
final View childF = child;
|
if (!mRemoveViews) {
|
||||||
|
// This flag is cleared during an animation that removes all notifications. There
|
||||||
|
// should be a call to remove all notifications when the animation is done, at which
|
||||||
|
// time the view will be removed.
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (mAnimateBounds) {
|
if (mAnimateBounds) {
|
||||||
if (mAppearingViews.containsKey(child)) {
|
if (mAppearingViews.containsKey(child)) {
|
||||||
mAppearingViews.remove(child);
|
mAppearingViews.remove(child);
|
||||||
}
|
}
|
||||||
child.setPivotY(0);
|
|
||||||
|
|
||||||
final ObjectAnimator alphaFade = ObjectAnimator.ofFloat(child, "alpha", 0f);
|
// Don't fade it out if it already has a low alpha value, but run a non-visual
|
||||||
alphaFade.setDuration(DISAPPEAR_ANIM_LEN);
|
// animation which is used by onLayout() to animate shrinking the gap that it left
|
||||||
alphaFade.addListener(new AnimatorListenerAdapter() {
|
// in the list
|
||||||
|
ValueAnimator anim;
|
||||||
|
float currentAlpha = child.getAlpha();
|
||||||
|
if (currentAlpha > .1) {
|
||||||
|
anim = ObjectAnimator.ofFloat(child, "alpha", currentAlpha, 0);
|
||||||
|
} else {
|
||||||
|
if (currentAlpha > 0) {
|
||||||
|
// Just make it go away - no need to render it anymore
|
||||||
|
child.setAlpha(0);
|
||||||
|
}
|
||||||
|
anim = ValueAnimator.ofFloat(0, 1);
|
||||||
|
}
|
||||||
|
anim.setDuration(DISAPPEAR_ANIM_LEN);
|
||||||
|
final View childF = child;
|
||||||
|
anim.addListener(new AnimatorListenerAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public void onAnimationEnd(Animator animation) {
|
public void onAnimationEnd(Animator animation) {
|
||||||
if (DEBUG) Slog.d(TAG, "actually removing child: " + childF);
|
if (DEBUG) Slog.d(TAG, "actually removing child: " + childF);
|
||||||
@@ -218,9 +247,8 @@ public class NotificationRowLayout extends ViewGroup implements SwipeHelper.Call
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
alphaFade.start();
|
anim.start();
|
||||||
|
mDisappearingViews.put(child, anim);
|
||||||
mDisappearingViews.put(child, alphaFade);
|
|
||||||
|
|
||||||
requestLayout(); // start the container animation
|
requestLayout(); // start the container animation
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user