summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Juan Sebastian Martinez <juansmartinez@google.com> 2025-02-27 12:43:11 -0800
committer Juan Sebastian Martinez <juansmartinez@google.com> 2025-03-10 08:27:13 -0700
commit1b11d6bc8866a9d15ae8690a107ab8b8c8cc2ff7 (patch)
tree85ab2219b5cee1b5fbd57fd45a8f2bab0e7f86e3
parent14cb9b41efe2074ae32484c1de33ead0430dca1c (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
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImplTest.kt29
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/SwipeHelper.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManager.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImpl.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java6
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();