diff options
author | 2025-02-03 14:20:47 -0800 | |
---|---|---|
committer | 2025-02-03 14:20:47 -0800 | |
commit | fdff7368a7c8cb760ab65276dba4455c6bd341a3 (patch) | |
tree | 987646a647d2b0d05f6e3bccf01b5d4ad84012a0 | |
parent | 5e3edd055cb2a397e439f5e034e5e6f542379e86 (diff) | |
parent | 72d225c4a977a588bf95241635387326a1e1ab97 (diff) |
Merge "Adding magnetic behavior for non-dismissibles and other ExpandableViews." into main
7 files changed, 249 insertions, 96 deletions
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 739636e01f0b..d14ff35f824a 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 @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.stack import android.os.testableLooper import android.testing.TestableLooper.RunWithLooper +import androidx.dynamicanimation.animation.SpringForce import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -46,13 +47,14 @@ class MagneticNotificationRowManagerImplTest : SysuiTestCase() { private val childrenNumber = 5 private val stackScrollLayout = mock<NotificationStackScrollLayout>() private val sectionsManager = mock<NotificationSectionsManager>() - private val swipedMultiplier = 0.5f private val msdlPlayer = kosmos.fakeMSDLPlayer + private var canRowBeDismissed = true private val underTest = kosmos.magneticNotificationRowManagerImpl private lateinit var notificationTestHelper: NotificationTestHelper private lateinit var children: NotificationChildrenContainer + private lateinit var swipedRow: ExpandableNotificationRow @Before fun setUp() { @@ -60,14 +62,15 @@ class MagneticNotificationRowManagerImplTest : SysuiTestCase() { notificationTestHelper = NotificationTestHelper(mContext, mDependency, kosmos.testableLooper, featureFlags) children = notificationTestHelper.createGroup(childrenNumber).childrenContainer + swipedRow = children.attachedChildren[childrenNumber / 2] + configureMagneticRowListener(swipedRow) } @Test fun setMagneticAndRoundableTargets_onIdle_targetsGetSet() = kosmos.testScope.runTest { // WHEN the targets are set for a row - val row = children.attachedChildren[childrenNumber / 2] - setTargetsForRow(row) + setTargets() // THEN the magnetic and roundable targets are defined and the state is TARGETS_SET assertThat(underTest.currentState).isEqualTo(State.TARGETS_SET) @@ -79,11 +82,10 @@ class MagneticNotificationRowManagerImplTest : SysuiTestCase() { fun setMagneticRowTranslation_whenTargetsAreSet_startsPulling() = kosmos.testScope.runTest { // GIVEN targets are set - val row = children.attachedChildren[childrenNumber / 2] - setTargetsForRow(row) + setTargets() // WHEN setting a translation for the swiped row - underTest.setMagneticRowTranslation(row, translation = 100f) + underTest.setMagneticRowTranslation(swipedRow, translation = 100f) // THEN the state moves to PULLING assertThat(underTest.currentState).isEqualTo(State.PULLING) @@ -107,8 +109,7 @@ class MagneticNotificationRowManagerImplTest : SysuiTestCase() { fun setMagneticRowTranslation_whenRowIsNotSwiped_doesNotSetMagneticTranslation() = kosmos.testScope.runTest { // GIVEN that targets are set - val row = children.attachedChildren[childrenNumber / 2] - setTargetsForRow(row) + setTargets() // WHEN setting a translation for a row that is not being swiped val differentRow = children.attachedChildren[childrenNumber / 2 - 1] @@ -120,41 +121,61 @@ class MagneticNotificationRowManagerImplTest : SysuiTestCase() { } @Test - fun setMagneticRowTranslation_belowThreshold_whilePulling_setsMagneticTranslations() = + fun setMagneticRowTranslation_whenDismissible_belowThreshold_whenPulling_setsTranslations() = kosmos.testScope.runTest { // GIVEN a threshold of 100 px val threshold = 100f underTest.setSwipeThresholdPx(threshold) // GIVEN that targets are set and the rows are being pulled - val row = children.attachedChildren[childrenNumber / 2] - setTargetsForRow(row) - underTest.setMagneticRowTranslation(row, translation = 100f) + setTargets() + underTest.setMagneticRowTranslation(swipedRow, translation = 100f) // WHEN setting a translation that will fall below the threshold - val translation = threshold / swipedMultiplier - 50f - underTest.setMagneticRowTranslation(row, translation) + val translation = threshold / underTest.swipedRowMultiplier - 50f + underTest.setMagneticRowTranslation(swipedRow, translation) // THEN the targets continue to be pulled and translations are set assertThat(underTest.currentState).isEqualTo(State.PULLING) - assertThat(row.translation).isEqualTo(swipedMultiplier * translation) + assertThat(swipedRow.translation).isEqualTo(underTest.swipedRowMultiplier * translation) } @Test - fun setMagneticRowTranslation_aboveThreshold_whilePulling_detachesMagneticTargets() = + fun setMagneticRowTranslation_whenNotDismissible_belowThreshold_whenPulling_setsTranslations() = kosmos.testScope.runTest { // GIVEN a threshold of 100 px val threshold = 100f underTest.setSwipeThresholdPx(threshold) // GIVEN that targets are set and the rows are being pulled - val row = children.attachedChildren[childrenNumber / 2] - setTargetsForRow(row) - underTest.setMagneticRowTranslation(row, translation = 100f) + canRowBeDismissed = false + setTargets() + underTest.setMagneticRowTranslation(swipedRow, translation = 100f) + + // WHEN setting a translation that will fall below the threshold + val translation = threshold / underTest.swipedRowMultiplier - 50f + underTest.setMagneticRowTranslation(swipedRow, translation) + + // THEN the targets continue to be pulled and reduced translations are set + val expectedTranslation = getReducedTranslation(translation) + assertThat(underTest.currentState).isEqualTo(State.PULLING) + assertThat(swipedRow.translation).isEqualTo(expectedTranslation) + } + + @Test + fun setMagneticRowTranslation_whenDismissible_aboveThreshold_whilePulling_detaches() = + kosmos.testScope.runTest { + // GIVEN a threshold of 100 px + val threshold = 100f + underTest.setSwipeThresholdPx(threshold) + + // GIVEN that targets are set and the rows are being pulled + setTargets() + underTest.setMagneticRowTranslation(swipedRow, translation = 100f) // WHEN setting a translation that will fall above the threshold - val translation = threshold / swipedMultiplier + 50f - underTest.setMagneticRowTranslation(row, translation) + val translation = threshold / underTest.swipedRowMultiplier + 50f + underTest.setMagneticRowTranslation(swipedRow, translation) // THEN the swiped view detaches and the correct detach haptics play assertThat(underTest.currentState).isEqualTo(State.DETACHED) @@ -162,15 +183,36 @@ class MagneticNotificationRowManagerImplTest : SysuiTestCase() { } @Test + fun setMagneticRowTranslation_whenNotDismissible_aboveThreshold_whilePulling_doesNotDetach() = + kosmos.testScope.runTest { + // GIVEN a threshold of 100 px + val threshold = 100f + underTest.setSwipeThresholdPx(threshold) + + // GIVEN that targets are set and the rows are being pulled + canRowBeDismissed = false + setTargets() + underTest.setMagneticRowTranslation(swipedRow, translation = 100f) + + // WHEN setting a translation that will fall above the threshold + val translation = threshold / underTest.swipedRowMultiplier + 50f + underTest.setMagneticRowTranslation(swipedRow, translation) + + // THEN the swiped view does not detach and the reduced translation is set + val expectedTranslation = getReducedTranslation(translation) + assertThat(underTest.currentState).isEqualTo(State.PULLING) + assertThat(swipedRow.translation).isEqualTo(expectedTranslation) + } + + @Test fun setMagneticRowTranslation_whileDetached_setsTranslationAndStaysDetached() = kosmos.testScope.runTest { // GIVEN that the swiped view has been detached - val row = children.attachedChildren[childrenNumber / 2] - setDetachedState(row) + setDetachedState() // WHEN setting a new translation val translation = 300f - underTest.setMagneticRowTranslation(row, translation) + underTest.setMagneticRowTranslation(swipedRow, translation) // THEN the swiped view continues to be detached assertThat(underTest.currentState).isEqualTo(State.DETACHED) @@ -180,14 +222,13 @@ class MagneticNotificationRowManagerImplTest : SysuiTestCase() { fun onMagneticInteractionEnd_whilePulling_goesToIdle() = kosmos.testScope.runTest { // GIVEN targets are set - val row = children.attachedChildren[childrenNumber / 2] - setTargetsForRow(row) + setTargets() // WHEN setting a translation for the swiped row - underTest.setMagneticRowTranslation(row, translation = 100f) + underTest.setMagneticRowTranslation(swipedRow, translation = 100f) // WHEN the interaction ends on the row - underTest.onMagneticInteractionEnd(row, velocity = null) + underTest.onMagneticInteractionEnd(swipedRow, velocity = null) // THEN the state resets assertThat(underTest.currentState).isEqualTo(State.IDLE) @@ -197,32 +238,56 @@ class MagneticNotificationRowManagerImplTest : SysuiTestCase() { fun onMagneticInteractionEnd_whileDetached_goesToIdle() = kosmos.testScope.runTest { // GIVEN the swiped row is detached - val row = children.attachedChildren[childrenNumber / 2] - setDetachedState(row) + setDetachedState() // WHEN the interaction ends on the row - underTest.onMagneticInteractionEnd(row, velocity = null) + underTest.onMagneticInteractionEnd(swipedRow, velocity = null) // THEN the state resets assertThat(underTest.currentState).isEqualTo(State.IDLE) } - private fun setDetachedState(row: ExpandableNotificationRow) { + private fun setDetachedState() { val threshold = 100f underTest.setSwipeThresholdPx(threshold) // Set the pulling state - setTargetsForRow(row) - underTest.setMagneticRowTranslation(row, translation = 100f) + setTargets() + underTest.setMagneticRowTranslation(swipedRow, translation = 100f) // Set a translation that will fall above the threshold - val translation = threshold / swipedMultiplier + 50f - underTest.setMagneticRowTranslation(row, translation) + val translation = threshold / underTest.swipedRowMultiplier + 50f + underTest.setMagneticRowTranslation(swipedRow, translation) assertThat(underTest.currentState).isEqualTo(State.DETACHED) } - private fun setTargetsForRow(row: ExpandableNotificationRow) { - underTest.setMagneticAndRoundableTargets(row, stackScrollLayout, sectionsManager) + private fun setTargets() { + underTest.setMagneticAndRoundableTargets(swipedRow, stackScrollLayout, sectionsManager) + } + + private fun getReducedTranslation(originalTranslation: Float) = + underTest.swipedRowMultiplier * + originalTranslation * + MagneticNotificationRowManagerImpl.MAGNETIC_REDUCTION + + private fun configureMagneticRowListener(row: ExpandableNotificationRow) { + val listener = + object : MagneticRowListener { + override fun setMagneticTranslation(translation: Float) { + row.translation = translation + } + + override fun triggerMagneticForce( + endTranslation: Float, + springForce: SpringForce, + startVelocity: Float, + ) {} + + override fun cancelMagneticAnimations() {} + + override fun canRowBeDismissed(): Boolean = canRowBeDismissed + } + row.magneticRowListener = listener } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index aa7665a5b630..15f316800000 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -71,7 +71,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.dynamicanimation.animation.FloatPropertyCompat; import androidx.dynamicanimation.animation.SpringAnimation; -import androidx.dynamicanimation.animation.SpringForce; import com.android.app.animation.Interpolators; import com.android.internal.annotations.VisibleForTesting; @@ -361,39 +360,14 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } }; - private final SpringAnimation mMagneticAnimator = new SpringAnimation( - this, FloatPropertyCompat.createFloatPropertyCompat(TRANSLATE_CONTENT)); - - private final MagneticRowListener mMagneticRowListener = new MagneticRowListener() { - - @Override - public void setMagneticTranslation(float translation) { - if (mMagneticAnimator.isRunning()) { - mMagneticAnimator.animateToFinalPosition(translation); - } else { - setTranslation(translation); - } - } - - @Override - public void triggerMagneticForce(float endTranslation, @NonNull SpringForce springForce, - float startVelocity) { - cancelMagneticAnimations(); - mMagneticAnimator.setSpring(springForce); - mMagneticAnimator.setStartVelocity(startVelocity); - mMagneticAnimator.animateToFinalPosition(endTranslation); - } - - @Override - public void cancelMagneticAnimations() { - cancelSnapBackAnimation(); - cancelTranslateAnimation(); - mMagneticAnimator.cancel(); - } - }; + @Override + protected void cancelTranslationAnimations() { + cancelSnapBackAnimation(); + cancelTranslateAnimation(); + } private void cancelSnapBackAnimation() { - PhysicsAnimator<ExpandableNotificationRow> animator = + PhysicsAnimator<ExpandableView> animator = PhysicsAnimator.getInstanceIfExists(this /* target */); if (animator != null) { animator.cancel(); @@ -2044,6 +2018,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView new NotificationInlineImageCache()); float radius = getResources().getDimension(R.dimen.notification_corner_radius_small); mSmallRoundness = radius / getMaxRadius(); + mMagneticAnimator = new SpringAnimation( + this, FloatPropertyCompat.createFloatPropertyCompat(TRANSLATE_CONTENT)); initDimens(); } @@ -3300,6 +3276,19 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } /** + * For the case of an {@link ExpandableNotificationRow}, the dismissibility of the row considers + * the exposure of guts, the state of the notification entry, and if the view itself is allowed + * to be dismissed. + */ + @Override + public boolean canExpandableViewBeDismissed() { + if (areGutsExposed() || !mEntry.hasFinishedInitialization()) { + return false; + } + return canViewBeDismissed(); + } + + /** * @return Whether this view is allowed to be dismissed. Only valid for visible notifications as * otherwise some state might not be updated. */ @@ -4067,6 +4056,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mLayouts = new NotificationContentView[]{mPrivateLayout, mPublicLayout}; } + @VisibleForTesting + public void setMagneticRowListener(MagneticRowListener listener) { + mMagneticRowListener = listener; + } + /** * Equivalent to View.OnLongClickListener with coordinates */ @@ -4317,8 +4311,4 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } mLogger.logRemoveTransientRow(row.getEntry(), getEntry()); } - - public MagneticRowListener getMagneticRowListener() { - return mMagneticRowListener; - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java index f83a1d9b7833..76ba7f9ea901 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java @@ -33,6 +33,9 @@ import android.widget.FrameLayout; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.dynamicanimation.animation.DynamicAnimation; +import androidx.dynamicanimation.animation.SpringAnimation; +import androidx.dynamicanimation.animation.SpringForce; import com.android.app.animation.Interpolators; import com.android.systemui.Dumpable; @@ -42,6 +45,7 @@ import com.android.systemui.statusbar.notification.Roundable; import com.android.systemui.statusbar.notification.RoundableState; import com.android.systemui.statusbar.notification.headsup.PinnedStatus; import com.android.systemui.statusbar.notification.stack.ExpandableViewState; +import com.android.systemui.statusbar.notification.stack.MagneticRowListener; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.util.Compile; import com.android.systemui.util.DumpUtilsKt; @@ -85,6 +89,55 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable, Ro protected boolean mLastInSection; protected boolean mFirstInSection; + protected SpringAnimation mMagneticAnimator = new SpringAnimation( + this /* object */, DynamicAnimation.TRANSLATION_X); + + protected MagneticRowListener mMagneticRowListener = new MagneticRowListener() { + + @Override + public void setMagneticTranslation(float translation) { + if (mMagneticAnimator.isRunning()) { + mMagneticAnimator.animateToFinalPosition(translation); + } else { + setTranslation(translation); + } + } + + @Override + public void triggerMagneticForce(float endTranslation, @NonNull SpringForce springForce, + float startVelocity) { + cancelMagneticAnimations(); + mMagneticAnimator.setSpring(springForce); + mMagneticAnimator.setStartVelocity(startVelocity); + mMagneticAnimator.animateToFinalPosition(endTranslation); + } + + @Override + public void cancelMagneticAnimations() { + cancelTranslationAnimations(); + mMagneticAnimator.cancel(); + } + + @Override + public boolean canRowBeDismissed() { + return canExpandableViewBeDismissed(); + } + }; + + /** + * @return true if the ExpandableView can be dismissed. False otherwise. + */ + public boolean canExpandableViewBeDismissed() { + return false; + } + + /** Cancel any trailing animations on the translation of the view */ + protected void cancelTranslationAnimations(){} + + public MagneticRowListener getMagneticRowListener() { + return mMagneticRowListener; + } + public ExpandableView(Context context, AttributeSet attrs) { super(context, attrs); mViewState = createExpandableViewState(); 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 61aaf4998e87..3941700496f4 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 @@ -57,7 +57,7 @@ constructor( SpringForce().setStiffness(SNAP_BACK_STIFFNESS).setDampingRatio(SNAP_BACK_DAMPING_RATIO) // Multiplier applied to the translation of a row while swiped - private val swipedRowMultiplier = + val swipedRowMultiplier = MAGNETIC_TRANSLATION_MULTIPLIERS[MAGNETIC_TRANSLATION_MULTIPLIERS.size / 2] override fun setSwipeThresholdPx(thresholdPx: Float) { @@ -111,24 +111,22 @@ constructor( ): Boolean { if (!row.isSwipedTarget()) return false + val canTargetBeDismissed = + currentMagneticListeners.swipedListener()?.canRowBeDismissed() ?: false when (currentState) { State.IDLE -> { logger.logMagneticRowTranslationNotSet(currentState, row.entry) return false } State.TARGETS_SET -> { - pullTargets(translation) + pullTargets(translation, canTargetBeDismissed) currentState = State.PULLING } State.PULLING -> { - val targetTranslation = swipedRowMultiplier * translation - val crossedThreshold = abs(targetTranslation) >= magneticDetachThreshold - if (crossedThreshold) { - snapNeighborsBack() - currentMagneticListeners.swipedListener()?.let { detach(it, translation) } - currentState = State.DETACHED + if (canTargetBeDismissed) { + pullDismissibleRow(translation) } else { - pullTargets(translation) + pullTargets(translation, canSwipedBeDismissed = false) } } State.DETACHED -> { @@ -139,23 +137,49 @@ constructor( return true } - private fun pullTargets(translation: Float) { + private fun pullDismissibleRow(translation: Float) { + val targetTranslation = swipedRowMultiplier * translation + val crossedThreshold = abs(targetTranslation) >= magneticDetachThreshold + if (crossedThreshold) { + snapNeighborsBack() + currentMagneticListeners.swipedListener()?.let { detach(it, translation) } + currentState = State.DETACHED + } else { + pullTargets(translation, canSwipedBeDismissed = true) + } + } + + private fun pullTargets(translation: Float, canSwipedBeDismissed: Boolean) { var targetTranslation: Float currentMagneticListeners.forEachIndexed { i, listener -> - targetTranslation = MAGNETIC_TRANSLATION_MULTIPLIERS[i] * translation - listener?.setMagneticTranslation(targetTranslation) + listener?.let { + if (!canSwipedBeDismissed || !it.canRowBeDismissed()) { + // Use a reduced translation if the target swiped can't be dismissed or if the + // target itself can't be dismissed + targetTranslation = + MAGNETIC_TRANSLATION_MULTIPLIERS[i] * translation * MAGNETIC_REDUCTION + } else { + targetTranslation = MAGNETIC_TRANSLATION_MULTIPLIERS[i] * translation + } + it.setMagneticTranslation(targetTranslation) + } } - playPullHaptics(mappedTranslation = swipedRowMultiplier * translation) + playPullHaptics(mappedTranslation = swipedRowMultiplier * translation, canSwipedBeDismissed) } - private fun playPullHaptics(mappedTranslation: Float) { + private fun playPullHaptics(mappedTranslation: Float, canSwipedBeDismissed: Boolean) { val normalizedTranslation = abs(mappedTranslation) / magneticDetachThreshold - val vibrationScale = - (normalizedTranslation * MAX_VIBRATION_SCALE).pow(VIBRATION_PERCEPTION_EXPONENT) + val scaleFactor = + if (canSwipedBeDismissed) { + WEAK_VIBRATION_SCALE + } else { + STRONG_VIBRATION_SCALE + } + val vibrationScale = scaleFactor * normalizedTranslation msdlPlayer.playToken( MSDLToken.DRAG_INDICATOR_CONTINUOUS, InteractionProperties.DynamicVibrationScale( - scale = vibrationScale, + scale = vibrationScale.pow(VIBRATION_PERCEPTION_EXPONENT), vibrationAttributes = VIBRATION_ATTRIBUTES_PIPELINING, ), ) @@ -233,6 +257,8 @@ constructor( */ private val MAGNETIC_TRANSLATION_MULTIPLIERS = listOf(0.18f, 0.28f, 0.5f, 0.28f, 0.18f) + const val MAGNETIC_REDUCTION = 0.65f + /** Spring parameters for physics animators */ private const val DETACH_STIFFNESS = 800f private const val DETACH_DAMPING_RATIO = 0.95f @@ -244,7 +270,8 @@ constructor( .setUsage(VibrationAttributes.USAGE_TOUCH) .setFlags(VibrationAttributes.FLAG_PIPELINED_EFFECT) .build() - private const val MAX_VIBRATION_SCALE = 0.2f private const val VIBRATION_PERCEPTION_EXPONENT = 1 / 0.89f + private const val WEAK_VIBRATION_SCALE = 0.2f + private const val STRONG_VIBRATION_SCALE = 0.45f } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticRowListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticRowListener.kt index 8a1adfe95392..46036d4c1fad 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticRowListener.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticRowListener.kt @@ -41,4 +41,7 @@ interface MagneticRowListener { /** Cancel any animations related to the magnetic interactions of the row */ fun cancelMagneticAnimations() + + /** Can the row be dismissed. */ + fun canRowBeDismissed(): Boolean } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index ce1fc97cbffe..d6b34b068cc5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -6609,10 +6609,7 @@ public class NotificationStackScrollLayout static boolean canChildBeDismissed(View v) { if (v instanceof ExpandableNotificationRow row) { - if (row.areGutsExposed() || !row.getEntry().hasFinishedInitialization()) { - return false; - } - return row.canViewBeDismissed(); + return row.canExpandableViewBeDismissed(); } return false; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelper.kt index 74e8b8ef29c2..b69b936ea9f0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelper.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelper.kt @@ -3,7 +3,9 @@ package com.android.systemui.statusbar.notification.stack import androidx.core.view.children import androidx.core.view.isVisible import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.NotificationShelf import com.android.systemui.statusbar.notification.Roundable +import com.android.systemui.statusbar.notification.footer.ui.view.FooterView import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.ExpandableView import javax.inject.Inject @@ -129,6 +131,10 @@ class NotificationTargetsHelper @Inject constructor() { magneticTargets[leftIndex] = leftElement.magneticRowListener leftIndex-- } else { + if (leftElement.isValidMagneticBoundary()) { + // Add the boundary and then stop the iterating + magneticTargets[leftIndex] = leftElement?.magneticRowListener + } canMoveLeft = false } } @@ -138,12 +144,24 @@ class NotificationTargetsHelper @Inject constructor() { magneticTargets[rightIndex] = rightElement.magneticRowListener rightIndex++ } else { + if (rightElement.isValidMagneticBoundary()) { + // Add the boundary and then stop the iterating + magneticTargets[rightIndex] = rightElement?.magneticRowListener + } canMoveRight = false } } } return magneticTargets } + + private fun ExpandableView?.isValidMagneticBoundary(): Boolean = + when (this) { + is FooterView, + is NotificationShelf, + is SectionHeaderView -> true + else -> false + } } /** |