Aggressively reset SwipeHelper state

* Resetting just swipe (used internally as resetSwipeState) when snap was also in progress will now clear the mSnappingChild field, which also ensures snapChildIfNeeded() doesn't return early.
* Resetting all state (used externally as resetTouchState) will always clear mSnappingChild and will cancel snap animations and reset state.
* NSSL will now also resetTouchState whenever the notifications are hidden or unhidden.

These changes all assume that the mTouchedView still pointed to the view which was misaligned in order to fix the bug.  If the mTouchedView does NOT point to the misaligned view, then the fix for this bug is going to require one of two things:
 1) we could track all active swipe/snap/dismiss views, rather than just the latest one, so that we can appropriately tear down all state.  This is a major project.
 2) we could, when notifications become visible, iterate all rows recursively and reset all their swipe translations naively.  I'm worried about possible jank this could cause.

Fixes: 259529672
Test: dumpsysui SwipeHelper
Test: atest NotificationStackScrollLayoutTest
Change-Id: I53d8e217e7f133388943e07be088d63623c4b735
This commit is contained in:
Jeff DeCew
2023-05-26 13:37:05 -04:00
parent a3dbe509c1
commit 27c8877af3
3 changed files with 39 additions and 12 deletions

View File

@@ -817,13 +817,39 @@ public class SwipeHelper implements Gefingerpoken, Dumpable {
}
}
public void resetSwipeState() {
View swipedView = getSwipedView();
private void resetSwipeState() {
resetSwipeStates(/* resetAll= */ false);
}
public void resetTouchState() {
resetSwipeStates(/* resetAll= */ true);
}
/** This method resets the swipe state, and if `resetAll` is true, also resets the snap state */
private void resetSwipeStates(boolean resetAll) {
final View touchedView = mTouchedView;
final boolean wasSnapping = mSnappingChild;
final boolean wasSwiping = mIsSwiping;
mTouchedView = null;
mIsSwiping = false;
if (swipedView != null) {
snapChildIfNeeded(swipedView, false, 0);
onChildSnappedBack(swipedView, 0);
// If we were swiping, then we resetting swipe requires resetting everything.
resetAll |= wasSwiping;
if (resetAll) {
mSnappingChild = false;
}
if (touchedView == null) return; // No view to reset visually
// When snap needs to be reset, first thing is to cancel any translation animation
final boolean snapNeedsReset = resetAll && wasSnapping;
if (snapNeedsReset) {
cancelTranslateAnimation(touchedView);
}
// actually reset the view to default state
if (resetAll) {
snapChildIfNeeded(touchedView, false, 0);
}
// report if a swipe or snap was reset.
if (wasSwiping || snapNeedsReset) {
onChildSnappedBack(touchedView, 0);
}
}

View File

@@ -4395,6 +4395,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
boolean nowHiddenAtAll = mAmbientState.isHiddenAtAll();
if (nowFullyHidden != wasFullyHidden) {
updateVisibility();
mSwipeHelper.resetTouchState();
}
if (!wasHiddenAtAll && nowHiddenAtAll) {
resetExposedMenuView(true /* animate */, true /* animate */);
@@ -5850,7 +5851,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
private void cancelActiveSwipe() {
mSwipeHelper.resetSwipeState();
mSwipeHelper.resetTouchState();
updateContinuousShadowDrawing();
}

View File

@@ -794,7 +794,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
}
@Test
public void onShadeClosesWithAnimationWillResetSwipeState() {
public void onShadeClosesWithAnimationWillResetTouchState() {
// GIVEN shade is expanded
mStackScroller.setIsExpanded(true);
clearInvocations(mNotificationSwipeHelper);
@@ -804,12 +804,12 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
mStackScroller.setIsExpanded(false);
mStackScroller.onExpansionStopped();
// VERIFY swipe is reset
verify(mNotificationSwipeHelper).resetSwipeState();
// VERIFY touch is reset
verify(mNotificationSwipeHelper).resetTouchState();
}
@Test
public void onShadeClosesWithoutAnimationWillResetSwipeState() {
public void onShadeClosesWithoutAnimationWillResetTouchState() {
// GIVEN shade is expanded
mStackScroller.setIsExpanded(true);
clearInvocations(mNotificationSwipeHelper);
@@ -817,8 +817,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
// WHEN closing the shade without the animation
mStackScroller.setIsExpanded(false);
// VERIFY swipe is reset
verify(mNotificationSwipeHelper).resetSwipeState();
// VERIFY touch is reset
verify(mNotificationSwipeHelper).resetTouchState();
}
@Test