diff options
| author | 2024-10-03 20:07:58 +0000 | |
|---|---|---|
| committer | 2024-10-03 20:07:58 +0000 | |
| commit | b885520ee23ba3b08b40e719eaeedddeb987c3bf (patch) | |
| tree | 8cb1fad00b1ad9b1a12532c5f445a828749fe268 | |
| parent | 66c96334c962091f3e7d90c8b9924ec1c0fe34d6 (diff) | |
| parent | 9a302637d1751cb274069c170d7b0e637e3c6c3a (diff) | |
Merge "Add ability to make transitions not replaceable" into main
7 files changed, 110 insertions, 27 deletions
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt index 5fa5db880cce..085157ac72b9 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt @@ -226,7 +226,6 @@ internal class DraggableHandlerImpl( val fromSource = resolveSwipeSource(startedPosition) val upOrLeft = resolveSwipe(pointersDown, fromSource, isUpOrLeft = true) val downOrRight = resolveSwipe(pointersDown, fromSource, isUpOrLeft = false) - return if (fromSource == null) { Swipes( upOrLeft = null, @@ -366,10 +365,18 @@ private class DragControllerImpl( return 0f } + val currentTransitionIrreversible = + if (swipeAnimation.isUpOrLeft) { + swipes.upOrLeftResult?.isIrreversible ?: false + } else { + swipes.downOrRightResult?.isIrreversible ?: false + } + val needNewTransition = - hasReachedToContent || - result.toContent(layoutState.currentScene) != swipeAnimation.toContent || - result.transitionKey != swipeAnimation.contentTransition.key + !currentTransitionIrreversible && + (hasReachedToContent || + result.toContent(layoutState.currentScene) != swipeAnimation.toContent || + result.transitionKey != swipeAnimation.contentTransition.key) if (needNewTransition) { // Make sure the current transition will finish to the right current scene. diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt index cec888380513..c9a4d5808cac 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt @@ -498,6 +498,12 @@ sealed class UserActionResult( * bigger than 100% when the user released their finger. ` */ open val requiresFullDistanceSwipe: Boolean, + + /** + * Whether swiping back in the opposite direction past the origin point of the swipe can replace + * the action with the action for the opposite direction. + */ + open val isIrreversible: Boolean = false, ) { internal abstract fun toContent(currentScene: SceneKey): ContentKey @@ -507,6 +513,7 @@ sealed class UserActionResult( val toScene: SceneKey, override val transitionKey: TransitionKey? = null, override val requiresFullDistanceSwipe: Boolean = false, + override val isIrreversible: Boolean = false, ) : UserActionResult(transitionKey, requiresFullDistanceSwipe) { override fun toContent(currentScene: SceneKey): ContentKey = toScene } @@ -516,6 +523,7 @@ sealed class UserActionResult( val overlay: OverlayKey, override val transitionKey: TransitionKey? = null, override val requiresFullDistanceSwipe: Boolean = false, + override val isIrreversible: Boolean = false, ) : UserActionResult(transitionKey, requiresFullDistanceSwipe) { override fun toContent(currentScene: SceneKey): ContentKey = overlay } @@ -558,7 +566,14 @@ sealed class UserActionResult( * the user released their finger. */ requiresFullDistanceSwipe: Boolean = false, - ): UserActionResult = ChangeScene(toScene, transitionKey, requiresFullDistanceSwipe) + + /** + * Whether swiping back in the opposite direction past the origin point of the swipe can + * replace the action with the action for the opposite direction. + */ + isIrreversible: Boolean = false, + ): UserActionResult = + ChangeScene(toScene, transitionKey, requiresFullDistanceSwipe, isIrreversible) /** A [UserActionResult] that shows [toOverlay]. */ operator fun invoke( diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt index 302f207fd0bf..ecef6be49df8 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt @@ -507,6 +507,54 @@ class DraggableHandlerTest { } @Test + fun onDragWithActionsInBothDirections_dragToOppositeDirectionReplacesAction() = runGestureTest { + // We are on SceneA. UP -> B, DOWN-> C. + val dragController = onDragStarted(overSlop = up(fractionOfScreen = 0.2f)) + assertTransition( + currentScene = SceneA, + fromScene = SceneA, + toScene = SceneB, + progress = 0.2f, + ) + + // Reverse drag direction, it will replace the previous transition + dragController.onDragDelta(pixels = down(fractionOfScreen = 0.5f)) + assertTransition( + currentScene = SceneA, + fromScene = SceneA, + toScene = SceneC, + progress = 0.3f, + ) + } + + @Test + fun onDragWithActionsInBothDirections_dragToOppositeDirectionNotReplaceable() = runGestureTest { + // We are on SceneA. UP -> B, DOWN-> C. The up swipe is not replaceable though. + mutableUserActionsA = + mapOf(Swipe.Up to UserActionResult(SceneB, isIrreversible = true), Swipe.Down to SceneC) + val dragController = + onDragStarted( + startedPosition = Offset(SCREEN_SIZE * 0.5f, SCREEN_SIZE * 0.5f), + overSlop = up(fractionOfScreen = 0.2f), + ) + assertTransition( + currentScene = SceneA, + fromScene = SceneA, + toScene = SceneB, + progress = 0.2f, + ) + + // Reverse drag direction, it cannot replace the previous transition + dragController.onDragDelta(pixels = down(fractionOfScreen = 0.5f)) + assertTransition( + currentScene = SceneA, + fromScene = SceneA, + toScene = SceneB, + progress = -0.3f, + ) + } + + @Test fun onDragFromEdge_startTransitionToEdgeAction() = runGestureTest { navigateToSceneC() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalUserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalUserActionsViewModelTest.kt index 58b59ffd8894..755c4ebf5016 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalUserActionsViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalUserActionsViewModelTest.kt @@ -85,7 +85,8 @@ class CommunalUserActionsViewModelTest : SysuiTestCase() { assertThat(actions).isNotEmpty() assertThat(actions?.get(Swipe.End)).isEqualTo(UserActionResult(SceneFamilies.Home)) assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer)) - assertThat(actions?.get(Swipe.Down)).isEqualTo(UserActionResult(Scenes.Shade)) + assertThat(actions?.get(Swipe.Down)) + .isEqualTo(UserActionResult(Scenes.Shade, isIrreversible = true)) setUpState( isShadeTouchable = false, @@ -102,7 +103,8 @@ class CommunalUserActionsViewModelTest : SysuiTestCase() { assertThat(actions).isNotEmpty() assertThat(actions?.get(Swipe.End)).isEqualTo(UserActionResult(SceneFamilies.Home)) assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone)) - assertThat(actions?.get(Swipe.Down)).isEqualTo(UserActionResult(Scenes.Shade)) + assertThat(actions?.get(Swipe.Down)) + .isEqualTo(UserActionResult(Scenes.Shade, isIrreversible = true)) } @Test @@ -120,7 +122,7 @@ class CommunalUserActionsViewModelTest : SysuiTestCase() { assertThat(actions?.get(Swipe.End)).isEqualTo(UserActionResult(SceneFamilies.Home)) assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer)) assertThat(actions?.get(Swipe.Down)) - .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade)) + .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade, isIrreversible = true)) setUpState( isShadeTouchable = false, @@ -138,7 +140,7 @@ class CommunalUserActionsViewModelTest : SysuiTestCase() { assertThat(actions?.get(Swipe.End)).isEqualTo(UserActionResult(SceneFamilies.Home)) assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone)) assertThat(actions?.get(Swipe.Down)) - .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade)) + .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade, isIrreversible = true)) } @Test @@ -156,7 +158,9 @@ class CommunalUserActionsViewModelTest : SysuiTestCase() { assertThat(actions?.get(Swipe.End)).isEqualTo(UserActionResult(SceneFamilies.Home)) assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer)) assertThat(actions?.get(Swipe.Down)) - .isEqualTo(UserActionResult(Overlays.NotificationsShade)) + .isEqualTo( + UserActionResult.ShowOverlay(Overlays.NotificationsShade, isIrreversible = true) + ) setUpState( isShadeTouchable = false, @@ -170,7 +174,9 @@ class CommunalUserActionsViewModelTest : SysuiTestCase() { assertThat(actions?.get(Swipe.End)).isEqualTo(UserActionResult(SceneFamilies.Home)) assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone)) assertThat(actions?.get(Swipe.Down)) - .isEqualTo(UserActionResult(Overlays.NotificationsShade)) + .isEqualTo( + UserActionResult.ShowOverlay(Overlays.NotificationsShade, isIrreversible = true) + ) } private fun TestScope.setUpState( diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelTest.kt index fb1bf281715d..6397979d9627 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelTest.kt @@ -303,7 +303,8 @@ class LockscreenUserActionsViewModelTest : SysuiTestCase() { // Top edge is not applicable in dual shade, as well as two-finger swipe. assertThat(downDestination).isNull() } else { - assertThat(downDestination).isEqualTo(ShowOverlay(Overlays.NotificationsShade)) + assertThat(downDestination) + .isEqualTo(ShowOverlay(Overlays.NotificationsShade, isIrreversible = true)) assertThat(downDestination?.transitionKey).isNull() } @@ -320,7 +321,7 @@ class LockscreenUserActionsViewModelTest : SysuiTestCase() { downWithTwoPointers -> assertThat(downFromTopRightDestination).isNull() else -> { assertThat(downFromTopRightDestination) - .isEqualTo(ShowOverlay(Overlays.QuickSettingsShade)) + .isEqualTo(ShowOverlay(Overlays.QuickSettingsShade, isIrreversible = true)) assertThat(downFromTopRightDestination?.transitionKey).isNull() } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneUserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneUserActionsViewModelTest.kt index 5c47f552e400..47fae9f0629f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneUserActionsViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneUserActionsViewModelTest.kt @@ -106,7 +106,7 @@ class GoneUserActionsViewModelTest : SysuiTestCase() { runCurrent() assertThat(userActions?.get(swipeDownFromTopWithTwoFingers())) - .isEqualTo(UserActionResult(Scenes.QuickSettings)) + .isEqualTo(UserActionResult(Scenes.QuickSettings, isIrreversible = true)) } @Test @@ -118,7 +118,7 @@ class GoneUserActionsViewModelTest : SysuiTestCase() { runCurrent() assertThat(userActions?.get(swipeDownFromTopWithTwoFingers())) - .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade)) + .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade, isIrreversible = true)) } @Test diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActions.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActions.kt index 65b6231705d4..e5f684635ac7 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActions.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActions.kt @@ -30,41 +30,47 @@ import com.android.systemui.scene.ui.viewmodel.SceneContainerEdge fun singleShadeActions( requireTwoPointersForTopEdgeForQs: Boolean = false ): Array<Pair<UserAction, UserActionResult>> { + val shadeUserActionResult = UserActionResult(Scenes.Shade, isIrreversible = true) + val qsSceneUserActionResult = UserActionResult(Scenes.QuickSettings, isIrreversible = true) return arrayOf( // Swiping down, not from the edge, always goes to shade. - Swipe.Down to Scenes.Shade, - swipeDown(pointerCount = 2) to Scenes.Shade, + Swipe.Down to shadeUserActionResult, + swipeDown(pointerCount = 2) to shadeUserActionResult, // Swiping down from the top edge. swipeDownFromTop(pointerCount = 1) to if (requireTwoPointersForTopEdgeForQs) { - Scenes.Shade + shadeUserActionResult } else { - Scenes.QuickSettings + qsSceneUserActionResult }, - swipeDownFromTop(pointerCount = 2) to Scenes.QuickSettings, + swipeDownFromTop(pointerCount = 2) to qsSceneUserActionResult, ) } /** Returns collection of [UserAction] to [UserActionResult] pairs for opening the split shade. */ fun splitShadeActions(): Array<Pair<UserAction, UserActionResult>> { - val splitShadeSceneKey = UserActionResult(Scenes.Shade, ToSplitShade) + val shadeUserActionResult = UserActionResult(Scenes.Shade, ToSplitShade, isIrreversible = true) return arrayOf( // Swiping down, not from the edge, always goes to shade. - Swipe.Down to splitShadeSceneKey, - swipeDown(pointerCount = 2) to splitShadeSceneKey, + Swipe.Down to shadeUserActionResult, + swipeDown(pointerCount = 2) to shadeUserActionResult, // Swiping down from the top edge goes to QS. - swipeDownFromTop(pointerCount = 1) to splitShadeSceneKey, - swipeDownFromTop(pointerCount = 2) to splitShadeSceneKey, + swipeDownFromTop(pointerCount = 1) to shadeUserActionResult, + swipeDownFromTop(pointerCount = 2) to shadeUserActionResult, ) } /** Returns collection of [UserAction] to [UserActionResult] pairs for opening the dual shade. */ fun dualShadeActions(): Array<Pair<UserAction, UserActionResult>> { + val notifShadeUserActionResult = + UserActionResult.ShowOverlay(Overlays.NotificationsShade, isIrreversible = true) + val qsShadeuserActionResult = + UserActionResult.ShowOverlay(Overlays.QuickSettingsShade, isIrreversible = true) return arrayOf( - Swipe.Down to Overlays.NotificationsShade, + Swipe.Down to notifShadeUserActionResult, Swipe(direction = SwipeDirection.Down, fromSource = SceneContainerEdge.TopRight) to - Overlays.QuickSettingsShade, + qsShadeuserActionResult, ) } |