Merge changes from topic "notif_smooth_dismissal" into sc-dev am: 4721ef7c65 am: 1c82804dca

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/14965033

Change-Id: Ie8b196593432e30a0be2487dfede11cb921faba0
This commit is contained in:
Selim Cinek
2021-06-16 13:44:02 +00:00
committed by Automerger Merge Worker
11 changed files with 236 additions and 59 deletions

View File

@@ -396,10 +396,6 @@
<!-- Whether or not the notifications should always fade as they are dismissed. --> <!-- Whether or not the notifications should always fade as they are dismissed. -->
<bool name="config_fadeNotificationsOnDismiss">false</bool> <bool name="config_fadeNotificationsOnDismiss">false</bool>
<!-- Whether or not the parent of the notification row itself is being translated when swiped or
its children views. If true, then the contents are translated and vice versa. -->
<bool name="config_translateNotificationContentsOnSwipe">true</bool>
<!-- Whether or not the fade on the notification is based on the amount that it has been swiped <!-- Whether or not the fade on the notification is based on the amount that it has been swiped
off-screen. --> off-screen. -->
<bool name="config_fadeDependingOnAmountSwiped">false</bool> <bool name="config_fadeDependingOnAmountSwiped">false</bool>

View File

@@ -391,9 +391,9 @@ public class SwipeHelper implements Gefingerpoken {
boolean animateLeft = (Math.abs(velocity) > getEscapeVelocity() && velocity < 0) || boolean animateLeft = (Math.abs(velocity) > getEscapeVelocity() && velocity < 0) ||
(getTranslation(animView) < 0 && !isDismissAll); (getTranslation(animView) < 0 && !isDismissAll);
if (animateLeft || animateLeftForRtl || animateUpForMenu) { if (animateLeft || animateLeftForRtl || animateUpForMenu) {
newPos = -getSize(animView); newPos = -getTotalTranslationLength(animView);
} else { } else {
newPos = getSize(animView); newPos = getTotalTranslationLength(animView);
} }
long duration; long duration;
if (fixedDuration == 0) { if (fixedDuration == 0) {
@@ -469,6 +469,15 @@ public class SwipeHelper implements Gefingerpoken {
anim.start(); 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. * Called to update the dismiss animation.
*/ */

View File

@@ -71,11 +71,11 @@ public final class NotificationClicker implements View.OnClickListener {
// Check if the notification is displaying the menu, if so slide notification back // Check if the notification is displaying the menu, if so slide notification back
if (isMenuVisible(row)) { if (isMenuVisible(row)) {
mLogger.logMenuVisible(entry); mLogger.logMenuVisible(entry);
row.animateTranslateNotification(0); row.animateResetTranslation();
return; return;
} else if (row.isChildInGroup() && isMenuVisible(row.getNotificationParent())) { } else if (row.isChildInGroup() && isMenuVisible(row.getNotificationParent())) {
mLogger.logParentMenuVisible(entry); mLogger.logParentMenuVisible(entry);
row.getNotificationParent().animateTranslateNotification(0); row.getNotificationParent().animateResetTranslation();
return; return;
} else if (row.isSummaryWithChildren() && row.areChildrenExpanded()) { } else if (row.isSummaryWithChildren() && row.areChildrenExpanded()) {
// We never want to open the app directly if the user clicks in between // We never want to open the app directly if the user clicks in between

View File

@@ -874,7 +874,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
@Override @Override
protected boolean handleSlideBack() { protected boolean handleSlideBack() {
if (mMenuRow != null && mMenuRow.isMenuVisible()) { if (mMenuRow != null && mMenuRow.isMenuVisible()) {
animateTranslateNotification(0 /* targetLeft */); animateResetTranslation();
return true; return true;
} }
return false; return false;
@@ -1711,21 +1711,17 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mChildrenContainer.setContainingNotification(ExpandableNotificationRow.this); mChildrenContainer.setContainingNotification(ExpandableNotificationRow.this);
mChildrenContainer.onNotificationUpdated(); mChildrenContainer.onNotificationUpdated();
if (mShouldTranslateContents) { mTranslateableViews.add(mChildrenContainer);
mTranslateableViews.add(mChildrenContainer);
}
}); });
if (mShouldTranslateContents) { // Add the views that we translate to reveal the menu
// Add the views that we translate to reveal the menu mTranslateableViews = new ArrayList<>();
mTranslateableViews = new ArrayList<>(); for (int i = 0; i < getChildCount(); i++) {
for (int i = 0; i < getChildCount(); i++) { mTranslateableViews.add(getChildAt(i));
mTranslateableViews.add(getChildAt(i));
}
// Remove views that don't translate
mTranslateableViews.remove(mChildrenContainerStub);
mTranslateableViews.remove(mGutsStub);
} }
// Remove views that don't translate
mTranslateableViews.remove(mChildrenContainerStub);
mTranslateableViews.remove(mGutsStub);
} }
private void doLongClickCallback() { private void doLongClickCallback() {
@@ -1803,7 +1799,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mTranslateAnim.cancel(); mTranslateAnim.cancel();
} }
if (!mShouldTranslateContents) { if (mDismissUsingRowTranslationX) {
setTranslationX(0); setTranslationX(0);
} else if (mTranslateableViews != null) { } else if (mTranslateableViews != null) {
for (int i = 0; i < mTranslateableViews.size(); i++) { for (int i = 0; i < mTranslateableViews.size(); i++) {
@@ -1865,23 +1861,47 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
return mPrivateLayout.getActiveRemoteInputText(); return mPrivateLayout.getActiveRemoteInputText();
} }
public void animateTranslateNotification(final float leftTarget) { /**
* Reset the translation with an animation.
*/
public void animateResetTranslation() {
if (mTranslateAnim != null) { if (mTranslateAnim != null) {
mTranslateAnim.cancel(); mTranslateAnim.cancel();
} }
mTranslateAnim = getTranslateViewAnimator(leftTarget, null /* updateListener */); mTranslateAnim = getTranslateViewAnimator(0, null /* updateListener */);
if (mTranslateAnim != null) { if (mTranslateAnim != null) {
mTranslateAnim.start(); 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 @Override
public void setTranslation(float translationX) { public void setTranslation(float translationX) {
invalidate(); invalidate();
if (isBlockingHelperShowingAndTranslationFinished()) { if (isBlockingHelperShowingAndTranslationFinished()) {
mGuts.setTranslationX(translationX); mGuts.setTranslationX(translationX);
return; return;
} else if (!mShouldTranslateContents) { } else if (mDismissUsingRowTranslationX) {
setTranslationX(translationX); setTranslationX(translationX);
} else if (mTranslateableViews != null) { } else if (mTranslateableViews != null) {
// Translate the group of views // Translate the group of views
@@ -1905,7 +1925,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
@Override @Override
public float getTranslation() { public float getTranslation() {
if (!mShouldTranslateContents) { if (mDismissUsingRowTranslationX) {
return getTranslationX(); return getTranslationX();
} }
@@ -2896,7 +2916,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
float y = event.getY(); float y = event.getY();
NotificationViewWrapper wrapper = getVisibleNotificationViewWrapper(); NotificationViewWrapper wrapper = getVisibleNotificationViewWrapper();
NotificationHeaderView header = wrapper == null ? null : wrapper.getNotificationHeader(); 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; return true;
} }
if ((!mIsSummaryWithChildren || shouldShowPublic()) if ((!mIsSummaryWithChildren || shouldShowPublic())

View File

@@ -71,10 +71,10 @@ public abstract class ExpandableOutlineView extends ExpandableView {
private int mBackgroundTop; 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. * 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 float[] mTmpCornerRadii = new float[8];
private final ViewOutlineProvider mProvider = new ViewOutlineProvider() { private final ViewOutlineProvider mProvider = new ViewOutlineProvider() {
@@ -82,7 +82,8 @@ public abstract class ExpandableOutlineView extends ExpandableView {
public void getOutline(View view, Outline outline) { public void getOutline(View view, Outline outline) {
if (!mCustomOutline && getCurrentTopRoundness() == 0.0f if (!mCustomOutline && getCurrentTopRoundness() == 0.0f
&& getCurrentBottomRoundness() == 0.0f && !mAlwaysRoundBothCorners) { && 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 left = Math.max(translation, 0);
int top = mClipTopAmount + mBackgroundTop; int top = mClipTopAmount + mBackgroundTop;
int right = getWidth() + Math.min(translation, 0); int right = getWidth() + Math.min(translation, 0);
@@ -107,7 +108,9 @@ public abstract class ExpandableOutlineView extends ExpandableView {
float topRoundness = mAlwaysRoundBothCorners float topRoundness = mAlwaysRoundBothCorners
? mOutlineRadius : getCurrentBackgroundRadiusTop(); ? mOutlineRadius : getCurrentBackgroundRadiusTop();
if (!mCustomOutline) { 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) getTranslation() : 0;
int halfExtraWidth = (int) (mExtraWidthForClipping / 2.0f); int halfExtraWidth = (int) (mExtraWidthForClipping / 2.0f);
left = Math.max(translation, 0) - halfExtraWidth; left = Math.max(translation, 0) - halfExtraWidth;
@@ -196,13 +199,14 @@ public abstract class ExpandableOutlineView extends ExpandableView {
} }
protected boolean isClippingNeeded() { 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() { private void initDimens() {
Resources res = getResources(); Resources res = getResources();
mShouldTranslateContents =
res.getBoolean(R.bool.config_translateNotificationContentsOnSwipe);
mOutlineRadius = res.getDimension(R.dimen.notification_shadow_radius); mOutlineRadius = res.getDimension(R.dimen.notification_shadow_radius);
mAlwaysRoundBothCorners = res.getBoolean(R.bool.config_clipNotificationsToOutline); mAlwaysRoundBothCorners = res.getBoolean(R.bool.config_clipNotificationsToOutline);
if (!mAlwaysRoundBothCorners) { 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 @Override
public int getOutlineTranslation() { public int getOutlineTranslation() {
return mCustomOutline ? mOutlineRect.left : (int) getTranslation(); if (mCustomOutline) {
return mOutlineRect.left;
}
if (mDismissUsingRowTranslationX) {
return 0;
}
return (int) getTranslation();
} }
public void updateOutline() { public void updateOutline() {

View File

@@ -425,13 +425,19 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private final Rect mBackgroundAnimationRect = new Rect(); private final Rect mBackgroundAnimationRect = new Rect();
private ArrayList<BiConsumer<Float, Float>> mExpandedHeightListeners = new ArrayList<>(); private ArrayList<BiConsumer<Float, Float>> mExpandedHeightListeners = new ArrayList<>();
private int mHeadsUpInset; 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 HeadsUpAppearanceController mHeadsUpAppearanceController;
private final Rect mTmpRect = new Rect(); private final Rect mTmpRect = new Rect();
private DismissListener mDismissListener; private DismissListener mDismissListener;
private DismissAllAnimationListener mDismissAllAnimationListener; private DismissAllAnimationListener mDismissAllAnimationListener;
private NotificationRemoteInputManager mRemoteInputManager; private NotificationRemoteInputManager mRemoteInputManager;
private ShadeController mShadeController; private ShadeController mShadeController;
private Runnable mOnStackYChanged; private Consumer<Boolean> mOnStackYChanged;
protected boolean mClearAllEnabled; protected boolean mClearAllEnabled;
@@ -471,10 +477,21 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private int mRoundedRectClippingRight; private int mRoundedRectClippingRight;
private float[] mBgCornerRadii = new float[8]; 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 * Are we launching a notification right now
*/ */
private boolean mLaunchingNotification; private boolean mLaunchingNotification;
/**
* Do notifications dismiss with normal transitioning
*/
private boolean mDismissUsingRowTranslationX = true;
private NotificationEntry mTopHeadsUpEntry; private NotificationEntry mTopHeadsUpEntry;
private long mNumHeadsUp; private long mNumHeadsUp;
private NotificationStackScrollLayoutController.TouchHandler mTouchHandler; private NotificationStackScrollLayoutController.TouchHandler mTouchHandler;
@@ -528,7 +545,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mSectionsManager = notificationSectionsManager; mSectionsManager = notificationSectionsManager;
mFeatureFlags = featureFlags; mFeatureFlags = featureFlags;
mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController; mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
mShouldUseSplitNotificationShade = shouldUseSplitNotificationShade(mFeatureFlags, res); updateSplitNotificationShade();
mSectionsManager.initialize(this, LayoutInflater.from(context)); mSectionsManager.initialize(this, LayoutInflater.from(context));
mSections = mSectionsManager.createSectionsForBuckets(); mSections = mSectionsManager.createSectionsForBuckets();
@@ -884,6 +901,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mCornerRadius = res.getDimensionPixelSize(R.dimen.notification_corner_radius); mCornerRadius = res.getDimensionPixelSize(R.dimen.notification_corner_radius);
mHeadsUpInset = mStatusBarHeight + res.getDimensionPixelSize( mHeadsUpInset = mStatusBarHeight + res.getDimensionPixelSize(
R.dimen.heads_up_status_bar_padding); R.dimen.heads_up_status_bar_padding);
mQsScrollBoundaryPosition = res.getDimensionPixelSize(
com.android.internal.R.dimen.quick_qs_offset_height);
} }
void updateCornerRadius() { void updateCornerRadius() {
@@ -983,6 +1002,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
updateFirstAndLastBackgroundViews(); updateFirstAndLastBackgroundViews();
updateAlgorithmLayoutMinHeight(); updateAlgorithmLayoutMinHeight();
updateOwnTranslationZ(); updateOwnTranslationZ();
// Once the layout has finished, we don't need to animate any scrolling clampings anymore.
mAnimateStackYForContentHeightChange = false;
} }
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
@@ -1117,7 +1139,13 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private void clampScrollPosition() { private void clampScrollPosition() {
int scrollRange = getScrollRange(); int scrollRange = getScrollRange();
if (scrollRange < mOwnScrollY) { 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. * Apply expansion fraction to the y position and height of the notifications panel.
*/ */
private void updateStackPosition() { 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 // Consider interpolating from an mExpansionStartY for use on lockscreen and AOD
float endTopPosition = mTopPadding + mExtraTopInsetForFullShadeTransition float endTopPosition = mTopPadding + mExtraTopInsetForFullShadeTransition
+ mAmbientState.getOverExpansion(); + mAmbientState.getOverExpansion();
@@ -1153,7 +1189,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
final float stackY = MathUtils.lerp(0, endTopPosition, fraction); final float stackY = MathUtils.lerp(0, endTopPosition, fraction);
mAmbientState.setStackY(stackY); mAmbientState.setStackY(stackY);
if (mOnStackYChanged != null) { if (mOnStackYChanged != null) {
mOnStackYChanged.run(); mOnStackYChanged.accept(listenerNeedsAnimation);
} }
if (mQsExpansionFraction <= 0) { if (mQsExpansionFraction <= 0) {
final float stackEndHeight = Math.max(0f, 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<Boolean> onStackYChanged) {
mOnStackYChanged = onStackYChanged; mOnStackYChanged = onStackYChanged;
} }
@@ -1600,8 +1640,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
protected void onConfigurationChanged(Configuration newConfig) { protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig); super.onConfigurationChanged(newConfig);
Resources res = getResources(); Resources res = getResources();
mShouldUseSplitNotificationShade = shouldUseSplitNotificationShade(mFeatureFlags, res); updateSplitNotificationShade();
updateUseRoundedRectClipping();
mStatusBarHeight = res.getDimensionPixelOffset(R.dimen.status_bar_height); mStatusBarHeight = res.getDimensionPixelOffset(R.dimen.status_bar_height);
float densityScale = res.getDisplayMetrics().density; float densityScale = res.getDisplayMetrics().density;
mSwipeHelper.setDensityScale(densityScale); mSwipeHelper.setDensityScale(densityScale);
@@ -2528,8 +2567,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
updateScrollStateForRemovedChild(child); updateScrollStateForRemovedChild(child);
boolean animationGenerated = generateRemoveAnimation(child); boolean animationGenerated = generateRemoveAnimation(child);
if (animationGenerated) { if (animationGenerated) {
if (!mSwipedOutViews.contains(child) if (!mSwipedOutViews.contains(child) || !isFullySwipedOut(child)) {
|| Math.abs(child.getTranslation()) != child.getWidth()) {
container.addTransientView(child, 0); container.addTransientView(child, 0);
child.setTransientContainer(container); child.setTransientContainer(container);
} }
@@ -2541,6 +2579,13 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
focusNextViewIfFocused(child); 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) @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void focusNextViewIfFocused(View view) { private void focusNextViewIfFocused(View view) {
if (view instanceof ExpandableNotificationRow) { if (view instanceof ExpandableNotificationRow) {
@@ -2660,17 +2705,27 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
final int startingPosition = getPositionInLinearLayout(removedChild); final int startingPosition = getPositionInLinearLayout(removedChild);
final int childHeight = getIntrinsicHeight(removedChild) + mPaddingBetweenElements; final int childHeight = getIntrinsicHeight(removedChild) + mPaddingBetweenElements;
final int endPosition = startingPosition + childHeight; 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 // This child is fully scrolled of the top, so we have to deduct its height from the
// scrollPosition // scrollPosition
setOwnScrollY(mOwnScrollY - childHeight); 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 // This child is currently being scrolled into, set the scroll position to the
// start of this child // 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) @ShadeViewRefactor(RefactorComponent.COORDINATOR)
private int getIntrinsicHeight(View view) { private int getIntrinsicHeight(View view) {
if (view instanceof ExpandableView) { if (view instanceof ExpandableView) {
@@ -2759,7 +2814,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
updateAnimationState(child); updateAnimationState(child);
updateChronometerForChild(child); updateChronometerForChild(child);
if (child instanceof ExpandableNotificationRow) { 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(); removedTranslation = row.getTranslationWhenRemoved();
ignoreChildren = false; ignoreChildren = false;
} }
childWasSwipedOut |= Math.abs(row.getTranslation()) == row.getWidth(); childWasSwipedOut |= isFullySwipedOut(row);
} else if (child instanceof MediaHeaderView) { } else if (child instanceof MediaHeaderView) {
childWasSwipedOut = true; childWasSwipedOut = true;
} }
@@ -3041,11 +3099,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
Rect clipBounds = child.getClipBounds(); Rect clipBounds = child.getClipBounds();
childWasSwipedOut = clipBounds != null && clipBounds.height() == 0; 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 // 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 // out, as we won't be animating it further (due to its height already being
// clipped to 0. // clipped to 0.
ViewGroup transientContainer = ((ExpandableView) child).getTransientContainer(); ViewGroup transientContainer = child.getTransientContainer();
if (transientContainer != null) { if (transientContainer != null) {
transientContainer.removeTransientView(child); transientContainer.removeTransientView(child);
} }
@@ -3819,6 +3877,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
} }
void onChildHeightChanged(ExpandableView view, boolean needsAnimation) { void onChildHeightChanged(ExpandableView view, boolean needsAnimation) {
boolean previouslyNeededAnimation = mAnimateStackYForContentHeightChange;
if (needsAnimation) {
mAnimateStackYForContentHeightChange = true;
}
updateContentHeight(); updateContentHeight();
updateScrollPositionOnExpandInBottom(view); updateScrollPositionOnExpandInBottom(view);
clampScrollPosition(); clampScrollPosition();
@@ -3839,6 +3901,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
requestAnimationOnViewResize(row); requestAnimationOnViewResize(row);
} }
requestChildrenUpdate(); requestChildrenUpdate();
mAnimateStackYForContentHeightChange = previouslyNeededAnimation;
} }
void onChildHeightReset(ExpandableView view) { void onChildHeightReset(ExpandableView view) {
@@ -4051,7 +4114,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
expandableView.setFakeShadowIntensity( expandableView.setFakeShadowIntensity(
diff / FakeShadowView.SHADOW_SIBLING_TRESHOLD, diff / FakeShadowView.SHADOW_SIBLING_TRESHOLD,
previous.getOutlineAlpha(), (int) yLocation, previous.getOutlineAlpha(), (int) yLocation,
previous.getOutlineTranslation()); (int) (previous.getOutlineTranslation() + previous.getTranslation()));
} }
previous = expandableView; previous = expandableView;
} }
@@ -4564,13 +4627,18 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@ShadeViewRefactor(RefactorComponent.COORDINATOR) @ShadeViewRefactor(RefactorComponent.COORDINATOR)
private void setOwnScrollY(int ownScrollY) { private void setOwnScrollY(int ownScrollY) {
setOwnScrollY(ownScrollY, false /* animateScrollChangeListener */);
}
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
private void setOwnScrollY(int ownScrollY, boolean animateStackYChangeListener) {
if (ownScrollY != mOwnScrollY) { if (ownScrollY != mOwnScrollY) {
// We still want to call the normal scrolled changed for accessibility reasons // We still want to call the normal scrolled changed for accessibility reasons
onScrollChanged(mScrollX, ownScrollY, mScrollX, mOwnScrollY); onScrollChanged(mScrollX, ownScrollY, mScrollX, mOwnScrollY);
mOwnScrollY = ownScrollY; mOwnScrollY = ownScrollY;
mAmbientState.setScrollY(mOwnScrollY); mAmbientState.setScrollY(mOwnScrollY);
updateOnScrollChange(); updateOnScrollChange();
updateStackPosition(); updateStackPosition(animateStackYChangeListener);
} }
} }
@@ -4636,6 +4704,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mStatusBarState = statusBarState; mStatusBarState = statusBarState;
mAmbientState.setStatusBarState(statusBarState); mAmbientState.setStatusBarState(statusBarState);
updateSpeedBumpIndex(); updateSpeedBumpIndex();
updateDismissBehavior();
} }
void onStatePostChange(boolean fromShadeLocked) { 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. * Set if we're launching a notification right now.
*/ */
@@ -5258,6 +5353,19 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
super.dispatchDraw(canvas); 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 * A listener that is notified when the empty space below the notifications is clicked on
*/ */

View File

@@ -383,6 +383,11 @@ public class NotificationStackScrollLayoutController {
false /* resetMenu */); false /* resetMenu */);
} }
@Override
public float getTotalTranslationLength(View animView) {
return mView.getTotalTranslationLength(animView);
}
@Override @Override
public void onSnooze(StatusBarNotification sbn, public void onSnooze(StatusBarNotification sbn,
NotificationSwipeActionHelper.SnoozeOption snoozeOption) { NotificationSwipeActionHelper.SnoozeOption snoozeOption) {
@@ -1018,7 +1023,7 @@ public class NotificationStackScrollLayoutController {
mView.setQsExpansionFraction(expansionFraction); mView.setQsExpansionFraction(expansionFraction);
} }
public void setOnStackYChanged(Runnable onStackYChanged) { public void setOnStackYChanged(Consumer<Boolean> onStackYChanged) {
mView.setOnStackYChanged(onStackYChanged); mView.setOnStackYChanged(onStackYChanged);
} }

View File

@@ -324,6 +324,11 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc
} }
} }
@Override
protected float getTotalTranslationLength(View animView) {
return mCallback.getTotalTranslationLength(animView);
}
@Override @Override
public void setTranslation(View v, float translate) { public void setTranslation(View v, float translate) {
if (v instanceof SwipeableView) { if (v instanceof SwipeableView) {
@@ -466,6 +471,13 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc
void onSnooze(StatusBarNotification sbn, SnoozeOption snoozeOption); void onSnooze(StatusBarNotification sbn, SnoozeOption snoozeOption);
void onDismiss(); 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 { static class Builder {

View File

@@ -392,7 +392,7 @@ public class StackStateAnimator {
0, () -> removeTransientView(changingView), null); 0, () -> removeTransientView(changingView), null);
} else if (event.animationType == } else if (event.animationType ==
NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT) { NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT) {
if (Math.abs(changingView.getTranslation()) == changingView.getWidth() if (mHostLayout.isFullySwipedOut(changingView)
&& changingView.getTransientContainer() != null) { && changingView.getTransientContainer() != null) {
changingView.getTransientContainer().removeTransientView(changingView); changingView.getTransientContainer().removeTransientView(changingView);
} }

View File

@@ -832,6 +832,7 @@ public class NotificationPanelViewController extends PanelViewController {
mNotificationStackScrollLayoutController.setOverscrollTopChangedListener( mNotificationStackScrollLayoutController.setOverscrollTopChangedListener(
mOnOverscrollTopChangedListener); mOnOverscrollTopChangedListener);
mNotificationStackScrollLayoutController.setOnScrollListener(this::onNotificationScrolled); mNotificationStackScrollLayoutController.setOnScrollListener(this::onNotificationScrolled);
mNotificationStackScrollLayoutController.setOnStackYChanged(this::onStackYChanged);
mNotificationStackScrollLayoutController.setOnEmptySpaceClickListener( mNotificationStackScrollLayoutController.setOnEmptySpaceClickListener(
mOnEmptySpaceClickListener); mOnEmptySpaceClickListener);
addTrackingHeadsUpListener(mNotificationStackScrollLayoutController::setTrackingHeadsUp); addTrackingHeadsUpListener(mNotificationStackScrollLayoutController::setTrackingHeadsUp);
@@ -2211,15 +2212,17 @@ public class NotificationPanelViewController extends PanelViewController {
mDepthController.setQsPanelExpansion(qsExpansionFraction); mDepthController.setQsPanelExpansion(qsExpansionFraction);
} }
private Runnable mOnStackYChanged = () -> { private void onStackYChanged(boolean shouldAnimate) {
if (mQs != null) { if (mQs != null) {
if (shouldAnimate) {
mAnimateNextNotificationBounds = true;
mNotificationBoundsAnimationDelay = 0;
}
setQSClippingBounds(); setQSClippingBounds();
} }
}; };
private void onNotificationScrolled(int newScrollPosition) { 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(); updateQSExpansionEnabledAmbient();
} }
@@ -3348,7 +3351,6 @@ public class NotificationPanelViewController extends PanelViewController {
// The expandedHeight is always the full panel Height when bypassing // The expandedHeight is always the full panel Height when bypassing
expandedHeight = getMaxPanelHeightNonBypass(); expandedHeight = getMaxPanelHeightNonBypass();
} }
mNotificationStackScrollLayoutController.setOnStackYChanged(mOnStackYChanged);
mNotificationStackScrollLayoutController.setExpandedHeight(expandedHeight); mNotificationStackScrollLayoutController.setExpandedHeight(expandedHeight);
updateKeyguardBottomAreaAlpha(); updateKeyguardBottomAreaAlpha();
updateBigClockAlpha(); updateBigClockAlpha();

View File

@@ -289,6 +289,7 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
@Test @Test
public void testIconScrollXAfterTranslationAndReset() throws Exception { public void testIconScrollXAfterTranslationAndReset() throws Exception {
mGroupRow.setDismissUsingRowTranslationX(false);
mGroupRow.setTranslation(50); mGroupRow.setTranslation(50);
assertEquals(50, -mGroupRow.getEntry().getIcons().getShelfIcon().getScrollX()); assertEquals(50, -mGroupRow.getEntry().getIcons().getShelfIcon().getScrollX());