From 982d5430b8469bf45bbaa5d6009d04e08cf2aac4 Mon Sep 17 00:00:00 2001 From: Selim Cinek Date: Mon, 14 Jun 2021 14:09:06 +0200 Subject: [PATCH] Animating when the qs boundary changes due to notification interactions When dismissing notifications, the logic how the scrolling was adapted wasn't updated yet, leading to accidental scrolls after dismisses. Additionally, we're now animating these changes properly when the boundary changes for these events. Fixes: 188439802 Test: have a couple of notifications, scroll, dismiss the first notification, observe nice animation Change-Id: Ic4faec910a7ca82fd283ab2053ddb4222668a161 --- .../stack/NotificationStackScrollLayout.java | 71 ++++++++++++++++--- ...tificationStackScrollLayoutController.java | 2 +- .../NotificationPanelViewController.java | 10 +-- 3 files changed, 70 insertions(+), 13 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index 4bfa03b6982a2..d79c57565dcdc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -425,13 +425,19 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private final Rect mBackgroundAnimationRect = new Rect(); private ArrayList> mExpandedHeightListeners = new ArrayList<>(); private int mHeadsUpInset; + + /** + * The position of the scroll boundary relative to this view. This is where the notifications + * stop scrolling and will start to clip instead. + */ + private int mQsScrollBoundaryPosition; private HeadsUpAppearanceController mHeadsUpAppearanceController; private final Rect mTmpRect = new Rect(); private DismissListener mDismissListener; private DismissAllAnimationListener mDismissAllAnimationListener; private NotificationRemoteInputManager mRemoteInputManager; private ShadeController mShadeController; - private Runnable mOnStackYChanged; + private Consumer mOnStackYChanged; protected boolean mClearAllEnabled; @@ -471,6 +477,12 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private int mRoundedRectClippingRight; private float[] mBgCornerRadii = new float[8]; + /** + * Whether stackY should be animated in case the view is getting shorter than the scroll + * position and this scrolling will lead to the top scroll inset getting smaller. + */ + private boolean mAnimateStackYForContentHeightChange = false; + /** * Are we launching a notification right now */ @@ -889,6 +901,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mCornerRadius = res.getDimensionPixelSize(R.dimen.notification_corner_radius); mHeadsUpInset = mStatusBarHeight + res.getDimensionPixelSize( R.dimen.heads_up_status_bar_padding); + mQsScrollBoundaryPosition = res.getDimensionPixelSize( + com.android.internal.R.dimen.quick_qs_offset_height); } void updateCornerRadius() { @@ -988,6 +1002,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable updateFirstAndLastBackgroundViews(); updateAlgorithmLayoutMinHeight(); updateOwnTranslationZ(); + + // Once the layout has finished, we don't need to animate any scrolling clampings anymore. + mAnimateStackYForContentHeightChange = false; } @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) @@ -1122,7 +1139,13 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private void clampScrollPosition() { int scrollRange = getScrollRange(); if (scrollRange < mOwnScrollY) { - setOwnScrollY(scrollRange); + boolean animateStackY = false; + if (scrollRange < getScrollAmountToScrollBoundary() + && mAnimateStackYForContentHeightChange) { + // if the scroll boundary updates the position of the stack, + animateStackY = true; + } + setOwnScrollY(scrollRange, animateStackY); } } @@ -1151,6 +1174,14 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable * Apply expansion fraction to the y position and height of the notifications panel. */ private void updateStackPosition() { + updateStackPosition(false /* listenerNeedsAnimation */); + } + + /** + * Apply expansion fraction to the y position and height of the notifications panel. + * @param listenerNeedsAnimation does the listener need to animate? + */ + private void updateStackPosition(boolean listenerNeedsAnimation) { // Consider interpolating from an mExpansionStartY for use on lockscreen and AOD float endTopPosition = mTopPadding + mExtraTopInsetForFullShadeTransition + mAmbientState.getOverExpansion(); @@ -1158,7 +1189,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable final float stackY = MathUtils.lerp(0, endTopPosition, fraction); mAmbientState.setStackY(stackY); if (mOnStackYChanged != null) { - mOnStackYChanged.run(); + mOnStackYChanged.accept(listenerNeedsAnimation); } if (mQsExpansionFraction <= 0) { final float stackEndHeight = Math.max(0f, @@ -1170,7 +1201,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } } - void setOnStackYChanged(Runnable onStackYChanged) { + /** + * Add a listener when the StackY changes. The argument signifies whether an animation is + * needed. + */ + void setOnStackYChanged(Consumer onStackYChanged) { mOnStackYChanged = onStackYChanged; } @@ -2670,17 +2705,27 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable final int startingPosition = getPositionInLinearLayout(removedChild); final int childHeight = getIntrinsicHeight(removedChild) + mPaddingBetweenElements; final int endPosition = startingPosition + childHeight; - if (endPosition <= mOwnScrollY) { + final int scrollBoundaryStart = getScrollAmountToScrollBoundary(); + mAnimateStackYForContentHeightChange = true; + // This is reset onLayout + if (endPosition <= mOwnScrollY - scrollBoundaryStart) { // This child is fully scrolled of the top, so we have to deduct its height from the // scrollPosition setOwnScrollY(mOwnScrollY - childHeight); - } else if (startingPosition < mOwnScrollY) { + } else if (startingPosition < mOwnScrollY - scrollBoundaryStart) { // This child is currently being scrolled into, set the scroll position to the // start of this child - setOwnScrollY(startingPosition); + setOwnScrollY(startingPosition + scrollBoundaryStart); } } + /** + * @return the amount of scrolling needed to start clipping notifications. + */ + private int getScrollAmountToScrollBoundary() { + return mTopPadding - mQsScrollBoundaryPosition; + } + @ShadeViewRefactor(RefactorComponent.COORDINATOR) private int getIntrinsicHeight(View view) { if (view instanceof ExpandableView) { @@ -3832,6 +3877,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } void onChildHeightChanged(ExpandableView view, boolean needsAnimation) { + boolean previouslyNeededAnimation = mAnimateStackYForContentHeightChange; + if (needsAnimation) { + mAnimateStackYForContentHeightChange = true; + } updateContentHeight(); updateScrollPositionOnExpandInBottom(view); clampScrollPosition(); @@ -3852,6 +3901,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable requestAnimationOnViewResize(row); } requestChildrenUpdate(); + mAnimateStackYForContentHeightChange = previouslyNeededAnimation; } void onChildHeightReset(ExpandableView view) { @@ -4577,13 +4627,18 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable @ShadeViewRefactor(RefactorComponent.COORDINATOR) private void setOwnScrollY(int ownScrollY) { + setOwnScrollY(ownScrollY, false /* animateScrollChangeListener */); + } + + @ShadeViewRefactor(RefactorComponent.COORDINATOR) + private void setOwnScrollY(int ownScrollY, boolean animateStackYChangeListener) { if (ownScrollY != mOwnScrollY) { // We still want to call the normal scrolled changed for accessibility reasons onScrollChanged(mScrollX, ownScrollY, mScrollX, mOwnScrollY); mOwnScrollY = ownScrollY; mAmbientState.setScrollY(mOwnScrollY); updateOnScrollChange(); - updateStackPosition(); + updateStackPosition(animateStackYChangeListener); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index 0f8fc14f9d36e..fb4f5592e97f0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -1023,7 +1023,7 @@ public class NotificationStackScrollLayoutController { mView.setQsExpansionFraction(expansionFraction); } - public void setOnStackYChanged(Runnable onStackYChanged) { + public void setOnStackYChanged(Consumer onStackYChanged) { mView.setOnStackYChanged(onStackYChanged); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index eef519fef16c8..35d1526c4c593 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -830,6 +830,7 @@ public class NotificationPanelViewController extends PanelViewController { mNotificationStackScrollLayoutController.setOverscrollTopChangedListener( mOnOverscrollTopChangedListener); mNotificationStackScrollLayoutController.setOnScrollListener(this::onNotificationScrolled); + mNotificationStackScrollLayoutController.setOnStackYChanged(this::onStackYChanged); mNotificationStackScrollLayoutController.setOnEmptySpaceClickListener( mOnEmptySpaceClickListener); addTrackingHeadsUpListener(mNotificationStackScrollLayoutController::setTrackingHeadsUp); @@ -2198,15 +2199,17 @@ public class NotificationPanelViewController extends PanelViewController { mDepthController.setQsPanelExpansion(qsExpansionFraction); } - private Runnable mOnStackYChanged = () -> { + private void onStackYChanged(boolean shouldAnimate) { if (mQs != null) { + if (shouldAnimate) { + mAnimateNextNotificationBounds = true; + mNotificationBoundsAnimationDelay = 0; + } setQSClippingBounds(); } }; private void onNotificationScrolled(int newScrollPosition) { - // Since this is an overscroller, sometimes the scrollY can be temporarily negative - // (when overscrollng on the top and flinging). Let's updateQSExpansionEnabledAmbient(); } @@ -3335,7 +3338,6 @@ public class NotificationPanelViewController extends PanelViewController { // The expandedHeight is always the full panel Height when bypassing expandedHeight = getMaxPanelHeightNonBypass(); } - mNotificationStackScrollLayoutController.setOnStackYChanged(mOnStackYChanged); mNotificationStackScrollLayoutController.setExpandedHeight(expandedHeight); updateKeyguardBottomAreaAlpha(); updateBigClockAlpha();