diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index f7eaea997fa78..374240d86f11a 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -396,10 +396,6 @@ false - - true - false diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java index b0f4da251208e..affad7a57d868 100644 --- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java @@ -391,9 +391,9 @@ public class SwipeHelper implements Gefingerpoken { boolean animateLeft = (Math.abs(velocity) > getEscapeVelocity() && velocity < 0) || (getTranslation(animView) < 0 && !isDismissAll); if (animateLeft || animateLeftForRtl || animateUpForMenu) { - newPos = -getSize(animView); + newPos = -getTotalTranslationLength(animView); } else { - newPos = getSize(animView); + newPos = getTotalTranslationLength(animView); } long duration; if (fixedDuration == 0) { @@ -469,6 +469,15 @@ public class SwipeHelper implements Gefingerpoken { anim.start(); } + /** + * Get the total translation length where we want to swipe to when dismissing the view. By + * default this is the size of the view, but can also be larger. + * @param animView the view to ask about + */ + protected float getTotalTranslationLength(View animView) { + return getSize(animView); + } + /** * Called to update the dismiss animation. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java index aef01e9bd8114..0fb1c54bb1500 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java @@ -71,11 +71,11 @@ public final class NotificationClicker implements View.OnClickListener { // Check if the notification is displaying the menu, if so slide notification back if (isMenuVisible(row)) { mLogger.logMenuVisible(entry); - row.animateTranslateNotification(0); + row.animateResetTranslation(); return; } else if (row.isChildInGroup() && isMenuVisible(row.getNotificationParent())) { mLogger.logParentMenuVisible(entry); - row.getNotificationParent().animateTranslateNotification(0); + row.getNotificationParent().animateResetTranslation(); return; } else if (row.isSummaryWithChildren() && row.areChildrenExpanded()) { // We never want to open the app directly if the user clicks in between diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index d5acf2d630257..ba28dc59def4e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -874,7 +874,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView @Override protected boolean handleSlideBack() { if (mMenuRow != null && mMenuRow.isMenuVisible()) { - animateTranslateNotification(0 /* targetLeft */); + animateResetTranslation(); return true; } return false; @@ -1711,21 +1711,17 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mChildrenContainer.setContainingNotification(ExpandableNotificationRow.this); mChildrenContainer.onNotificationUpdated(); - if (mShouldTranslateContents) { - mTranslateableViews.add(mChildrenContainer); - } + mTranslateableViews.add(mChildrenContainer); }); - if (mShouldTranslateContents) { - // Add the views that we translate to reveal the menu - mTranslateableViews = new ArrayList<>(); - for (int i = 0; i < getChildCount(); i++) { - mTranslateableViews.add(getChildAt(i)); - } - // Remove views that don't translate - mTranslateableViews.remove(mChildrenContainerStub); - mTranslateableViews.remove(mGutsStub); + // Add the views that we translate to reveal the menu + mTranslateableViews = new ArrayList<>(); + for (int i = 0; i < getChildCount(); i++) { + mTranslateableViews.add(getChildAt(i)); } + // Remove views that don't translate + mTranslateableViews.remove(mChildrenContainerStub); + mTranslateableViews.remove(mGutsStub); } private void doLongClickCallback() { @@ -1803,7 +1799,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mTranslateAnim.cancel(); } - if (!mShouldTranslateContents) { + if (mDismissUsingRowTranslationX) { setTranslationX(0); } else if (mTranslateableViews != null) { for (int i = 0; i < mTranslateableViews.size(); i++) { @@ -1865,23 +1861,47 @@ public class ExpandableNotificationRow extends ActivatableNotificationView return mPrivateLayout.getActiveRemoteInputText(); } - public void animateTranslateNotification(final float leftTarget) { + /** + * Reset the translation with an animation. + */ + public void animateResetTranslation() { if (mTranslateAnim != null) { mTranslateAnim.cancel(); } - mTranslateAnim = getTranslateViewAnimator(leftTarget, null /* updateListener */); + mTranslateAnim = getTranslateViewAnimator(0, null /* updateListener */); if (mTranslateAnim != null) { mTranslateAnim.start(); } } + /** + * Set the dismiss behavior of the view. + * @param usingRowTranslationX {@code true} if the view should translate using regular + * translationX, otherwise the contents will be + * translated. + */ + @Override + public void setDismissUsingRowTranslationX(boolean usingRowTranslationX) { + if (usingRowTranslationX != mDismissUsingRowTranslationX) { + // In case we were already transitioning, let's switch over! + float previousTranslation = getTranslation(); + if (previousTranslation != 0) { + setTranslation(0); + } + super.setDismissUsingRowTranslationX(usingRowTranslationX); + if (previousTranslation != 0) { + setTranslation(previousTranslation); + } + } + } + @Override public void setTranslation(float translationX) { invalidate(); if (isBlockingHelperShowingAndTranslationFinished()) { mGuts.setTranslationX(translationX); return; - } else if (!mShouldTranslateContents) { + } else if (mDismissUsingRowTranslationX) { setTranslationX(translationX); } else if (mTranslateableViews != null) { // Translate the group of views @@ -1905,7 +1925,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView @Override public float getTranslation() { - if (!mShouldTranslateContents) { + if (mDismissUsingRowTranslationX) { return getTranslationX(); } @@ -2896,7 +2916,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView float y = event.getY(); NotificationViewWrapper wrapper = getVisibleNotificationViewWrapper(); NotificationHeaderView header = wrapper == null ? null : wrapper.getNotificationHeader(); - if (header != null && header.isInTouchRect(x - getTranslation(), y)) { + // the extra translation only needs to be added, if we're translating the notification + // contents, otherwise the motionEvent is already at the right place due to the + // touch event system. + float translation = !mDismissUsingRowTranslationX ? getTranslation() : 0; + if (header != null && header.isInTouchRect(x - translation, y)) { return true; } if ((!mIsSummaryWithChildren || shouldShowPublic()) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java index a7035229bf5f9..d58fe3b3c4a3c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java @@ -71,10 +71,10 @@ public abstract class ExpandableOutlineView extends ExpandableView { private int mBackgroundTop; /** - * {@code true} if the children views of the {@link ExpandableOutlineView} are translated when + * {@code false} if the children views of the {@link ExpandableOutlineView} are translated when * it is moved. Otherwise, the translation is set on the {@code ExpandableOutlineView} itself. */ - protected boolean mShouldTranslateContents; + protected boolean mDismissUsingRowTranslationX = true; private float[] mTmpCornerRadii = new float[8]; private final ViewOutlineProvider mProvider = new ViewOutlineProvider() { @@ -82,7 +82,8 @@ public abstract class ExpandableOutlineView extends ExpandableView { public void getOutline(View view, Outline outline) { if (!mCustomOutline && getCurrentTopRoundness() == 0.0f && getCurrentBottomRoundness() == 0.0f && !mAlwaysRoundBothCorners) { - int translation = mShouldTranslateContents ? (int) getTranslation() : 0; + // Only when translating just the contents, does the outline need to be shifted. + int translation = !mDismissUsingRowTranslationX ? (int) getTranslation() : 0; int left = Math.max(translation, 0); int top = mClipTopAmount + mBackgroundTop; int right = getWidth() + Math.min(translation, 0); @@ -107,7 +108,9 @@ public abstract class ExpandableOutlineView extends ExpandableView { float topRoundness = mAlwaysRoundBothCorners ? mOutlineRadius : getCurrentBackgroundRadiusTop(); if (!mCustomOutline) { - int translation = mShouldTranslateContents && !ignoreTranslation + // The outline just needs to be shifted if we're translating the contents. Otherwise + // it's already in the right place. + int translation = !mDismissUsingRowTranslationX && !ignoreTranslation ? (int) getTranslation() : 0; int halfExtraWidth = (int) (mExtraWidthForClipping / 2.0f); left = Math.max(translation, 0) - halfExtraWidth; @@ -196,13 +199,14 @@ public abstract class ExpandableOutlineView extends ExpandableView { } protected boolean isClippingNeeded() { - return mAlwaysRoundBothCorners || mCustomOutline || getTranslation() != 0 ; + // When translating the contents instead of the overall view, we need to make sure we clip + // rounded to the contents. + boolean forTranslation = getTranslation() != 0 && !mDismissUsingRowTranslationX; + return mAlwaysRoundBothCorners || mCustomOutline || forTranslation; } private void initDimens() { Resources res = getResources(); - mShouldTranslateContents = - res.getBoolean(R.bool.config_translateNotificationContentsOnSwipe); mOutlineRadius = res.getDimension(R.dimen.notification_shadow_radius); mAlwaysRoundBothCorners = res.getBoolean(R.bool.config_clipNotificationsToOutline); if (!mAlwaysRoundBothCorners) { @@ -342,9 +346,25 @@ public abstract class ExpandableOutlineView extends ExpandableView { } } + /** + * Set the dismiss behavior of the view. + * @param usingRowTranslationX {@code true} if the view should translate using regular + * translationX, otherwise the contents will be + * translated. + */ + public void setDismissUsingRowTranslationX(boolean usingRowTranslationX) { + mDismissUsingRowTranslationX = usingRowTranslationX; + } + @Override public int getOutlineTranslation() { - return mCustomOutline ? mOutlineRect.left : (int) getTranslation(); + if (mCustomOutline) { + return mOutlineRect.left; + } + if (mDismissUsingRowTranslationX) { + return 0; + } + return (int) getTranslation(); } public void updateOutline() { 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 2a5fdd5cee93a..f67322e701c1c 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,10 +477,21 @@ 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 */ private boolean mLaunchingNotification; + + /** + * Do notifications dismiss with normal transitioning + */ + private boolean mDismissUsingRowTranslationX = true; private NotificationEntry mTopHeadsUpEntry; private long mNumHeadsUp; private NotificationStackScrollLayoutController.TouchHandler mTouchHandler; @@ -528,7 +545,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mSectionsManager = notificationSectionsManager; mFeatureFlags = featureFlags; mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController; - mShouldUseSplitNotificationShade = shouldUseSplitNotificationShade(mFeatureFlags, res); + updateSplitNotificationShade(); mSectionsManager.initialize(this, LayoutInflater.from(context)); mSections = mSectionsManager.createSectionsForBuckets(); @@ -884,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() { @@ -983,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) @@ -1117,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); } } @@ -1146,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(); @@ -1153,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, @@ -1165,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; } @@ -1600,8 +1640,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); Resources res = getResources(); - mShouldUseSplitNotificationShade = shouldUseSplitNotificationShade(mFeatureFlags, res); - updateUseRoundedRectClipping(); + updateSplitNotificationShade(); mStatusBarHeight = res.getDimensionPixelOffset(R.dimen.status_bar_height); float densityScale = res.getDisplayMetrics().density; mSwipeHelper.setDensityScale(densityScale); @@ -2528,8 +2567,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable updateScrollStateForRemovedChild(child); boolean animationGenerated = generateRemoveAnimation(child); if (animationGenerated) { - if (!mSwipedOutViews.contains(child) - || Math.abs(child.getTranslation()) != child.getWidth()) { + if (!mSwipedOutViews.contains(child) || !isFullySwipedOut(child)) { container.addTransientView(child, 0); child.setTransientContainer(container); } @@ -2541,6 +2579,13 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable focusNextViewIfFocused(child); } + /** + * Has this view been fully swiped out such that it's not visible anymore. + */ + public boolean isFullySwipedOut(ExpandableView child) { + return Math.abs(child.getTranslation()) >= Math.abs(getTotalTranslationLength(child)); + } + @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) private void focusNextViewIfFocused(View view) { if (view instanceof ExpandableNotificationRow) { @@ -2660,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) { @@ -2759,7 +2814,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable updateAnimationState(child); updateChronometerForChild(child); if (child instanceof ExpandableNotificationRow) { - ((ExpandableNotificationRow) child).setDismissRtl(mDismissRtl); + ExpandableNotificationRow row = (ExpandableNotificationRow) child; + row.setDismissRtl(mDismissRtl); + row.setDismissUsingRowTranslationX(mDismissUsingRowTranslationX); + } } @@ -3033,7 +3091,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable removedTranslation = row.getTranslationWhenRemoved(); ignoreChildren = false; } - childWasSwipedOut |= Math.abs(row.getTranslation()) == row.getWidth(); + childWasSwipedOut |= isFullySwipedOut(row); } else if (child instanceof MediaHeaderView) { childWasSwipedOut = true; } @@ -3041,11 +3099,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable Rect clipBounds = child.getClipBounds(); childWasSwipedOut = clipBounds != null && clipBounds.height() == 0; - if (childWasSwipedOut && child instanceof ExpandableView) { + if (childWasSwipedOut) { // Clean up any potential transient views if the child has already been swiped // out, as we won't be animating it further (due to its height already being // clipped to 0. - ViewGroup transientContainer = ((ExpandableView) child).getTransientContainer(); + ViewGroup transientContainer = child.getTransientContainer(); if (transientContainer != null) { transientContainer.removeTransientView(child); } @@ -3819,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(); @@ -3839,6 +3901,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable requestAnimationOnViewResize(row); } requestChildrenUpdate(); + mAnimateStackYForContentHeightChange = previouslyNeededAnimation; } void onChildHeightReset(ExpandableView view) { @@ -4051,7 +4114,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable expandableView.setFakeShadowIntensity( diff / FakeShadowView.SHADOW_SIBLING_TRESHOLD, previous.getOutlineAlpha(), (int) yLocation, - previous.getOutlineTranslation()); + (int) (previous.getOutlineTranslation() + previous.getTranslation())); } previous = expandableView; } @@ -4564,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); } } @@ -4636,6 +4704,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mStatusBarState = statusBarState; mAmbientState.setStatusBarState(statusBarState); updateSpeedBumpIndex(); + updateDismissBehavior(); } void onStatePostChange(boolean fromShadeLocked) { @@ -5224,6 +5293,32 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } } + private void updateSplitNotificationShade() { + boolean split = shouldUseSplitNotificationShade(mFeatureFlags, getResources()); + if (split != mShouldUseSplitNotificationShade) { + mShouldUseSplitNotificationShade = split; + updateDismissBehavior(); + updateUseRoundedRectClipping(); + } + } + + private void updateDismissBehavior() { + // On the split keyguard, dismissing with clipping without a visual boundary looks odd, + // so let's use the content dismiss behavior instead. + boolean dismissUsingRowTranslationX = !mShouldUseSplitNotificationShade + || mStatusBarState != StatusBarState.KEYGUARD; + if (mDismissUsingRowTranslationX != dismissUsingRowTranslationX) { + mDismissUsingRowTranslationX = dismissUsingRowTranslationX; + for (int i = 0; i < getChildCount(); i++) { + View child = getChildAt(i); + if (child instanceof ExpandableNotificationRow) { + ((ExpandableNotificationRow) child).setDismissUsingRowTranslationX( + dismissUsingRowTranslationX); + } + } + } + } + /** * Set if we're launching a notification right now. */ @@ -5258,6 +5353,19 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable super.dispatchDraw(canvas); } + /** + * Calculate the total translation needed when dismissing. + */ + public float getTotalTranslationLength(View animView) { + if (!mDismissUsingRowTranslationX) { + return animView.getMeasuredWidth(); + } + float notificationWidth = animView.getMeasuredWidth(); + int containerWidth = getMeasuredWidth(); + float padding = (containerWidth - notificationWidth) / 2.0f; + return containerWidth - padding; + } + /** * A listener that is notified when the empty space below the notifications is clicked on */ 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 92acdf89e9d40..a45c5e1aeb796 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 @@ -383,6 +383,11 @@ public class NotificationStackScrollLayoutController { false /* resetMenu */); } + @Override + public float getTotalTranslationLength(View animView) { + return mView.getTotalTranslationLength(animView); + } + @Override public void onSnooze(StatusBarNotification sbn, NotificationSwipeActionHelper.SnoozeOption snoozeOption) { @@ -1018,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/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java index f4c4d440b0634..664776975b240 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java @@ -324,6 +324,11 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc } } + @Override + protected float getTotalTranslationLength(View animView) { + return mCallback.getTotalTranslationLength(animView); + } + @Override public void setTranslation(View v, float translate) { if (v instanceof SwipeableView) { @@ -466,6 +471,13 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc void onSnooze(StatusBarNotification sbn, SnoozeOption snoozeOption); void onDismiss(); + + /** + * Get the total translation length where we want to swipe to when dismissing the view. By + * default this is the size of the view, but can also be larger. + * @param animView the view to ask about + */ + float getTotalTranslationLength(View animView); } static class Builder { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java index 4fd2064b394d1..ee12b4b2d728c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java @@ -392,7 +392,7 @@ public class StackStateAnimator { 0, () -> removeTransientView(changingView), null); } else if (event.animationType == NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT) { - if (Math.abs(changingView.getTranslation()) == changingView.getWidth() + if (mHostLayout.isFullySwipedOut(changingView) && changingView.getTransientContainer() != null) { changingView.getTransientContainer().removeTransientView(changingView); } 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 eceadcb7ff268..6c91e5e74fa11 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -832,6 +832,7 @@ public class NotificationPanelViewController extends PanelViewController { mNotificationStackScrollLayoutController.setOverscrollTopChangedListener( mOnOverscrollTopChangedListener); mNotificationStackScrollLayoutController.setOnScrollListener(this::onNotificationScrolled); + mNotificationStackScrollLayoutController.setOnStackYChanged(this::onStackYChanged); mNotificationStackScrollLayoutController.setOnEmptySpaceClickListener( mOnEmptySpaceClickListener); addTrackingHeadsUpListener(mNotificationStackScrollLayoutController::setTrackingHeadsUp); @@ -2211,15 +2212,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(); } @@ -3348,7 +3351,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(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java index 2e0827f24bf84..fa25c3f1e0053 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java @@ -289,6 +289,7 @@ public class ExpandableNotificationRowTest extends SysuiTestCase { @Test public void testIconScrollXAfterTranslationAndReset() throws Exception { + mGroupRow.setDismissUsingRowTranslationX(false); mGroupRow.setTranslation(50); assertEquals(50, -mGroupRow.getEntry().getIcons().getShelfIcon().getScrollX());