When swiping notifications it doesn't clip early anymore

Previously notifications started to clip before reaching
the edge. We're now changinging the flags to dismiss with
translation instead of clipping the contents.

This is change 1/2 as the notification container should
clip the notification now, which is especially noticeable
in split shade.

Bug: 181887261
Test: dismiss notifications, visually observe no clipping
Change-Id: I003b1486ce64dd127a90ca2d9f982230eba79f60
This commit is contained in:
Selim Cinek
2021-06-11 10:11:50 +02:00
parent 07045f95e0
commit a21cbcdf03
10 changed files with 166 additions and 46 deletions

View File

@@ -396,10 +396,6 @@
<!-- Whether or not the notifications should always fade as they are dismissed. -->
<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
off-screen. -->
<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) ||
(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.
*/

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
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

View File

@@ -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())

View File

@@ -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() {

View File

@@ -475,6 +475,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
* 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 +533,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();
@@ -1600,8 +1605,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 +2532,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 +2544,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) {
@@ -2759,7 +2769,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 +3046,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 +3054,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);
}
@@ -4051,7 +4064,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;
}
@@ -4636,6 +4649,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mStatusBarState = statusBarState;
mAmbientState.setStatusBarState(statusBarState);
updateSpeedBumpIndex();
updateDismissBehavior();
}
void onStatePostChange(boolean fromShadeLocked) {
@@ -5224,6 +5238,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 +5298,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
*/

View File

@@ -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) {

View File

@@ -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 {

View File

@@ -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);
}

View File

@@ -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());