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

Commit 9d4e6911 authored by Selim Cinek's avatar Selim Cinek Committed by Android Git Automerger
Browse files

am 807a8155: Introduced animations for the clipTopAmount of notifications.

* commit '807a81552eae005882c65df2c01ce2bed9478f4a':
  Introduced animations for the clipTopAmount of notifications.
parents 844ec7b9 708a6c12
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ public class AnimationFilter {
    boolean animateZ;
    boolean animateScale;
    boolean animateHeight;
    boolean animateTopInset;
    boolean animateDimmed;
    boolean hasDelays;

@@ -60,6 +61,11 @@ public class AnimationFilter {
        return this;
    }

    public AnimationFilter animateTopInset() {
        animateTopInset = true;
        return this;
    }

    public AnimationFilter animateDimmed() {
        animateDimmed = true;
        return this;
@@ -84,6 +90,7 @@ public class AnimationFilter {
        animateZ |= filter.animateZ;
        animateScale |= filter.animateScale;
        animateHeight |= filter.animateHeight;
        animateTopInset |= filter.animateTopInset;
        animateDimmed |= filter.animateDimmed;
        hasDelays |= filter.hasDelays;
    }
@@ -94,6 +101,7 @@ public class AnimationFilter {
        animateZ = false;
        animateScale = false;
        animateHeight = false;
        animateTopInset = false;
        animateDimmed = false;
        hasDelays = false;
    }
+5 −0
Original line number Diff line number Diff line
@@ -1599,6 +1599,7 @@ public class NotificationStackScrollLayout extends ViewGroup
                new AnimationFilter()
                        .animateAlpha()
                        .animateHeight()
                        .animateTopInset()
                        .animateY()
                        .animateZ()
                        .hasDelays(),
@@ -1607,6 +1608,7 @@ public class NotificationStackScrollLayout extends ViewGroup
                new AnimationFilter()
                        .animateAlpha()
                        .animateHeight()
                        .animateTopInset()
                        .animateY()
                        .animateZ()
                        .hasDelays(),
@@ -1615,6 +1617,7 @@ public class NotificationStackScrollLayout extends ViewGroup
                new AnimationFilter()
                        .animateAlpha()
                        .animateHeight()
                        .animateTopInset()
                        .animateY()
                        .animateZ()
                        .hasDelays(),
@@ -1623,6 +1626,7 @@ public class NotificationStackScrollLayout extends ViewGroup
                new AnimationFilter()
                        .animateAlpha()
                        .animateHeight()
                        .animateTopInset()
                        .animateY()
                        .animateDimmed()
                        .animateScale()
@@ -1651,6 +1655,7 @@ public class NotificationStackScrollLayout extends ViewGroup
                new AnimationFilter()
                        .animateAlpha()
                        .animateHeight()
                        .animateTopInset()
                        .animateY()
                        .animateZ()
        };
+64 −0
Original line number Diff line number Diff line
@@ -49,6 +49,7 @@ public class StackScrollAlgorithm {
    private int mBottomStackPeekSize;
    private int mZDistanceBetweenElements;
    private int mZBasicHeight;
    private int mRoundedRectCornerRadius;

    private StackIndentationFunctor mTopStackIndentationFunctor;
    private StackIndentationFunctor mBottomStackIndentationFunctor;
@@ -111,6 +112,8 @@ public class StackScrollAlgorithm {
        mZBasicHeight = (MAX_ITEMS_IN_BOTTOM_STACK + 1) * mZDistanceBetweenElements;
        mBottomStackSlowDownLength = context.getResources()
                .getDimensionPixelSize(R.dimen.bottom_stack_slow_down_length);
        mRoundedRectCornerRadius = context.getResources().getDimensionPixelSize(
                com.android.internal.R.dimen.notification_quantum_rounded_rect_radius);
    }


@@ -146,6 +149,67 @@ public class StackScrollAlgorithm {

        handleDraggedViews(ambientState, resultState, algorithmState);
        updateDimmedActivated(ambientState, resultState, algorithmState);
        updateClipping(resultState, algorithmState);
    }

    private void updateClipping(StackScrollState resultState,
            StackScrollAlgorithmState algorithmState) {
        float previousNotificationEnd = 0;
        float previousNotificationStart = 0;
        boolean previousNotificationIsSwiped = false;
        int childCount = algorithmState.visibleChildren.size();
        for (int i = 0; i < childCount; i++) {
            ExpandableView child = algorithmState.visibleChildren.get(i);
            StackScrollState.ViewState state = resultState.getViewStateForView(child);
            float newYTranslation = state.yTranslation;
            int newHeight = state.height;
            // apply clipping and shadow
            float newNotificationEnd = newYTranslation + newHeight;

            // In the unlocked shade we have to clip a little bit higher because of the rounded
            // corners of the notifications.
            float clippingCorrection = state.dimmed ? 0 : mRoundedRectCornerRadius;

            // When the previous notification is swiped, we don't clip the content to the
            // bottom of it.
            float clipHeight = previousNotificationIsSwiped
                    ? newHeight
                    : newNotificationEnd - (previousNotificationEnd - clippingCorrection);

            updateChildClippingAndBackground(state, newHeight, clipHeight,
                    (int) (newHeight - (previousNotificationStart - newYTranslation)));

            if (!child.isTransparent()) {
                // Only update the previous values if we are not transparent,
                // otherwise we would clip to a transparent view.
                previousNotificationStart = newYTranslation + child.getClipTopAmount();
                previousNotificationEnd = newNotificationEnd;
                previousNotificationIsSwiped = child.getTranslationX() != 0;
            }
        }
    }

    /**
     * Updates the shadow outline and the clipping for a view.
     *
     * @param state the viewState to update
     * @param realHeight the currently applied height of the view
     * @param clipHeight the desired clip height, the rest of the view will be clipped from the top
     * @param backgroundHeight the desired background height. The shadows of the view will be
     *                         based on this height and the content will be clipped from the top
     */
    private void updateChildClippingAndBackground(StackScrollState.ViewState state, int realHeight,
            float clipHeight, int backgroundHeight) {
        if (realHeight > clipHeight) {
            state.topOverLap = (int) (realHeight - clipHeight);
        } else {
            state.topOverLap = 0;
        }
        if (realHeight > backgroundHeight) {
            state.clipTopAmount = (realHeight - backgroundHeight);
        } else {
            state.clipTopAmount = 0;
        }
    }

    /**
+38 −70
Original line number Diff line number Diff line
@@ -16,7 +16,6 @@

package com.android.systemui.statusbar.stack;

import android.graphics.Outline;
import android.graphics.Rect;
import android.util.Log;
import android.view.View;
@@ -37,15 +36,12 @@ public class StackScrollState {
    private static final String CHILD_NOT_FOUND_TAG = "StackScrollStateNoSuchChild";

    private final ViewGroup mHostView;
    private final int mRoundedRectCornerRadius;
    private Map<ExpandableView, ViewState> mStateMap;
    private final Rect mClipRect = new Rect();

    public StackScrollState(ViewGroup hostView) {
        mHostView = hostView;
        mStateMap = new HashMap<ExpandableView, ViewState>();
        mRoundedRectCornerRadius = mHostView.getResources().getDimensionPixelSize(
                com.android.internal.R.dimen.notification_quantum_rounded_rect_radius);
    }

    public ViewGroup getHostView() {
@@ -83,9 +79,6 @@ public class StackScrollState {
     */
    public void apply() {
        int numChildren = mHostView.getChildCount();
        float previousNotificationEnd = 0;
        float previousNotificationStart = 0;
        boolean previousNotificationIsSwiped = false;
        for (int i = 0; i < numChildren; i++) {
            ExpandableView child = (ExpandableView) mHostView.getChildAt(i);
            ViewState state = mStateMap.get(child);
@@ -155,39 +148,41 @@ public class StackScrollState {
                // apply dimming
                child.setDimmed(state.dimmed, false /* animate */);

                // apply clipping and shadow
                float newNotificationEnd = newYTranslation + newHeight;

                // In the unlocked shade we have to clip a little bit higher because of the rounded
                // corners of the notifications.
                float clippingCorrection = state.dimmed ? 0 : mRoundedRectCornerRadius;

                // When the previous notification is swiped, we don't clip the content to the
                // bottom of it.
                float clipHeight = previousNotificationIsSwiped
                        ? newHeight
                        : newNotificationEnd - (previousNotificationEnd - clippingCorrection);

                updateChildClippingAndBackground(child, newHeight,
                        clipHeight,
                        (int) (newHeight - (previousNotificationStart - newYTranslation)));

                if (!child.isTransparent()) {
                    // Only update the previous values if we are not transparent,
                    // otherwise we would clip to a transparent view.
                    previousNotificationStart = newYTranslation + child.getClipTopAmount();
                    previousNotificationEnd = newNotificationEnd;
                    previousNotificationIsSwiped = child.getTranslationX() != 0;
                float oldClipTopAmount = child.getClipTopAmount();
                if (oldClipTopAmount != state.clipTopAmount) {
                    child.setClipTopAmount(state.clipTopAmount);
                }

                if (state.topOverLap != 0) {
                    updateChildClip(child, newHeight, state.topOverLap);
                } else {
                    child.setClipBounds(null);
                }

                if(child instanceof SpeedBumpView) {
                    performSpeedBumpAnimation(i, (SpeedBumpView) child, newNotificationEnd,
                    float speedBumpEnd = newYTranslation + newHeight;
                    performSpeedBumpAnimation(i, (SpeedBumpView) child, speedBumpEnd,
                            newYTranslation);
                }
            }
        }
    }

    /**
     * Updates the clipping of a view
     *
     * @param child the view to update
     * @param height the currently applied height of the view
     * @param clipInset how much should this view be clipped from the top
     */
    private void updateChildClip(View child, int height, int clipInset) {
        mClipRect.set(0,
                clipInset,
                child.getWidth(),
                height);
        child.setClipBounds(mClipRect);
    }

    private void performSpeedBumpAnimation(int i, SpeedBumpView speedBump, float speedBumpEnd,
            float speedBumpStart) {
        View nextChild = getNextChildNotGone(i);
@@ -216,45 +211,6 @@ public class StackScrollState {
        return null;
    }

    /**
     * Updates the shadow outline and the clipping for a view.
     *
     * @param child the view to update
     * @param realHeight the currently applied height of the view
     * @param clipHeight the desired clip height, the rest of the view will be clipped from the top
     * @param backgroundHeight the desired background height. The shadows of the view will be
     *                         based on this height and the content will be clipped from the top
     */
    private void updateChildClippingAndBackground(ExpandableView child, int realHeight,
            float clipHeight, int backgroundHeight) {
        if (realHeight > clipHeight) {
            updateChildClip(child, realHeight, clipHeight);
        } else {
            child.setClipBounds(null);
        }
        if (realHeight > backgroundHeight) {
            child.setClipTopAmount(realHeight - backgroundHeight);
        } else {
            child.setClipTopAmount(0);
        }
    }

    /**
     * Updates the clipping of a view
     *
     * @param child the view to update
     * @param height the currently applied height of the view
     * @param clipHeight the desired clip height, the rest of the view will be clipped from the top
     */
    private void updateChildClip(View child, int height, float clipHeight) {
        int clipInset = (int) (height - clipHeight);
        mClipRect.set(0,
                clipInset,
                child.getWidth(),
                height);
        child.setClipBounds(mClipRect);
    }

    public static class ViewState {

        // These are flags such that we can create masks for filtering.
@@ -275,6 +231,18 @@ public class StackScrollState {
        float scale;
        boolean dimmed;

        /**
         * The amount which the view should be clipped from the top. This is calculated to
         * perceive consistent shadows.
         */
        int clipTopAmount;

        /**
         * How much does the child overlap with the previous view on the top? Can be used for
         * a clipping optimization
         */
        int topOverLap;

        /**
         * The index of the view, only accounting for views not equal to GONE
         */
+65 −1
Original line number Diff line number Diff line
@@ -133,10 +133,11 @@ public class StackStateAnimator {
        boolean scaleChanging = child.getScaleX() != viewState.scale;
        boolean alphaChanging = alpha != child.getAlpha();
        boolean heightChanging = viewState.height != child.getActualHeight();
        boolean topInsetChanging = viewState.clipTopAmount != child.getClipTopAmount();
        boolean wasAdded = mNewAddChildren.contains(child);
        boolean hasDelays = mAnimationFilter.hasDelays;
        boolean isDelayRelevant = yTranslationChanging || zTranslationChanging || scaleChanging ||
                alphaChanging || heightChanging;
                alphaChanging || heightChanging || topInsetChanging;
        long delay = 0;
        if (hasDelays && isDelayRelevant || wasAdded) {
            delay = calculateChildAnimationDelay(viewState, finalState);
@@ -167,6 +168,11 @@ public class StackStateAnimator {
            startHeightAnimation(child, viewState, delay);
        }

        // start top inset animation
        if (topInsetChanging) {
            startInsetAnimation(child, viewState, delay);
        }

        // start dimmed animation
        child.setDimmed(viewState.dimmed, mAnimationFilter.animateDimmed);

@@ -280,6 +286,64 @@ public class StackStateAnimator {
        child.setTag(TAG_END_HEIGHT, newEndValue);
    }

    private void startInsetAnimation(final ExpandableView child,
            StackScrollState.ViewState viewState, long delay) {
        Integer previousStartValue = getChildTag(child, TAG_START_TOP_INSET);
        Integer previousEndValue = getChildTag(child, TAG_END_TOP_INSET);
        int newEndValue = viewState.clipTopAmount;
        if (previousEndValue != null && previousEndValue == newEndValue) {
            return;
        }
        ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TOP_INSET);
        if (!mAnimationFilter.animateTopInset) {
            // just a local update was performed
            if (previousAnimator != null) {
                // we need to increase all animation keyframes of the previous animator by the
                // relative change to the end value
                PropertyValuesHolder[] values = previousAnimator.getValues();
                int relativeDiff = newEndValue - previousEndValue;
                int newStartValue = previousStartValue + relativeDiff;
                values[0].setIntValues(newStartValue, newEndValue);
                child.setTag(TAG_START_TOP_INSET, newStartValue);
                child.setTag(TAG_END_TOP_INSET, newEndValue);
                previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
                return;
            } else {
                // no new animation needed, let's just apply the value
                child.setClipTopAmount(newEndValue);
                return;
            }
        }

        ValueAnimator animator = ValueAnimator.ofInt(child.getClipTopAmount(), newEndValue);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                child.setClipTopAmount((int) animation.getAnimatedValue());
            }
        });
        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() {
            @Override
            public void onAnimationEnd(Animator animation) {
                child.setTag(TAG_ANIMATOR_TOP_INSET, null);
                child.setTag(TAG_START_TOP_INSET, null);
                child.setTag(TAG_END_TOP_INSET, null);
            }
        });
        startAnimator(animator);
        child.setTag(TAG_ANIMATOR_TOP_INSET, animator);
        child.setTag(TAG_START_TOP_INSET, child.getClipTopAmount());
        child.setTag(TAG_END_TOP_INSET, newEndValue);
    }

    private void startAlphaAnimation(final ExpandableView child,
            final StackScrollState.ViewState viewState, long delay) {
        Float previousStartValue = getChildTag(child,TAG_START_ALPHA);