summaryrefslogtreecommitdiffstats
path: root/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
diff options
context:
space:
mode:
Diffstat (limited to 'packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java')
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java262
1 files changed, 259 insertions, 3 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index ac16164..e3dac4a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -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;
}