Merge "Improved the animation logic of the stack scroller." into lmp-preview-dev
This commit is contained in:
@@ -21,15 +21,25 @@ import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapShader;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffColorFilter;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.Shader;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.view.animation.Interpolator;
|
||||
import android.view.animation.LinearInterpolator;
|
||||
import android.view.animation.PathInterpolator;
|
||||
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.statusbar.stack.StackStateAnimator;
|
||||
|
||||
/**
|
||||
* Base class for both {@link ExpandableNotificationRow} and {@link NotificationOverflowContainer}
|
||||
@@ -41,6 +51,36 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
|
||||
private static final int BACKGROUND_ANIMATION_LENGTH_MS = 220;
|
||||
private static final int ACTIVATE_ANIMATION_LENGTH = 220;
|
||||
|
||||
/**
|
||||
* The amount of width, which is kept in the end when performing a disappear animation (also
|
||||
* the amount from which the horizontal appearing begins)
|
||||
*/
|
||||
private static final float HORIZONTAL_COLLAPSED_REST_PARTIAL = 0.05f;
|
||||
|
||||
/**
|
||||
* At which point from [0,1] does the horizontal collapse animation end (or start when
|
||||
* expanding)? 1.0 meaning that it ends immediately and 0.0 that it is continuously animated.
|
||||
*/
|
||||
private static final float HORIZONTAL_ANIMATION_END = 0.2f;
|
||||
|
||||
/**
|
||||
* At which point from [0,1] does the alpha animation end (or start when
|
||||
* expanding)? 1.0 meaning that it ends immediately and 0.0 that it is continuously animated.
|
||||
*/
|
||||
private static final float ALPHA_ANIMATION_END = 0.0f;
|
||||
|
||||
/**
|
||||
* At which point from [0,1] does the horizontal collapse animation start (or start when
|
||||
* expanding)? 1.0 meaning that it starts immediately and 0.0 that it is animated at all.
|
||||
*/
|
||||
private static final float HORIZONTAL_ANIMATION_START = 1.0f;
|
||||
|
||||
/**
|
||||
* At which point from [0,1] does the vertical collapse animation start (or end when
|
||||
* expanding) 1.0 meaning that it starts immediately and 0.0 that it is animated at all.
|
||||
*/
|
||||
private static final float VERTICAL_ANIMATION_START = 1.0f;
|
||||
|
||||
private static final Interpolator ACTIVATE_INVERSE_INTERPOLATOR
|
||||
= new PathInterpolator(0.6f, 0, 0.5f, 1);
|
||||
private static final Interpolator ACTIVATE_INVERSE_ALPHA_INTERPOLATOR
|
||||
@@ -53,6 +93,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
|
||||
|
||||
private int mBgTint = 0;
|
||||
private int mDimmedBgTint = 0;
|
||||
private final int mRoundedRectCornerRadius;
|
||||
|
||||
/**
|
||||
* Flag to indicate that the notification has been touched once and the second touch will
|
||||
@@ -66,22 +107,41 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
|
||||
|
||||
private OnActivatedListener mOnActivatedListener;
|
||||
|
||||
private Interpolator mLinearOutSlowInInterpolator;
|
||||
private Interpolator mFastOutSlowInInterpolator;
|
||||
private final Interpolator mLinearOutSlowInInterpolator;
|
||||
private final Interpolator mFastOutSlowInInterpolator;
|
||||
private final Interpolator mSlowOutFastInInterpolator;
|
||||
private final Interpolator mSlowOutLinearInInterpolator;
|
||||
private final Interpolator mLinearInterpolator;
|
||||
private Interpolator mCurrentAppearInterpolator;
|
||||
private Interpolator mCurrentAlphaInterpolator;
|
||||
|
||||
private NotificationBackgroundView mBackgroundNormal;
|
||||
private NotificationBackgroundView mBackgroundDimmed;
|
||||
private ObjectAnimator mBackgroundAnimator;
|
||||
private RectF mAppearAnimationRect = new RectF();
|
||||
private PorterDuffColorFilter mAppearAnimationFilter;
|
||||
private float mAnimationTranslationY;
|
||||
private boolean mDrawingAppearAnimation;
|
||||
private Paint mAppearPaint = new Paint();
|
||||
private ValueAnimator mAppearAnimator;
|
||||
private float mAppearAnimationFraction = -1.0f;
|
||||
private float mAppearAnimationTranslation;
|
||||
|
||||
public ActivatableNotificationView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
|
||||
mFastOutSlowInInterpolator =
|
||||
AnimationUtils.loadInterpolator(context, android.R.interpolator.fast_out_slow_in);
|
||||
mSlowOutFastInInterpolator = new PathInterpolator(0.8f, 0.0f, 0.6f, 1.0f);
|
||||
mLinearOutSlowInInterpolator =
|
||||
AnimationUtils.loadInterpolator(context, android.R.interpolator.linear_out_slow_in);
|
||||
mSlowOutLinearInInterpolator = new PathInterpolator(0.8f, 0.0f, 1.0f, 1.0f);
|
||||
mLinearInterpolator = new LinearInterpolator();
|
||||
setClipChildren(false);
|
||||
setClipToPadding(false);
|
||||
mAppearAnimationFilter = new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_ATOP);
|
||||
mRoundedRectCornerRadius = getResources().getDimensionPixelSize(
|
||||
com.android.internal.R.dimen.notification_quantum_rounded_rect_radius);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -316,6 +376,202 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
|
||||
mBackgroundDimmed.setClipTopAmount(clipTopAmount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performRemoveAnimation(float translationDirection, Runnable onFinishedRunnable) {
|
||||
enableAppearDrawing(true);
|
||||
if (mDrawingAppearAnimation) {
|
||||
startAppearAnimation(false /* isAppearing */, translationDirection,
|
||||
0, onFinishedRunnable);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performAddAnimation(long delay) {
|
||||
enableAppearDrawing(true);
|
||||
if (mDrawingAppearAnimation) {
|
||||
startAppearAnimation(true /* isAppearing */, -1.0f, delay, null);
|
||||
}
|
||||
}
|
||||
|
||||
private void startAppearAnimation(boolean isAppearing,
|
||||
float translationDirection, long delay, final Runnable onFinishedRunnable) {
|
||||
if (mAppearAnimator != null) {
|
||||
mAppearAnimator.cancel();
|
||||
}
|
||||
mAnimationTranslationY = translationDirection * mActualHeight;
|
||||
if (mAppearAnimationFraction == -1.0f) {
|
||||
// not initialized yet, we start anew
|
||||
if (isAppearing) {
|
||||
mAppearAnimationFraction = 0.0f;
|
||||
mAppearAnimationTranslation = mAnimationTranslationY;
|
||||
} else {
|
||||
mAppearAnimationFraction = 1.0f;
|
||||
mAppearAnimationTranslation = 0;
|
||||
}
|
||||
}
|
||||
|
||||
float targetValue;
|
||||
if (isAppearing) {
|
||||
mCurrentAppearInterpolator = mSlowOutFastInInterpolator;
|
||||
mCurrentAlphaInterpolator = mLinearOutSlowInInterpolator;
|
||||
targetValue = 1.0f;
|
||||
} else {
|
||||
mCurrentAppearInterpolator = mFastOutSlowInInterpolator;
|
||||
mCurrentAlphaInterpolator = mSlowOutLinearInInterpolator;
|
||||
targetValue = 0.0f;
|
||||
}
|
||||
mAppearAnimator = ValueAnimator.ofFloat(mAppearAnimationFraction,
|
||||
targetValue);
|
||||
mAppearAnimator.setInterpolator(mLinearInterpolator);
|
||||
mAppearAnimator.setDuration(
|
||||
(long) (StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR
|
||||
* Math.abs(mAppearAnimationFraction - targetValue)));
|
||||
mAppearAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
mAppearAnimationFraction = (float) animation.getAnimatedValue();
|
||||
updateAppearAnimationAlpha();
|
||||
updateAppearRect();
|
||||
invalidate();
|
||||
}
|
||||
});
|
||||
if (delay > 0) {
|
||||
// we need to apply the initial state already to avoid drawn frames in the wrong state
|
||||
updateAppearAnimationAlpha();
|
||||
updateAppearRect();
|
||||
mAppearAnimator.setStartDelay(delay);
|
||||
}
|
||||
mAppearAnimator.addListener(new AnimatorListenerAdapter() {
|
||||
private boolean mWasCancelled;
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
if (onFinishedRunnable != null) {
|
||||
onFinishedRunnable.run();
|
||||
}
|
||||
if (!mWasCancelled) {
|
||||
mAppearAnimationFraction = -1;
|
||||
setOutlineRect(null);
|
||||
enableAppearDrawing(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
mWasCancelled = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
mWasCancelled = true;
|
||||
}
|
||||
});
|
||||
mAppearAnimator.start();
|
||||
}
|
||||
|
||||
private void updateAppearRect() {
|
||||
float inverseFraction = (1.0f - mAppearAnimationFraction);
|
||||
float translationFraction = mCurrentAppearInterpolator.getInterpolation(inverseFraction);
|
||||
float translateYTotalAmount = translationFraction * mAnimationTranslationY;
|
||||
mAppearAnimationTranslation = translateYTotalAmount;
|
||||
|
||||
// handle width animation
|
||||
float widthFraction = (inverseFraction - (1.0f - HORIZONTAL_ANIMATION_START))
|
||||
/ (HORIZONTAL_ANIMATION_START - HORIZONTAL_ANIMATION_END);
|
||||
widthFraction = Math.min(1.0f, Math.max(0.0f, widthFraction));
|
||||
widthFraction = mCurrentAppearInterpolator.getInterpolation(widthFraction);
|
||||
float left = (getWidth() * (0.5f - HORIZONTAL_COLLAPSED_REST_PARTIAL / 2.0f) *
|
||||
widthFraction);
|
||||
float right = getWidth() - left;
|
||||
|
||||
// handle top animation
|
||||
float heightFraction = (inverseFraction - (1.0f - VERTICAL_ANIMATION_START)) /
|
||||
VERTICAL_ANIMATION_START;
|
||||
heightFraction = Math.max(0.0f, heightFraction);
|
||||
heightFraction = mCurrentAppearInterpolator.getInterpolation(heightFraction);
|
||||
|
||||
float top;
|
||||
float bottom;
|
||||
if (mAnimationTranslationY > 0.0f) {
|
||||
bottom = mActualHeight - heightFraction * mAnimationTranslationY * 0.1f
|
||||
- translateYTotalAmount;
|
||||
top = bottom * heightFraction;
|
||||
} else {
|
||||
top = heightFraction * (mActualHeight + mAnimationTranslationY) * 0.1f -
|
||||
translateYTotalAmount;
|
||||
bottom = mActualHeight * (1 - heightFraction) + top * heightFraction;
|
||||
}
|
||||
mAppearAnimationRect.set(left, top, right, bottom);
|
||||
setOutlineRect(left, top + mAppearAnimationTranslation, right,
|
||||
bottom + mAppearAnimationTranslation);
|
||||
}
|
||||
|
||||
private void updateAppearAnimationAlpha() {
|
||||
int backgroundColor = getBackgroundColor();
|
||||
if (backgroundColor != -1) {
|
||||
float contentAlphaProgress = mAppearAnimationFraction;
|
||||
contentAlphaProgress = contentAlphaProgress / (1.0f - ALPHA_ANIMATION_END);
|
||||
contentAlphaProgress = Math.min(1.0f, contentAlphaProgress);
|
||||
contentAlphaProgress = mCurrentAlphaInterpolator.getInterpolation(contentAlphaProgress);
|
||||
int sourceColor = Color.argb((int) (255 * (1.0f - contentAlphaProgress)),
|
||||
Color.red(backgroundColor), Color.green(backgroundColor),
|
||||
Color.blue(backgroundColor));
|
||||
mAppearAnimationFilter.setColor(sourceColor);
|
||||
mAppearPaint.setColorFilter(mAppearAnimationFilter);
|
||||
}
|
||||
}
|
||||
|
||||
private int getBackgroundColor() {
|
||||
// TODO: get real color
|
||||
return 0xfffafafa;
|
||||
}
|
||||
|
||||
/**
|
||||
* When we draw the appear animation, we render the view in a bitmap and render this bitmap
|
||||
* as a shader of a rect. This call creates the Bitmap and switches the drawing mode,
|
||||
* such that the normal drawing of the views does not happen anymore.
|
||||
*
|
||||
* @param enable Should it be enabled.
|
||||
*/
|
||||
private void enableAppearDrawing(boolean enable) {
|
||||
if (enable != mDrawingAppearAnimation) {
|
||||
if (enable) {
|
||||
if (getWidth() == 0 || getActualHeight() == 0) {
|
||||
// TODO: This should not happen, but it can during expansion. Needs
|
||||
// investigation
|
||||
return;
|
||||
}
|
||||
Bitmap bitmap = Bitmap.createBitmap(getWidth(), getActualHeight(),
|
||||
Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
draw(canvas);
|
||||
mAppearPaint.setShader(new BitmapShader(bitmap, Shader.TileMode.CLAMP,
|
||||
Shader.TileMode.CLAMP));
|
||||
} else {
|
||||
mAppearPaint.setShader(null);
|
||||
}
|
||||
mDrawingAppearAnimation = enable;
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dispatchDraw(Canvas canvas) {
|
||||
if (!mDrawingAppearAnimation) {
|
||||
super.dispatchDraw(canvas);
|
||||
} else {
|
||||
drawAppearRect(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
private void drawAppearRect(Canvas canvas) {
|
||||
canvas.save();
|
||||
canvas.translate(0, mAppearAnimationTranslation);
|
||||
canvas.drawRoundRect(mAppearAnimationRect, mRoundedRectCornerRadius,
|
||||
mRoundedRectCornerRadius, mAppearPaint);
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
public void setOnActivatedListener(OnActivatedListener onActivatedListener) {
|
||||
mOnActivatedListener = onActivatedListener;
|
||||
}
|
||||
|
||||
@@ -1092,6 +1092,7 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
if (rowParent != null) rowParent.removeView(entry.row);
|
||||
updateRowStates();
|
||||
updateNotificationIcons();
|
||||
updateSpeedBump();
|
||||
|
||||
return entry.notification;
|
||||
}
|
||||
@@ -1135,8 +1136,22 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "addNotificationViews: added at " + pos);
|
||||
}
|
||||
updateNotificationIcons();
|
||||
updateRowStates();
|
||||
updateNotificationIcons();
|
||||
updateSpeedBump();
|
||||
}
|
||||
|
||||
protected void updateSpeedBump() {
|
||||
int n = mNotificationData.size();
|
||||
int speedBumpIndex = -1;
|
||||
for (int i = n-1; i >= 0; i--) {
|
||||
NotificationData.Entry entry = mNotificationData.get(i);
|
||||
if (entry.row.getVisibility() != View.GONE && speedBumpIndex == -1
|
||||
&& entry.row.isBelowSpeedBump() ) {
|
||||
speedBumpIndex = n - 1 - i;
|
||||
}
|
||||
}
|
||||
mStackScroller.updateSpeedBumpIndex(speedBumpIndex);
|
||||
}
|
||||
|
||||
private void addNotificationViews(StatusBarNotification notification) {
|
||||
@@ -1156,7 +1171,6 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
mKeyguardIconOverflowContainer.getIconsView().removeAllViews();
|
||||
int n = mNotificationData.size();
|
||||
int visibleNotifications = 0;
|
||||
int speedBumpIndex = -1;
|
||||
boolean onKeyguard = mState == StatusBarState.KEYGUARD;
|
||||
for (int i = n-1; i >= 0; i--) {
|
||||
NotificationData.Entry entry = mNotificationData.get(i);
|
||||
@@ -1177,17 +1191,14 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
mKeyguardIconOverflowContainer.getIconsView().addNotification(entry);
|
||||
}
|
||||
} else {
|
||||
if (entry.row.getVisibility() == View.GONE) {
|
||||
boolean wasGone = entry.row.getVisibility() == View.GONE;
|
||||
entry.row.setVisibility(View.VISIBLE);
|
||||
if (wasGone) {
|
||||
// notify the scroller of a child addition
|
||||
mStackScroller.generateAddAnimation(entry.row);
|
||||
}
|
||||
entry.row.setVisibility(View.VISIBLE);
|
||||
visibleNotifications++;
|
||||
}
|
||||
if (entry.row.getVisibility() != View.GONE && speedBumpIndex == -1
|
||||
&& entry.row.isBelowSpeedBump() ) {
|
||||
speedBumpIndex = n - 1 - i;
|
||||
}
|
||||
}
|
||||
|
||||
if (onKeyguard && mKeyguardIconOverflowContainer.getIconsView().getChildCount() > 0) {
|
||||
@@ -1195,8 +1206,6 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
} else {
|
||||
mKeyguardIconOverflowContainer.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
mStackScroller.updateSpeedBumpIndex(speedBumpIndex);
|
||||
}
|
||||
|
||||
private boolean shouldShowOnKeyguard(StatusBarNotification sbn) {
|
||||
@@ -1357,6 +1366,7 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
return;
|
||||
}
|
||||
updateRowStates();
|
||||
updateSpeedBump();
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
// It failed to add cleanly. Log, and remove the view from the panel.
|
||||
|
||||
@@ -18,8 +18,8 @@ package com.android.systemui.statusbar;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Outline;
|
||||
import android.graphics.RectF;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
/**
|
||||
* Like {@link ExpandableView}, but setting an outline for the height and clipping.
|
||||
@@ -27,9 +27,12 @@ import android.widget.FrameLayout;
|
||||
public abstract class ExpandableOutlineView extends ExpandableView {
|
||||
|
||||
private final Outline mOutline = new Outline();
|
||||
private boolean mCustomOutline;
|
||||
private float mDensity;
|
||||
|
||||
public ExpandableOutlineView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mDensity = getResources().getDisplayMetrics().density;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -50,11 +53,37 @@ public abstract class ExpandableOutlineView extends ExpandableView {
|
||||
updateOutline();
|
||||
}
|
||||
|
||||
private void updateOutline() {
|
||||
mOutline.setRect(0,
|
||||
mClipTopAmount,
|
||||
getWidth(),
|
||||
Math.max(mActualHeight, mClipTopAmount));
|
||||
protected void setOutlineRect(RectF rect) {
|
||||
if (rect != null) {
|
||||
setOutlineRect(rect.left, rect.top, rect.right, rect.bottom);
|
||||
} else {
|
||||
mCustomOutline = false;
|
||||
updateOutline();
|
||||
}
|
||||
}
|
||||
|
||||
protected void setOutlineRect(float left, float top, float right, float bottom) {
|
||||
mCustomOutline = true;
|
||||
|
||||
int rectLeft = (int) left;
|
||||
int rectTop = (int) top;
|
||||
int rectRight = (int) right;
|
||||
int rectBottom = (int) bottom;
|
||||
|
||||
// Outlines need to be at least 1 dp
|
||||
rectBottom = (int) Math.max(top + mDensity, rectBottom);
|
||||
rectRight = (int) Math.max(left + mDensity, rectRight);
|
||||
mOutline.setRect(rectLeft, rectTop, rectRight, rectBottom);
|
||||
setOutline(mOutline);
|
||||
}
|
||||
|
||||
private void updateOutline() {
|
||||
if (!mCustomOutline) {
|
||||
mOutline.setRect(0,
|
||||
mClipTopAmount,
|
||||
getWidth(),
|
||||
Math.max(mActualHeight, mClipTopAmount));
|
||||
setOutline(mOutline);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,6 +204,21 @@ public abstract class ExpandableView extends FrameLayout {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a remove animation on this view.
|
||||
*
|
||||
* @param translationDirection The direction value from [-1 ... 1] indicating in which the
|
||||
* animation should be performed. A value of -1 means that The
|
||||
* remove animation should be performed upwards,
|
||||
* such that the child appears to be going away to the top. 1
|
||||
* Should mean the opposite.
|
||||
* @param onFinishedRunnable A runnable which should be run when the animation is finished.
|
||||
*/
|
||||
public abstract void performRemoveAnimation(float translationDirection,
|
||||
Runnable onFinishedRunnable);
|
||||
|
||||
public abstract void performAddAnimation(long delay);
|
||||
|
||||
/**
|
||||
* A listener notifying when {@link #getActualHeight} changes.
|
||||
*/
|
||||
|
||||
@@ -34,7 +34,6 @@ public class NotificationBackgroundView extends View {
|
||||
|
||||
public NotificationBackgroundView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
setWillNotDraw(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -184,7 +184,7 @@ public class SpeedBumpView extends ExpandableView implements View.OnClickListene
|
||||
}
|
||||
|
||||
public void performVisibilityAnimation(boolean nowVisible) {
|
||||
animateDivider(nowVisible);
|
||||
animateDivider(nowVisible, null /* onFinishedRunnable */);
|
||||
|
||||
// Animate explanation Text
|
||||
if (mIsExpanded) {
|
||||
@@ -192,7 +192,14 @@ public class SpeedBumpView extends ExpandableView implements View.OnClickListene
|
||||
}
|
||||
}
|
||||
|
||||
public void animateDivider(boolean nowVisible) {
|
||||
/**
|
||||
* Animate the divider to a new visibility.
|
||||
*
|
||||
* @param nowVisible should it now be visible
|
||||
* @param onFinishedRunnable A runnable which should be run when the animation is
|
||||
* finished.
|
||||
*/
|
||||
public void animateDivider(boolean nowVisible, Runnable onFinishedRunnable) {
|
||||
if (nowVisible != mDividerVisible) {
|
||||
// Animate dividers
|
||||
float endValue = nowVisible ? 1.0f : 0.0f;
|
||||
@@ -204,7 +211,8 @@ public class SpeedBumpView extends ExpandableView implements View.OnClickListene
|
||||
.scaleX(endValue)
|
||||
.scaleY(endValue)
|
||||
.translationX(endTranslationXLeft)
|
||||
.setInterpolator(mFastOutSlowInInterpolator);
|
||||
.setInterpolator(mFastOutSlowInInterpolator)
|
||||
.withEndAction(onFinishedRunnable);
|
||||
mLineRight.animate()
|
||||
.alpha(endValue)
|
||||
.withLayer()
|
||||
@@ -216,6 +224,10 @@ public class SpeedBumpView extends ExpandableView implements View.OnClickListene
|
||||
// Animate dots
|
||||
mDots.performVisibilityAnimation(nowVisible);
|
||||
mDividerVisible = nowVisible;
|
||||
} else {
|
||||
if (onFinishedRunnable != null) {
|
||||
onFinishedRunnable.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,6 +262,16 @@ public class SpeedBumpView extends ExpandableView implements View.OnClickListene
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performRemoveAnimation(float translationDirection, Runnable onFinishedRunnable) {
|
||||
performVisibilityAnimation(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performAddAnimation(long delay) {
|
||||
performVisibilityAnimation(true);
|
||||
}
|
||||
|
||||
private void resetExplanationText() {
|
||||
mExplanationText.setTranslationY(0);
|
||||
mExplanationText.setVisibility(INVISIBLE);
|
||||
|
||||
@@ -214,7 +214,7 @@ public class NotificationPanelView extends PanelView implements
|
||||
mClockAnimationTarget = -1;
|
||||
}
|
||||
});
|
||||
StackStateAnimator.startInstantly(mClockAnimator);
|
||||
mClockAnimator.start();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2768,6 +2768,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
|
||||
updateStackScrollerState();
|
||||
updatePublicMode();
|
||||
updateRowStates();
|
||||
updateSpeedBump();
|
||||
checkBarModes();
|
||||
updateNotificationIcons();
|
||||
updateCarrierLabelVisibility(false);
|
||||
|
||||
@@ -28,6 +28,7 @@ public class AnimationFilter {
|
||||
boolean animateScale;
|
||||
boolean animateHeight;
|
||||
boolean animateDimmed;
|
||||
boolean hasDelays;
|
||||
|
||||
public AnimationFilter animateAlpha() {
|
||||
animateAlpha = true;
|
||||
@@ -39,6 +40,11 @@ public class AnimationFilter {
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnimationFilter hasDelays() {
|
||||
hasDelays = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnimationFilter animateZ() {
|
||||
animateZ = true;
|
||||
return this;
|
||||
@@ -79,6 +85,7 @@ public class AnimationFilter {
|
||||
animateScale |= filter.animateScale;
|
||||
animateHeight |= filter.animateHeight;
|
||||
animateDimmed |= filter.animateDimmed;
|
||||
hasDelays |= filter.hasDelays;
|
||||
}
|
||||
|
||||
private void reset() {
|
||||
@@ -88,5 +95,6 @@ public class AnimationFilter {
|
||||
animateScale = false;
|
||||
animateHeight = false;
|
||||
animateDimmed = false;
|
||||
hasDelays = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,6 +103,7 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
private ArrayList<View> mChildrenToRemoveAnimated = new ArrayList<View>();
|
||||
private ArrayList<View> mSnappedBackChildren = new ArrayList<View>();
|
||||
private ArrayList<View> mDragAnimPendingChildren = new ArrayList<View>();
|
||||
private ArrayList<View> mChildrenChangingPositions = new ArrayList<View>();
|
||||
private ArrayList<AnimationEvent> mAnimationEvents
|
||||
= new ArrayList<AnimationEvent>();
|
||||
private ArrayList<View> mSwipedOutViews = new ArrayList<View>();
|
||||
@@ -968,10 +969,25 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The first child which has visibility unequal to GONE which is currently below the
|
||||
* given translationY or equal to it.
|
||||
*/
|
||||
private View getFirstChildBelowTranlsationY(float translationY) {
|
||||
int childCount = getChildCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
View child = getChildAt(i);
|
||||
if (child.getVisibility() != View.GONE && child.getTranslationY() >= translationY) {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the last child which has visibility unequal to GONE
|
||||
*/
|
||||
private View getLastChildNotGone() {
|
||||
public View getLastChildNotGone() {
|
||||
int childCount = getChildCount();
|
||||
for (int i = childCount - 1; i >= 0; i--) {
|
||||
View child = getChildAt(i);
|
||||
@@ -1094,23 +1110,41 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
@Override
|
||||
protected void onViewRemoved(View child) {
|
||||
super.onViewRemoved(child);
|
||||
mStackScrollAlgorithm.notifyChildrenChanged(this);
|
||||
if (mChildrenChangingPositions.contains(child)) {
|
||||
// This is only a position change, don't do anything special
|
||||
return;
|
||||
}
|
||||
((ExpandableView) child).setOnHeightChangedListener(null);
|
||||
mCurrentStackScrollState.removeViewStateForView(child);
|
||||
mStackScrollAlgorithm.notifyChildrenChanged(this);
|
||||
updateScrollStateForRemovedChild(child);
|
||||
generateRemoveAnimation(child);
|
||||
boolean animationGenerated = generateRemoveAnimation(child);
|
||||
if (animationGenerated && !mSwipedOutViews.contains(child)) {
|
||||
// Add this view to an overlay in order to ensure that it will still be temporary
|
||||
// drawn when removed
|
||||
getOverlay().add(child);
|
||||
}
|
||||
}
|
||||
|
||||
private void generateRemoveAnimation(View child) {
|
||||
/**
|
||||
* Generate a remove animation for a child view.
|
||||
*
|
||||
* @param child The view to generate the remove animation for.
|
||||
* @return Whether an animation was generated.
|
||||
*/
|
||||
private boolean generateRemoveAnimation(View child) {
|
||||
if (mIsExpanded && mAnimationsEnabled) {
|
||||
if (!mChildrenToAddAnimated.contains(child)) {
|
||||
// Generate Animations
|
||||
mChildrenToRemoveAnimated.add(child);
|
||||
mNeedsAnimation = true;
|
||||
return true;
|
||||
} else {
|
||||
mChildrenToAddAnimated.remove(child);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1155,9 +1189,7 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
super.onViewAdded(child);
|
||||
mStackScrollAlgorithm.notifyChildrenChanged(this);
|
||||
((ExpandableView) child).setOnHeightChangedListener(this);
|
||||
if (child.getVisibility() != View.GONE) {
|
||||
generateAddAnimation(child);
|
||||
}
|
||||
generateAddAnimation(child);
|
||||
}
|
||||
|
||||
public void setAnimationsEnabled(boolean animationsEnabled) {
|
||||
@@ -1168,10 +1200,13 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
return mNeedsAnimation
|
||||
&& (!mChildrenToAddAnimated.isEmpty() || !mChildrenToRemoveAnimated.isEmpty());
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate an animation for an added child view.
|
||||
*
|
||||
* @param child The view to be added.
|
||||
*/
|
||||
public void generateAddAnimation(View child) {
|
||||
if (mIsExpanded && mAnimationsEnabled) {
|
||||
|
||||
if (mIsExpanded && mAnimationsEnabled && !mChildrenChangingPositions.contains(child)) {
|
||||
// Generate Animations
|
||||
mChildrenToAddAnimated.add(child);
|
||||
mNeedsAnimation = true;
|
||||
@@ -1186,9 +1221,10 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
*/
|
||||
public void changeViewPosition(View child, int newIndex) {
|
||||
if (child != null && child.getParent() == this) {
|
||||
mChildrenChangingPositions.add(child);
|
||||
removeView(child);
|
||||
addView(child, newIndex);
|
||||
// TODO: handle events
|
||||
mNeedsAnimation = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1197,16 +1233,18 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
generateChildHierarchyEvents();
|
||||
mNeedsAnimation = false;
|
||||
}
|
||||
if (!mAnimationEvents.isEmpty()) {
|
||||
if (!mAnimationEvents.isEmpty() || isCurrentlyAnimating()) {
|
||||
mStateAnimator.startAnimationForEvents(mAnimationEvents, mCurrentStackScrollState);
|
||||
mAnimationEvents.clear();
|
||||
} else {
|
||||
applyCurrentState();
|
||||
}
|
||||
}
|
||||
|
||||
private void generateChildHierarchyEvents() {
|
||||
generateChildAdditionEvents();
|
||||
generateChildRemovalEvents();
|
||||
generateChildAdditionEvents();
|
||||
generatePositionChangeEvents();
|
||||
generateSnapBackEvents();
|
||||
generateDragEvents();
|
||||
generateTopPaddingEvent();
|
||||
@@ -1237,12 +1275,24 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
int animationType = childWasSwipedOut
|
||||
? AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT
|
||||
: AnimationEvent.ANIMATION_TYPE_REMOVE;
|
||||
mAnimationEvents.add(new AnimationEvent(child, animationType));
|
||||
AnimationEvent event = new AnimationEvent(child, animationType);
|
||||
|
||||
// we need to know the view after this one
|
||||
event.viewAfterChangingView = getFirstChildBelowTranlsationY(child.getTranslationY());
|
||||
mAnimationEvents.add(event);
|
||||
}
|
||||
mSwipedOutViews.clear();
|
||||
mChildrenToRemoveAnimated.clear();
|
||||
}
|
||||
|
||||
private void generatePositionChangeEvents() {
|
||||
for (View child : mChildrenChangingPositions) {
|
||||
mAnimationEvents.add(new AnimationEvent(child,
|
||||
AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION));
|
||||
}
|
||||
mChildrenChangingPositions.clear();
|
||||
}
|
||||
|
||||
private void generateChildAdditionEvents() {
|
||||
for (View child : mChildrenToAddAnimated) {
|
||||
mAnimationEvents.add(new AnimationEvent(child,
|
||||
@@ -1467,7 +1517,6 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
|
||||
public void onChildAnimationFinished() {
|
||||
requestChildrenUpdate();
|
||||
mAnimationEvents.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1513,9 +1562,9 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
}
|
||||
|
||||
private void updateSpeedBump(boolean visible) {
|
||||
int newVisibility = visible ? VISIBLE : GONE;
|
||||
int oldVisibility = mSpeedBumpView.getVisibility();
|
||||
if (newVisibility != oldVisibility) {
|
||||
boolean notGoneBefore = mSpeedBumpView.getVisibility() != GONE;
|
||||
if (visible != notGoneBefore) {
|
||||
int newVisibility = visible ? VISIBLE : GONE;
|
||||
mSpeedBumpView.setVisibility(newVisibility);
|
||||
if (visible) {
|
||||
mSpeedBumpView.collapse();
|
||||
@@ -1551,21 +1600,24 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
.animateAlpha()
|
||||
.animateHeight()
|
||||
.animateY()
|
||||
.animateZ(),
|
||||
.animateZ()
|
||||
.hasDelays(),
|
||||
|
||||
// ANIMATION_TYPE_REMOVE
|
||||
new AnimationFilter()
|
||||
.animateAlpha()
|
||||
.animateHeight()
|
||||
.animateY()
|
||||
.animateZ(),
|
||||
.animateZ()
|
||||
.hasDelays(),
|
||||
|
||||
// ANIMATION_TYPE_REMOVE_SWIPED_OUT
|
||||
new AnimationFilter()
|
||||
.animateAlpha()
|
||||
.animateHeight()
|
||||
.animateY()
|
||||
.animateZ(),
|
||||
.animateZ()
|
||||
.hasDelays(),
|
||||
|
||||
// ANIMATION_TYPE_TOP_PADDING_CHANGED
|
||||
new AnimationFilter()
|
||||
@@ -1593,16 +1645,23 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
new AnimationFilter()
|
||||
.animateY()
|
||||
.animateScale()
|
||||
.animateDimmed()
|
||||
.animateDimmed(),
|
||||
|
||||
// ANIMATION_TYPE_CHANGE_POSITION
|
||||
new AnimationFilter()
|
||||
.animateAlpha()
|
||||
.animateHeight()
|
||||
.animateY()
|
||||
.animateZ()
|
||||
};
|
||||
|
||||
static int[] LENGTHS = new int[] {
|
||||
|
||||
// ANIMATION_TYPE_ADD
|
||||
StackStateAnimator.ANIMATION_DURATION_STANDARD,
|
||||
StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR,
|
||||
|
||||
// ANIMATION_TYPE_REMOVE
|
||||
StackStateAnimator.ANIMATION_DURATION_STANDARD,
|
||||
StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR,
|
||||
|
||||
// ANIMATION_TYPE_REMOVE_SWIPED_OUT
|
||||
StackStateAnimator.ANIMATION_DURATION_STANDARD,
|
||||
@@ -1621,22 +1680,27 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
|
||||
// ANIMATION_TYPE_DIMMED
|
||||
StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED,
|
||||
|
||||
// ANIMATION_TYPE_CHANGE_POSITION
|
||||
StackStateAnimator.ANIMATION_DURATION_STANDARD,
|
||||
};
|
||||
|
||||
static int ANIMATION_TYPE_ADD = 0;
|
||||
static int ANIMATION_TYPE_REMOVE = 1;
|
||||
static int ANIMATION_TYPE_REMOVE_SWIPED_OUT = 2;
|
||||
static int ANIMATION_TYPE_TOP_PADDING_CHANGED = 3;
|
||||
static int ANIMATION_TYPE_START_DRAG = 4;
|
||||
static int ANIMATION_TYPE_SNAP_BACK = 5;
|
||||
static int ANIMATION_TYPE_ACTIVATED_CHILD = 6;
|
||||
static int ANIMATION_TYPE_DIMMED = 7;
|
||||
static final int ANIMATION_TYPE_ADD = 0;
|
||||
static final int ANIMATION_TYPE_REMOVE = 1;
|
||||
static final int ANIMATION_TYPE_REMOVE_SWIPED_OUT = 2;
|
||||
static final int ANIMATION_TYPE_TOP_PADDING_CHANGED = 3;
|
||||
static final int ANIMATION_TYPE_START_DRAG = 4;
|
||||
static final int ANIMATION_TYPE_SNAP_BACK = 5;
|
||||
static final int ANIMATION_TYPE_ACTIVATED_CHILD = 6;
|
||||
static final int ANIMATION_TYPE_DIMMED = 7;
|
||||
static final int ANIMATION_TYPE_CHANGE_POSITION = 8;
|
||||
|
||||
final long eventStartTime;
|
||||
final View changingView;
|
||||
final int animationType;
|
||||
final AnimationFilter filter;
|
||||
final long length;
|
||||
View viewAfterChangingView;
|
||||
|
||||
AnimationEvent(View view, int type) {
|
||||
eventStartTime = AnimationUtils.currentAnimationTimeMillis();
|
||||
|
||||
@@ -208,6 +208,8 @@ public class StackScrollAlgorithm {
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
ExpandableView v = (ExpandableView) hostView.getChildAt(i);
|
||||
if (v.getVisibility() != View.GONE) {
|
||||
StackScrollState.ViewState viewState = resultState.getViewStateForView(v);
|
||||
viewState.notGoneIndex = state.visibleChildren.size();
|
||||
state.visibleChildren.add(v);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,14 +39,10 @@ public class StackScrollState {
|
||||
private final ViewGroup mHostView;
|
||||
private Map<ExpandableView, ViewState> mStateMap;
|
||||
private final Rect mClipRect = new Rect();
|
||||
private int mBackgroundRoundedRectCornerRadius;
|
||||
private final Outline mChildOutline = new Outline();
|
||||
|
||||
public StackScrollState(ViewGroup hostView) {
|
||||
mHostView = hostView;
|
||||
mStateMap = new HashMap<ExpandableView, ViewState>();
|
||||
mBackgroundRoundedRectCornerRadius = hostView.getResources().getDimensionPixelSize(
|
||||
com.android.internal.R.dimen.notification_quantum_rounded_rect_radius);
|
||||
}
|
||||
|
||||
public ViewGroup getHostView() {
|
||||
@@ -66,6 +62,7 @@ public class StackScrollState {
|
||||
viewState.height = child.getIntrinsicHeight();
|
||||
viewState.gone = child.getVisibility() == View.GONE;
|
||||
viewState.alpha = 1;
|
||||
viewState.notGoneIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,7 +187,7 @@ public class StackScrollState {
|
||||
if (nextChild != null) {
|
||||
ViewState nextState = getViewStateForView(nextChild);
|
||||
boolean startIsAboveNext = nextState.yTranslation > speedBumpStart;
|
||||
speedBump.animateDivider(startIsAboveNext);
|
||||
speedBump.animateDivider(startIsAboveNext, null /* onFinishedRunnable */);
|
||||
|
||||
// handle expanded case
|
||||
if (speedBump.isExpanded()) {
|
||||
@@ -271,6 +268,11 @@ public class StackScrollState {
|
||||
float scale;
|
||||
boolean dimmed;
|
||||
|
||||
/**
|
||||
* The index of the view, only accounting for views not equal to GONE
|
||||
*/
|
||||
int notGoneIndex;
|
||||
|
||||
/**
|
||||
* The location this view is currently rendered at.
|
||||
*
|
||||
|
||||
@@ -39,7 +39,11 @@ import java.util.Stack;
|
||||
public class StackStateAnimator {
|
||||
|
||||
public static final int ANIMATION_DURATION_STANDARD = 360;
|
||||
public static final int ANIMATION_DURATION_APPEAR_DISAPPEAR = 464;
|
||||
public static final int ANIMATION_DURATION_DIMMED_ACTIVATED = 220;
|
||||
public static final int ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING = 80;
|
||||
public static final int ANIMATION_DELAY_PER_ELEMENT_MANUAL = 32;
|
||||
private static final int DELAY_EFFECT_MAX_INDEX_DIFFERENCE = 2;
|
||||
|
||||
private static final int TAG_ANIMATOR_TRANSLATION_Y = R.id.translation_y_animator_tag;
|
||||
private static final int TAG_ANIMATOR_TRANSLATION_Z = R.id.translation_z_animator_tag;
|
||||
@@ -62,10 +66,9 @@ public class StackStateAnimator {
|
||||
|
||||
private final Interpolator mFastOutSlowInInterpolator;
|
||||
public NotificationStackScrollLayout mHostLayout;
|
||||
private ArrayList<NotificationStackScrollLayout.AnimationEvent> mHandledEvents =
|
||||
new ArrayList<>();
|
||||
private ArrayList<NotificationStackScrollLayout.AnimationEvent> mNewEvents =
|
||||
new ArrayList<>();
|
||||
private ArrayList<View> mNewAddChildren = new ArrayList<>();
|
||||
private Set<Animator> mAnimatorSet = new HashSet<Animator>();
|
||||
private Stack<AnimatorListenerAdapter> mAnimationListenerPool
|
||||
= new Stack<AnimatorListenerAdapter>();
|
||||
@@ -96,57 +99,130 @@ public class StackStateAnimator {
|
||||
mCurrentLength = NotificationStackScrollLayout.AnimationEvent.combineLength(mNewEvents);
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
final ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i);
|
||||
|
||||
StackScrollState.ViewState viewState = finalState.getViewStateForView(child);
|
||||
if (viewState == null) {
|
||||
if (viewState == null || child.getVisibility() == View.GONE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
startAnimations(child, viewState);
|
||||
|
||||
child.setClipBounds(null);
|
||||
startAnimations(child, viewState, finalState);
|
||||
}
|
||||
if (!isRunning()) {
|
||||
// no child has preformed any animation, lets finish
|
||||
onAnimationFinished();
|
||||
}
|
||||
mNewEvents.clear();
|
||||
mNewAddChildren.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Start an animation to the given viewState
|
||||
*/
|
||||
private void startAnimations(final ExpandableView child, StackScrollState.ViewState viewState) {
|
||||
private void startAnimations(final ExpandableView child, StackScrollState.ViewState viewState,
|
||||
StackScrollState finalState) {
|
||||
int childVisibility = child.getVisibility();
|
||||
boolean wasVisible = childVisibility == View.VISIBLE;
|
||||
final float alpha = viewState.alpha;
|
||||
if (!wasVisible && alpha != 0 && !viewState.gone) {
|
||||
child.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
boolean yTranslationChanging = child.getTranslationY() != viewState.yTranslation;
|
||||
boolean zTranslationChanging = child.getTranslationZ() != viewState.zTranslation;
|
||||
boolean scaleChanging = child.getScaleX() != viewState.scale;
|
||||
boolean alphaChanging = alpha != child.getAlpha();
|
||||
boolean heightChanging = viewState.height != child.getActualHeight();
|
||||
boolean wasAdded = mNewAddChildren.contains(child);
|
||||
boolean hasDelays = mAnimationFilter.hasDelays;
|
||||
boolean isDelayRelevant = yTranslationChanging || zTranslationChanging || scaleChanging ||
|
||||
alphaChanging || heightChanging;
|
||||
long delay = 0;
|
||||
if (hasDelays && isDelayRelevant || wasAdded) {
|
||||
delay = calculateChildAnimationDelay(viewState, finalState);
|
||||
}
|
||||
|
||||
// start translationY animation
|
||||
if (child.getTranslationY() != viewState.yTranslation) {
|
||||
startYTranslationAnimation(child, viewState);
|
||||
if (yTranslationChanging) {
|
||||
startYTranslationAnimation(child, viewState, delay);
|
||||
}
|
||||
|
||||
// start translationZ animation
|
||||
if (child.getTranslationZ() != viewState.zTranslation) {
|
||||
startZTranslationAnimation(child, viewState);
|
||||
if (zTranslationChanging) {
|
||||
startZTranslationAnimation(child, viewState, delay);
|
||||
}
|
||||
|
||||
// start scale animation
|
||||
if (child.getScaleX() != viewState.scale) {
|
||||
if (scaleChanging) {
|
||||
startScaleAnimation(child, viewState);
|
||||
}
|
||||
|
||||
// start alpha animation
|
||||
if (alpha != child.getAlpha()) {
|
||||
startAlphaAnimation(child, viewState);
|
||||
if (alphaChanging) {
|
||||
startAlphaAnimation(child, viewState, delay);
|
||||
}
|
||||
|
||||
// start height animation
|
||||
if (viewState.height != child.getActualHeight()) {
|
||||
startHeightAnimation(child, viewState);
|
||||
if (heightChanging) {
|
||||
startHeightAnimation(child, viewState, delay);
|
||||
}
|
||||
|
||||
// start dimmed animation
|
||||
child.setDimmed(viewState.dimmed, mAnimationFilter.animateDimmed);
|
||||
|
||||
if (wasAdded) {
|
||||
child.performAddAnimation(delay);
|
||||
}
|
||||
}
|
||||
|
||||
private long calculateChildAnimationDelay(StackScrollState.ViewState viewState,
|
||||
StackScrollState finalState) {
|
||||
long minDelay = 0;
|
||||
for (NotificationStackScrollLayout.AnimationEvent event : mNewEvents) {
|
||||
long delayPerElement = ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING;
|
||||
switch (event.animationType) {
|
||||
case NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_ADD: {
|
||||
int ownIndex = viewState.notGoneIndex;
|
||||
int changingIndex = finalState
|
||||
.getViewStateForView(event.changingView).notGoneIndex;
|
||||
int difference = Math.abs(ownIndex - changingIndex);
|
||||
difference = Math.max(0, Math.min(DELAY_EFFECT_MAX_INDEX_DIFFERENCE,
|
||||
difference - 1));
|
||||
long delay = (DELAY_EFFECT_MAX_INDEX_DIFFERENCE - difference) * delayPerElement;
|
||||
minDelay = Math.max(delay, minDelay);
|
||||
break;
|
||||
}
|
||||
case NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT:
|
||||
delayPerElement = ANIMATION_DELAY_PER_ELEMENT_MANUAL;
|
||||
case NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE: {
|
||||
int ownIndex = viewState.notGoneIndex;
|
||||
boolean noNextView = event.viewAfterChangingView == null;
|
||||
View viewAfterChangingView = noNextView
|
||||
? mHostLayout.getLastChildNotGone()
|
||||
: event.viewAfterChangingView;
|
||||
|
||||
int nextIndex = finalState
|
||||
.getViewStateForView(viewAfterChangingView).notGoneIndex;
|
||||
if (ownIndex >= nextIndex) {
|
||||
// we only have the view afterwards
|
||||
ownIndex++;
|
||||
}
|
||||
int difference = Math.abs(ownIndex - nextIndex);
|
||||
difference = Math.max(0, Math.min(DELAY_EFFECT_MAX_INDEX_DIFFERENCE,
|
||||
difference - 1));
|
||||
long delay = difference * delayPerElement;
|
||||
minDelay = Math.max(delay, minDelay);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return minDelay;
|
||||
}
|
||||
|
||||
private void startHeightAnimation(final ExpandableView child,
|
||||
StackScrollState.ViewState viewState) {
|
||||
StackScrollState.ViewState viewState, long delay) {
|
||||
Integer previousStartValue = getChildTag(child, TAG_START_HEIGHT);
|
||||
Integer previousEndValue = getChildTag(child, TAG_END_HEIGHT);
|
||||
int newEndValue = viewState.height;
|
||||
@@ -185,6 +261,9 @@ public class StackStateAnimator {
|
||||
animator.setInterpolator(mFastOutSlowInInterpolator);
|
||||
long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator);
|
||||
animator.setDuration(newDuration);
|
||||
if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) {
|
||||
animator.setStartDelay(delay);
|
||||
}
|
||||
animator.addListener(getGlobalAnimationFinishedListener());
|
||||
// remove the tag when the animation is finished
|
||||
animator.addListener(new AnimatorListenerAdapter() {
|
||||
@@ -195,14 +274,14 @@ public class StackStateAnimator {
|
||||
child.setTag(TAG_END_HEIGHT, null);
|
||||
}
|
||||
});
|
||||
startInstantly(animator);
|
||||
startAnimator(animator);
|
||||
child.setTag(TAG_ANIMATOR_HEIGHT, animator);
|
||||
child.setTag(TAG_START_HEIGHT, child.getActualHeight());
|
||||
child.setTag(TAG_END_HEIGHT, newEndValue);
|
||||
}
|
||||
|
||||
private void startAlphaAnimation(final ExpandableView child,
|
||||
final StackScrollState.ViewState viewState) {
|
||||
final StackScrollState.ViewState viewState, long delay) {
|
||||
Float previousStartValue = getChildTag(child,TAG_START_ALPHA);
|
||||
Float previousEndValue = getChildTag(child,TAG_END_ALPHA);
|
||||
final float newEndValue = viewState.alpha;
|
||||
@@ -264,6 +343,9 @@ public class StackStateAnimator {
|
||||
});
|
||||
long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator);
|
||||
animator.setDuration(newDuration);
|
||||
if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) {
|
||||
animator.setStartDelay(delay);
|
||||
}
|
||||
animator.addListener(getGlobalAnimationFinishedListener());
|
||||
// remove the tag when the animation is finished
|
||||
animator.addListener(new AnimatorListenerAdapter() {
|
||||
@@ -272,14 +354,14 @@ public class StackStateAnimator {
|
||||
|
||||
}
|
||||
});
|
||||
startInstantly(animator);
|
||||
startAnimator(animator);
|
||||
child.setTag(TAG_ANIMATOR_ALPHA, animator);
|
||||
child.setTag(TAG_START_ALPHA, child.getAlpha());
|
||||
child.setTag(TAG_END_ALPHA, newEndValue);
|
||||
}
|
||||
|
||||
private void startZTranslationAnimation(final ExpandableView child,
|
||||
final StackScrollState.ViewState viewState) {
|
||||
final StackScrollState.ViewState viewState, long delay) {
|
||||
Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Z);
|
||||
Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Z);
|
||||
float newEndValue = viewState.zTranslation;
|
||||
@@ -311,6 +393,9 @@ public class StackStateAnimator {
|
||||
animator.setInterpolator(mFastOutSlowInInterpolator);
|
||||
long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator);
|
||||
animator.setDuration(newDuration);
|
||||
if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) {
|
||||
animator.setStartDelay(delay);
|
||||
}
|
||||
animator.addListener(getGlobalAnimationFinishedListener());
|
||||
// remove the tag when the animation is finished
|
||||
animator.addListener(new AnimatorListenerAdapter() {
|
||||
@@ -321,14 +406,14 @@ public class StackStateAnimator {
|
||||
child.setTag(TAG_END_TRANSLATION_Z, null);
|
||||
}
|
||||
});
|
||||
startInstantly(animator);
|
||||
startAnimator(animator);
|
||||
child.setTag(TAG_ANIMATOR_TRANSLATION_Z, animator);
|
||||
child.setTag(TAG_START_TRANSLATION_Z, child.getTranslationZ());
|
||||
child.setTag(TAG_END_TRANSLATION_Z, newEndValue);
|
||||
}
|
||||
|
||||
private void startYTranslationAnimation(final ExpandableView child,
|
||||
StackScrollState.ViewState viewState) {
|
||||
StackScrollState.ViewState viewState, long delay) {
|
||||
Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Y);
|
||||
Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Y);
|
||||
float newEndValue = viewState.yTranslation;
|
||||
@@ -361,6 +446,9 @@ public class StackStateAnimator {
|
||||
animator.setInterpolator(mFastOutSlowInInterpolator);
|
||||
long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator);
|
||||
animator.setDuration(newDuration);
|
||||
if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) {
|
||||
animator.setStartDelay(delay);
|
||||
}
|
||||
animator.addListener(getGlobalAnimationFinishedListener());
|
||||
// remove the tag when the animation is finished
|
||||
animator.addListener(new AnimatorListenerAdapter() {
|
||||
@@ -371,7 +459,7 @@ public class StackStateAnimator {
|
||||
child.setTag(TAG_END_TRANSLATION_Y, null);
|
||||
}
|
||||
});
|
||||
startInstantly(animator);
|
||||
startAnimator(animator);
|
||||
child.setTag(TAG_ANIMATOR_TRANSLATION_Y, animator);
|
||||
child.setTag(TAG_START_TRANSLATION_Y, child.getTranslationY());
|
||||
child.setTag(TAG_END_TRANSLATION_Y, newEndValue);
|
||||
@@ -425,18 +513,15 @@ public class StackStateAnimator {
|
||||
child.setTag(TAG_END_SCALE, null);
|
||||
}
|
||||
});
|
||||
startInstantly(animator);
|
||||
startAnimator(animator);
|
||||
child.setTag(TAG_ANIMATOR_SCALE, animator);
|
||||
child.setTag(TAG_START_SCALE, child.getScaleX());
|
||||
child.setTag(TAG_END_SCALE, newEndValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start an animator instantly instead of waiting on the next synchronization frame
|
||||
*/
|
||||
public static void startInstantly(ValueAnimator animator) {
|
||||
private void startAnimator(ValueAnimator animator) {
|
||||
mAnimatorSet.add(animator);
|
||||
animator.start();
|
||||
animator.setCurrentPlayTime(0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -468,7 +553,6 @@ public class StackStateAnimator {
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
mAnimatorSet.add(animation);
|
||||
mWasCancelled = false;
|
||||
}
|
||||
};
|
||||
@@ -497,8 +581,6 @@ public class StackStateAnimator {
|
||||
}
|
||||
|
||||
private void onAnimationFinished() {
|
||||
mHandledEvents.clear();
|
||||
mNewEvents.clear();
|
||||
mHostLayout.onChildAnimationFinished();
|
||||
}
|
||||
|
||||
@@ -511,27 +593,60 @@ public class StackStateAnimator {
|
||||
private void processAnimationEvents(
|
||||
ArrayList<NotificationStackScrollLayout.AnimationEvent> animationEvents,
|
||||
StackScrollState finalState) {
|
||||
mNewEvents.clear();
|
||||
for (NotificationStackScrollLayout.AnimationEvent event : animationEvents) {
|
||||
View changingView = event.changingView;
|
||||
if (!mHandledEvents.contains(event)) {
|
||||
if (event.animationType == NotificationStackScrollLayout.AnimationEvent
|
||||
.ANIMATION_TYPE_ADD) {
|
||||
final ExpandableView changingView = (ExpandableView) event.changingView;
|
||||
if (event.animationType ==
|
||||
NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_ADD) {
|
||||
|
||||
// This item is added, initialize it's properties.
|
||||
StackScrollState.ViewState viewState = finalState
|
||||
.getViewStateForView(changingView);
|
||||
if (viewState == null) {
|
||||
// The position for this child was never generated, let's continue.
|
||||
continue;
|
||||
}
|
||||
changingView.setAlpha(0);
|
||||
changingView.setTranslationY(viewState.yTranslation);
|
||||
changingView.setTranslationZ(viewState.zTranslation);
|
||||
// This item is added, initialize it's properties.
|
||||
StackScrollState.ViewState viewState = finalState
|
||||
.getViewStateForView(changingView);
|
||||
if (viewState == null) {
|
||||
// The position for this child was never generated, let's continue.
|
||||
continue;
|
||||
}
|
||||
mHandledEvents.add(event);
|
||||
mNewEvents.add(event);
|
||||
if (changingView.getVisibility() == View.GONE) {
|
||||
// The view was set to gone but the state never removed
|
||||
finalState.removeViewStateForView(changingView);
|
||||
continue;
|
||||
}
|
||||
changingView.setAlpha(viewState.alpha);
|
||||
changingView.setTranslationY(viewState.yTranslation);
|
||||
changingView.setTranslationZ(viewState.zTranslation);
|
||||
changingView.setActualHeight(viewState.height, false);
|
||||
mNewAddChildren.add(changingView);
|
||||
|
||||
} else if (event.animationType ==
|
||||
NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE) {
|
||||
if (changingView.getVisibility() == View.GONE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find the amount to translate up. This is needed in order to understand the
|
||||
// direction of the remove animation (either downwards or upwards)
|
||||
StackScrollState.ViewState viewState = finalState
|
||||
.getViewStateForView(event.viewAfterChangingView);
|
||||
int actualHeight = changingView.getActualHeight();
|
||||
// upwards by default
|
||||
float translationDirection = -1.0f;
|
||||
if (viewState != null) {
|
||||
// there was a view after this one, Approximate the distance the next child
|
||||
// travelled
|
||||
translationDirection = ((viewState.yTranslation
|
||||
- (changingView.getTranslationY() + actualHeight / 2.0f)) * 2 /
|
||||
actualHeight);
|
||||
translationDirection = Math.max(Math.min(translationDirection, 1.0f),-1.0f);
|
||||
|
||||
}
|
||||
changingView.performRemoveAnimation(translationDirection, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// remove the temporary overlay
|
||||
mHostLayout.getOverlay().remove(changingView);
|
||||
}
|
||||
});
|
||||
}
|
||||
mNewEvents.add(event);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user