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

Commit 80a42c0f authored by Lyn Han's avatar Lyn Han
Browse files

New shelf shape, notification corner animations on scroll

Bug: 182486170
Fixes: 186168632

Test: visual
- shelf shape stays constant (top/bottom corners stay round)
- notification scrolling up from shelf first shows round
  top/bottom corners, then "merges" top corners with notification
  above if in same section; vice versa happens for scroll down
- on lockscreen, notif before shelf has round bottom corners
- no change while dozing/pulsing

Change-Id: I1ef915e3b91cb16e6277f8eaa1bff63dcc425dde
parent f5cded5a
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -181,6 +181,9 @@
    <!-- Radius for notifications corners without adjacent notifications -->
    <dimen name="notification_corner_radius">28dp</dimen>

    <!-- Distance over which notification corner animations run, near the shelf while scrolling. -->
    <dimen name="notification_corner_animation_distance">48dp</dimen>

    <!-- Radius for notifications corners with adjacent notifications -->
    <dimen name="notification_corner_radius_small">4dp</dimen>

+41 −21
Original line number Diff line number Diff line
@@ -84,7 +84,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
    private int mCutoutHeight;
    private int mGapHeight;
    private int mIndexOfFirstViewInShelf = -1;

    private float mCornerAnimationDistance;
    private NotificationShelfController mController;

    public NotificationShelf(Context context, AttributeSet attrs) {
@@ -104,6 +104,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
        setClipToPadding(false);
        mShelfIcons.setIsStaticLayout(false);
        setBottomRoundness(1.0f, false /* animate */);
        setTopRoundness(1f, false /* animate */);

        // Setting this to first in section to get the clipping to the top roundness correct. This
        // value determines the way we are clipping to the top roundness of the overall shade
@@ -134,6 +135,8 @@ public class NotificationShelf extends ActivatableNotificationView implements
        mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);
        mHiddenShelfIconSize = res.getDimensionPixelOffset(R.dimen.hidden_shelf_icon_size);
        mGapHeight = res.getDimensionPixelSize(R.dimen.qs_notification_padding);
        mCornerAnimationDistance = res.getDimensionPixelSize(
                R.dimen.notification_corner_animation_distance);

        mShelfIcons.setInNotificationIconShelf(true);
        if (!mShowNotificationShelf) {
@@ -256,7 +259,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
            boolean aboveShelf = ViewState.getFinalTranslationZ(child) > baseZHeight
                    || child.isPinned();
            boolean isLastChild = child == lastChild;
            float rowTranslationY = child.getTranslationY();
            final float viewStart = child.getTranslationY();

            final float inShelfAmount = updateShelfTransformation(i, child, scrollingFast,
                    expandingAnimated, isLastChild);
@@ -278,7 +281,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
                ExpandableNotificationRow expandableRow = (ExpandableNotificationRow) child;
                numViewsInShelf += inShelfAmount;
                int ownColorUntinted = expandableRow.getBackgroundColorWithoutTint();
                if (rowTranslationY >= shelfStart && mNotGoneIndex == -1) {
                if (viewStart >= shelfStart && mNotGoneIndex == -1) {
                    mNotGoneIndex = notGoneIndex;
                    setTintColor(previousColor);
                    setOverrideTintColor(colorTwoBefore, transitionAmount);
@@ -317,26 +320,44 @@ public class NotificationShelf extends ActivatableNotificationView implements
                notGoneIndex++;
            }

            final float viewEnd = viewStart + child.getActualHeight();
            final float cornerAnimationDistance = mCornerAnimationDistance
                    * mAmbientState.getExpansionFraction();
            final float cornerAnimationTop = shelfStart - cornerAnimationDistance;

            if (child instanceof ActivatableNotificationView) {
                ActivatableNotificationView anv =
                        (ActivatableNotificationView) child;
                if (anv.isFirstInSection() && previousAnv != null
                        && previousAnv.isLastInSection()) {
                    // If the top of the shelf is between the view before a gap and the view after a
                    // gap then we need to adjust the shelf's top roundness.
                    float distanceToGapBottom = child.getTranslationY() - getTranslationY();
                    float distanceToGapTop = getTranslationY()
                            - (previousAnv.getTranslationY() + previousAnv.getActualHeight());
                    if (distanceToGapTop > 0) {
                        // We interpolate our top roundness so that it's fully rounded if we're at
                        // the bottom of the gap, and not rounded at all if we're at the top of the
                        // gap (directly up against the bottom of previousAnv)
                        // Then we apply the same roundness to the bottom of previousAnv so that the
                        // corners join together as the shelf approaches previousAnv.
                        firstElementRoundness = (float) Math.min(1.0,
                                distanceToGapTop / mGapHeight);
                        previousAnv.setBottomRoundness(firstElementRoundness,
                                false /* don't animate */);

                if (viewStart < shelfStart
                        && !mHostLayoutController.isViewAffectedBySwipe(anv)
                        && !mAmbientState.isPulsing()
                        && !mAmbientState.isDozing()) {

                    if (viewEnd >= cornerAnimationTop) {
                        // Round bottom corners within animation bounds
                        final float changeFraction = MathUtils.saturate(
                                (viewEnd - cornerAnimationTop) / cornerAnimationDistance);
                        final float roundness = anv.isLastInSection() ? 1f : changeFraction * 1f;
                        anv.setBottomRoundness(roundness, false);

                    } else if (viewEnd < cornerAnimationTop) {
                        // Fast scroll skips frames and leaves corners with unfinished rounding.
                        // Reset top and bottom corners outside of animation bounds.
                        anv.setBottomRoundness(anv.isLastInSection() ? 1f : 0f, false);
                    }

                    if (viewStart >= cornerAnimationTop) {
                        // Round top corners within animation bounds
                        final float changeFraction = MathUtils.saturate(
                                (viewStart - cornerAnimationTop) / cornerAnimationDistance);
                        final float roundness = anv.isFirstInSection() ? 1f : changeFraction * 1f;
                        anv.setTopRoundness(roundness, false);

                    } else if (viewStart < cornerAnimationTop) {
                        // Fast scroll skips frames and leaves corners with unfinished rounding.
                        // Reset top and bottom corners outside of animation bounds.
                        anv.setTopRoundness(anv.isFirstInSection() ? 1f : 0f, false);
                    }
                }
                previousAnv = anv;
@@ -394,7 +415,6 @@ public class NotificationShelf extends ActivatableNotificationView implements
    private void setFirstElementRoundness(float firstElementRoundness) {
        if (mFirstElementRoundness != firstElementRoundness) {
            mFirstElementRoundness = firstElementRoundness;
            setTopRoundness(firstElementRoundness, false /* animate */);
        }
    }

+1 −2
Original line number Diff line number Diff line
@@ -84,8 +84,7 @@ public class NotificationBackgroundView extends View {
            int bottom = mActualHeight;
            if (mBottomIsRounded
                    && mBottomAmountClips
                    && !mExpandAnimationRunning
                    && !mLastInSection) {
                    && !mExpandAnimationRunning) {
                bottom -= mClipBottomAmount;
            }
            int left = 0;
+29 −27
Original line number Diff line number Diff line
@@ -71,6 +71,13 @@ public class NotificationRoundnessManager {
        }
    }

    public boolean isViewAffectedBySwipe(ExpandableView expandableView) {
        return expandableView != null
                && (expandableView == mSwipedView
                    || expandableView == mViewBeforeSwipedView
                    || expandableView == mViewAfterSwipedView);
    }

    boolean updateViewWithoutCallback(ExpandableView view,
            boolean animate) {
        if (view == null
@@ -78,38 +85,35 @@ public class NotificationRoundnessManager {
                || view == mViewAfterSwipedView) {
            return false;
        }
        float topRoundness = getRoundness(view, true /* top */);
        float bottomRoundness = getRoundness(view, false /* top */);
        boolean topChanged = view.setTopRoundness(topRoundness, animate);
        boolean bottomChanged = view.setBottomRoundness(bottomRoundness, animate);
        boolean firstInSection = isFirstInSection(view, false /* exclude first section */);
        boolean lastInSection = isLastInSection(view, false /* exclude last section */);
        view.setFirstInSection(firstInSection);
        view.setLastInSection(lastInSection);
        return (firstInSection || lastInSection) && (topChanged || bottomChanged);

        final float topRoundness = getRoundness(view, true /* top */);
        final float bottomRoundness = getRoundness(view, false /* top */);

        final boolean topChanged = view.setTopRoundness(topRoundness, animate);
        final boolean bottomChanged = view.setBottomRoundness(bottomRoundness, animate);

        final boolean isFirstInSection = isFirstInSection(view);
        final boolean isLastInSection = isLastInSection(view);

        view.setFirstInSection(isFirstInSection);
        view.setLastInSection(isLastInSection);

        return (isFirstInSection || isLastInSection) && (topChanged || bottomChanged);
    }

    private boolean isFirstInSection(ExpandableView view, boolean includeFirstSection) {
        int numNonEmptySections = 0;
    private boolean isFirstInSection(ExpandableView view) {
        for (int i = 0; i < mFirstInSectionViews.length; i++) {
            if (view == mFirstInSectionViews[i]) {
                return includeFirstSection || numNonEmptySections > 0;
            }
            if (mFirstInSectionViews[i] != null) {
                numNonEmptySections++;
                return true;
            }
        }
        return false;
    }

    private boolean isLastInSection(ExpandableView view, boolean includeLastSection) {
        int numNonEmptySections = 0;
    private boolean isLastInSection(ExpandableView view) {
        for (int i = mLastInSectionViews.length - 1; i >= 0; i--) {
            if (view == mLastInSectionViews[i]) {
                return includeLastSection || numNonEmptySections > 0;
            }
            if (mLastInSectionViews[i] != null) {
                numNonEmptySections++;
                return true;
            }
        }
        return false;
@@ -172,10 +176,10 @@ public class NotificationRoundnessManager {
                || (view.isHeadsUpAnimatingAway()) && !mExpanded)) {
            return 1.0f;
        }
        if (isFirstInSection(view, true /* include first section */) && top) {
        if (isFirstInSection(view) && top) {
            return 1.0f;
        }
        if (isLastInSection(view, true /* include last section */) && !top) {
        if (isLastInSection(view) && !top) {
            return 1.0f;
        }
        if (view == mTrackedHeadsUp) {
@@ -229,10 +233,8 @@ public class NotificationRoundnessManager {
                                    : section.getLastVisibleChild());
                    if (newView == oldView) {
                        isStillPresent = true;
                        if (oldView.isFirstInSection() != isFirstInSection(oldView,
                                false /* exclude first section */)
                                || oldView.isLastInSection() != isLastInSection(oldView,
                                false /* exclude last section */)) {
                        if (oldView.isFirstInSection() != isFirstInSection(oldView)
                                || oldView.isLastInSection() != isLastInSection(oldView)) {
                            adjacentSectionChanged = true;
                        }
                        break;
+4 −0
Original line number Diff line number Diff line
@@ -740,6 +740,10 @@ public class NotificationStackScrollLayoutController {
        return true;
    }

    public boolean isViewAffectedBySwipe(ExpandableView expandableView) {
        return mNotificationRoundnessManager.isViewAffectedBySwipe(expandableView);
    }

    public void addOnExpandedHeightChangedListener(BiConsumer<Float, Float> listener) {
        mView.addOnExpandedHeightChangedListener(listener);
    }
Loading