Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 215e25f2 authored by Selim Cinek's avatar Selim Cinek Committed by Android Git Automerger
Browse files

am 739b38cb: Merge "Improved the animation logic of the stack scroller." into lmp-preview-dev

* commit '739b38cb6fd8cc192686f0fbac6de270dd83e479':
  Improved the animation logic of the stack scroller.
parents 94d76b3b 8a4729d8
Loading
Loading
Loading
Loading
+259 −3
Original line number Diff line number Diff line
@@ -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;
    }
+20 −10
Original line number Diff line number Diff line
@@ -1131,6 +1131,7 @@ public abstract class BaseStatusBar extends SystemUI implements
        if (rowParent != null) rowParent.removeView(entry.row);
        updateRowStates();
        updateNotificationIcons();
        updateSpeedBump();

        return entry.notification;
    }
@@ -1174,8 +1175,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) {
@@ -1195,7 +1210,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);
@@ -1216,17 +1230,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) {
@@ -1234,8 +1245,6 @@ public abstract class BaseStatusBar extends SystemUI implements
        } else {
            mKeyguardIconOverflowContainer.setVisibility(View.GONE);
        }

        mStackScroller.updateSpeedBumpIndex(speedBumpIndex);
    }

    private boolean shouldShowOnKeyguard(StatusBarNotification sbn) {
@@ -1400,6 +1409,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.
+35 −6
Original line number Diff line number Diff line
@@ -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,7 +53,32 @@ public abstract class ExpandableOutlineView extends ExpandableView {
        updateOutline();
    }

    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(),
@@ -58,3 +86,4 @@ public abstract class ExpandableOutlineView extends ExpandableView {
            setOutline(mOutline);
        }
    }
}
+15 −0
Original line number Diff line number Diff line
@@ -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.
     */
+0 −1
Original line number Diff line number Diff line
@@ -34,7 +34,6 @@ public class NotificationBackgroundView extends View {

    public NotificationBackgroundView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setWillNotDraw(false);
    }

    @Override
Loading