diff options
| author | 2025-02-27 12:43:11 -0800 | |
|---|---|---|
| committer | 2025-03-10 08:27:13 -0700 | |
| commit | 1b11d6bc8866a9d15ae8690a107ab8b8c8cc2ff7 (patch) | |
| tree | 85ab2219b5cee1b5fbd57fd45a8f2bab0e7f86e3 | |
| parent | 14cb9b41efe2074ae32484c1de33ead0430dca1c (diff) | |
Consolidating magnetic detachment with dismissibility.
The condition that determines if a notification has been swiped far
enough and fast enought to be dismissed now depends on whether the
swiped notification has magnetically detached or not. We also flag the
conflicting previous haptic feedback when the notification is swiped
past the previous length threshold.
Test: MagneticNotificationRowManagerImplTest
Test: NotificationSwipeHelperTest
Flag: com.android.systemui.magnetic_notification_swipes
Bug: 397418669
Change-Id: I9b12c8a90176b6bc27c370ed463e713a33cbb292
9 files changed, 98 insertions, 20 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java index 9fdfca14a5b2..af52c31b1c53 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java @@ -28,6 +28,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.platform.test.annotations.DisableFlags; import android.provider.Settings; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; @@ -38,6 +39,7 @@ import android.view.ViewGroup; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; +import com.android.systemui.Flags; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; @@ -413,6 +415,7 @@ public class NotificationMenuRowTest extends LeakCheckedTest { assertTrue("when alpha is .5, menu is visible", row.isMenuVisible()); } + @DisableFlags(Flags.FLAG_MAGNETIC_NOTIFICATION_SWIPES) @Test public void testOnTouchMove() { NotificationMenuRow row = Mockito.spy( diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImplTest.kt index ccc8be7de038..376da236f2b9 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImplTest.kt @@ -130,7 +130,9 @@ class MagneticNotificationRowManagerImplTest : SysuiTestCase() { kosmos.testScope.runTest { // GIVEN a threshold of 100 px val threshold = 100f - underTest.setSwipeThresholdPx(threshold) + underTest.onDensityChange( + threshold / MagneticNotificationRowManager.MAGNETIC_DETACH_THRESHOLD_DP + ) // GIVEN that targets are set and the rows are being pulled setTargets() @@ -150,7 +152,9 @@ class MagneticNotificationRowManagerImplTest : SysuiTestCase() { kosmos.testScope.runTest { // GIVEN a threshold of 100 px val threshold = 100f - underTest.setSwipeThresholdPx(threshold) + underTest.onDensityChange( + threshold / MagneticNotificationRowManager.MAGNETIC_DETACH_THRESHOLD_DP + ) // GIVEN that targets are set and the rows are being pulled canRowBeDismissed = false @@ -172,7 +176,9 @@ class MagneticNotificationRowManagerImplTest : SysuiTestCase() { kosmos.testScope.runTest { // GIVEN a threshold of 100 px val threshold = 100f - underTest.setSwipeThresholdPx(threshold) + underTest.onDensityChange( + threshold / MagneticNotificationRowManager.MAGNETIC_DETACH_THRESHOLD_DP + ) // GIVEN that targets are set and the rows are being pulled setTargets() @@ -192,7 +198,9 @@ class MagneticNotificationRowManagerImplTest : SysuiTestCase() { kosmos.testScope.runTest { // GIVEN a threshold of 100 px val threshold = 100f - underTest.setSwipeThresholdPx(threshold) + underTest.onDensityChange( + threshold / MagneticNotificationRowManager.MAGNETIC_DETACH_THRESHOLD_DP + ) // GIVEN that targets are set and the rows are being pulled canRowBeDismissed = false @@ -294,6 +302,15 @@ class MagneticNotificationRowManagerImplTest : SysuiTestCase() { assertThat(underTest.isSwipedViewRoundableSet).isFalse() } + @Test + fun isMagneticRowDismissible_isDismissibleWhenDetached() = + kosmos.testScope.runTest { + setDetachedState() + + val isDismissible = underTest.isMagneticRowSwipeDetached(swipedRow) + assertThat(isDismissible).isTrue() + } + @After fun tearDown() { // We reset the manager so that all MagneticRowListener can cancel all animations @@ -302,7 +319,9 @@ class MagneticNotificationRowManagerImplTest : SysuiTestCase() { private fun setDetachedState() { val threshold = 100f - underTest.setSwipeThresholdPx(threshold) + underTest.onDensityChange( + threshold / MagneticNotificationRowManager.MAGNETIC_DETACH_THRESHOLD_DP + ) // Set the pulling state setTargets() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java index 789701f5e4b0..de48f4018989 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java @@ -49,6 +49,7 @@ import android.view.ViewConfiguration; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; +import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.flags.FakeFeatureFlags; @@ -362,6 +363,7 @@ public class NotificationSwipeHelperTest extends SysuiTestCase { verify(mSwipeHelper, times(1)).isFalseGesture(); } + @DisableFlags(Flags.FLAG_MAGNETIC_NOTIFICATION_SWIPES) @Test public void testIsDismissGesture_farEnough() { doReturn(false).when(mSwipeHelper).isFalseGesture(); @@ -374,6 +376,20 @@ public class NotificationSwipeHelperTest extends SysuiTestCase { verify(mSwipeHelper, times(1)).isFalseGesture(); } + @EnableFlags(Flags.FLAG_MAGNETIC_NOTIFICATION_SWIPES) + @Test + public void testIsDismissGesture_magneticSwipeIsDismissible() { + doReturn(false).when(mSwipeHelper).isFalseGesture(); + doReturn(false).when(mSwipeHelper).swipedFarEnough(); + doReturn(false).when(mSwipeHelper).swipedFastEnough(); + doReturn(true).when(mCallback).isMagneticViewDetached(any()); + when(mCallback.canChildBeDismissedInDirection(any(), anyBoolean())).thenReturn(true); + when(mEvent.getActionMasked()).thenReturn(MotionEvent.ACTION_UP); + + assertTrue("Should be a dismissal", mSwipeHelper.isDismissGesture(mEvent)); + verify(mSwipeHelper, times(1)).isFalseGesture(); + } + @Test public void testIsDismissGesture_notFarOrFastEnough() { doReturn(false).when(mSwipeHelper).isFalseGesture(); diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java index c78f75a334fd..e3afe2e190e9 100644 --- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java @@ -778,18 +778,26 @@ public class SwipeHelper implements Gefingerpoken, Dumpable { protected boolean swipedFarEnough() { float translation = getTranslation(mTouchedView); - return Math.abs(translation) > SWIPED_FAR_ENOUGH_SIZE_FRACTION * getSize( - mTouchedView); + return Math.abs(translation) > SWIPED_FAR_ENOUGH_SIZE_FRACTION * getSize(mTouchedView); } public boolean isDismissGesture(MotionEvent ev) { float translation = getTranslation(mTouchedView); return ev.getActionMasked() == MotionEvent.ACTION_UP && !mFalsingManager.isUnlockingDisabled() - && !isFalseGesture() && (swipedFastEnough() || swipedFarEnough()) + && !isFalseGesture() && isSwipeDismissible() && mCallback.canChildBeDismissedInDirection(mTouchedView, translation > 0); } + /** Can the swipe gesture on the touched view be considered as a dismiss intention */ + public boolean isSwipeDismissible() { + if (magneticNotificationSwipes()) { + return mCallback.isMagneticViewDetached(mTouchedView) || swipedFastEnough(); + } else { + return swipedFastEnough() || swipedFarEnough(); + } + } + /** Returns true if the gesture should be rejected. */ public boolean isFalseGesture() { boolean falsingDetected = mCallback.isAntiFalsingNeeded(); @@ -970,6 +978,13 @@ public class SwipeHelper implements Gefingerpoken, Dumpable { void onMagneticInteractionEnd(View view, float velocity); /** + * Determine if a view managed by magnetic interactions is magnetically detached + * @param view The magnetic view + * @return if the view is detached according to its magnetic state. + */ + boolean isMagneticViewDetached(View view); + + /** * Called when the child is long pressed and available to start drag and drop. * * @param v the view that was long pressed. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java index e89a76fd5a69..286f07b7413d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java @@ -32,7 +32,6 @@ import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.Looper; import android.provider.Settings; -import android.service.notification.StatusBarNotification; import android.util.ArrayMap; import android.view.LayoutInflater; import android.view.View; @@ -358,7 +357,9 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl final float dismissThreshold = getDismissThreshold(); final boolean snappingToDismiss = delta < -dismissThreshold || delta > dismissThreshold; if (mSnappingToDismiss != snappingToDismiss) { - getMenuView().performHapticFeedback(CLOCK_TICK); + if (!Flags.magneticNotificationSwipes()) { + getMenuView().performHapticFeedback(CLOCK_TICK); + } } mSnappingToDismiss = snappingToDismiss; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManager.kt index aa6951715755..fa7f63046895 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManager.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManager.kt @@ -33,12 +33,12 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow interface MagneticNotificationRowManager { /** - * Set the swipe threshold in pixels. After crossing the threshold, the magnetic target detaches - * and the magnetic neighbors snap back. + * Notifies a change in the device density. The density can be used to compute the values of + * thresholds in pixels. * - * @param[threshold] Swipe threshold in pixels. + * @param[density] The device density. */ - fun setSwipeThresholdPx(thresholdPx: Float) + fun onDensityChange(density: Float) /** * Set the magnetic and roundable targets of a magnetic swipe interaction. @@ -87,6 +87,9 @@ interface MagneticNotificationRowManager { */ fun onMagneticInteractionEnd(row: ExpandableNotificationRow, velocity: Float? = null) + /** Determine if the given [ExpandableNotificationRow] has been magnetically detached. */ + fun isMagneticRowSwipeDetached(row: ExpandableNotificationRow): Boolean + /* Reset any roundness that magnetic targets may have */ fun resetRoundness() @@ -109,7 +112,7 @@ interface MagneticNotificationRowManager { val Empty: MagneticNotificationRowManager get() = object : MagneticNotificationRowManager { - override fun setSwipeThresholdPx(thresholdPx: Float) {} + override fun onDensityChange(density: Float) {} override fun setMagneticAndRoundableTargets( swipingRow: ExpandableNotificationRow, @@ -127,6 +130,10 @@ interface MagneticNotificationRowManager { velocity: Float?, ) {} + override fun isMagneticRowSwipeDetached( + row: ExpandableNotificationRow + ): Boolean = false + override fun resetRoundness() {} override fun reset() {} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImpl.kt index 9bd5a5bd903f..fa94c5700027 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImpl.kt @@ -62,8 +62,9 @@ constructor( val swipedRowMultiplier = MAGNETIC_TRANSLATION_MULTIPLIERS[MAGNETIC_TRANSLATION_MULTIPLIERS.size / 2] - override fun setSwipeThresholdPx(thresholdPx: Float) { - magneticDetachThreshold = thresholdPx + override fun onDensityChange(density: Float) { + magneticDetachThreshold = + density * MagneticNotificationRowManager.MAGNETIC_DETACH_THRESHOLD_DP } override fun setMagneticAndRoundableTargets( @@ -254,6 +255,9 @@ constructor( } } + override fun isMagneticRowSwipeDetached(row: ExpandableNotificationRow): Boolean = + row.isSwipedTarget() && currentState == State.DETACHED + override fun resetRoundness() = notificationRoundnessManager.clear() override fun reset() { 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 124e6f590bfe..bdb65d456473 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 @@ -477,15 +477,22 @@ public class NotificationStackScrollLayoutController implements Dumpable { } @Override + public boolean isMagneticViewDetached(View view) { + if (view instanceof ExpandableNotificationRow row) { + return mMagneticNotificationRowManager.isMagneticRowSwipeDetached(row); + } else { + return false; + } + } + + @Override public float getTotalTranslationLength(View animView) { return mView.getTotalTranslationLength(animView); } @Override public void onDensityScaleChange(float density) { - mMagneticNotificationRowManager.setSwipeThresholdPx( - density * MagneticNotificationRowManager.MAGNETIC_DETACH_THRESHOLD_DP - ); + mMagneticNotificationRowManager.onDensityChange(density); } @Override 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 c5a846e1da05..33b94783b24a 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 @@ -33,6 +33,7 @@ import android.view.ViewConfiguration; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.jank.InteractionJankMonitor; +import com.android.systemui.Flags; import com.android.systemui.SwipeHelper; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; @@ -191,6 +192,11 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc @Override public boolean handleUpEvent(MotionEvent ev, View animView, float velocity, float translation) { + if (Flags.magneticNotificationSwipes() + && (mCallback.isMagneticViewDetached(animView) || swipedFastEnough())) { + dismiss(animView, velocity); + return true; + } NotificationMenuRowPlugin menuRow = getCurrentMenuRow(); if (menuRow != null) { menuRow.onTouchEnd(); |