diff options
author | 2024-01-25 17:19:54 +0100 | |
---|---|---|
committer | 2024-01-26 11:07:52 +0100 | |
commit | 977abc0da499f77addbaf42977974e8dff02dec3 (patch) | |
tree | 848b604640a62b07bbde4ea2d8a697eed64e6a4f | |
parent | ac9016aaf7f06e98d139fb1bfcd6d022cc41f2b4 (diff) |
Fix regression in SwipeToScene
This CL fixes a regression caused by ag/25992835: now that
MultiPointerDraggableNode.enabled is a lambda (that might never change),
we need to explicitly observe the result of this lambda to reset the
associated pointer input whenever enabled is changed.
Bug: 291071158
Test: atest SwipeToSceneTest
Flag: N/A
Change-Id: I476228f9f5e66d1b69de33b0879f62507ce31980
2 files changed, 80 insertions, 14 deletions
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt index 436e5bf6debd..7c4fd5a32596 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt @@ -40,8 +40,10 @@ import androidx.compose.ui.input.pointer.util.addPointerInputChange import androidx.compose.ui.node.CompositionLocalConsumerModifierNode import androidx.compose.ui.node.DelegatingNode import androidx.compose.ui.node.ModifierNodeElement +import androidx.compose.ui.node.ObserverModifierNode import androidx.compose.ui.node.PointerInputModifierNode import androidx.compose.ui.node.currentValueOf +import androidx.compose.ui.node.observeReads import androidx.compose.ui.platform.LocalViewConfiguration import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.Velocity @@ -118,10 +120,15 @@ internal class MultiPointerDraggableNode( var onDragStarted: (startedPosition: Offset, overSlop: Float, pointersDown: Int) -> Unit, var onDragDelta: (Float) -> Unit, var onDragStopped: (velocity: Float) -> Unit, -) : PointerInputModifierNode, DelegatingNode(), CompositionLocalConsumerModifierNode { +) : + PointerInputModifierNode, + DelegatingNode(), + CompositionLocalConsumerModifierNode, + ObserverModifierNode { private val pointerInputHandler: suspend PointerInputScope.() -> Unit = { pointerInput() } private val delegate = delegate(SuspendingPointerInputModifierNode(pointerInputHandler)) private val velocityTracker = VelocityTracker() + private var previousEnabled: Boolean = false var enabled: () -> Boolean = enabled set(value) { @@ -141,6 +148,21 @@ internal class MultiPointerDraggableNode( } } + override fun onAttach() { + previousEnabled = enabled() + onObservedReadsChanged() + } + + override fun onObservedReadsChanged() { + observeReads { + val newEnabled = enabled() + if (newEnabled != previousEnabled) { + delegate.resetPointerInputHandler() + } + previousEnabled = newEnabled + } + } + override fun onCancelPointerInput() = delegate.onCancelPointerInput() override fun onPointerEvent( diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt index 560f3b7c7d53..d81631aad13a 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt @@ -21,6 +21,9 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.size import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.platform.LocalViewConfiguration @@ -63,7 +66,10 @@ class SwipeToSceneTest { /** The content under test. */ @Composable - private fun TestContent(layoutState: SceneTransitionLayoutState) { + private fun TestContent( + layoutState: SceneTransitionLayoutState, + swipesEnabled: () -> Boolean = { true }, + ) { SceneTransitionLayout( state = layoutState, modifier = Modifier.size(LayoutWidth, LayoutHeight).testTag(TestElements.Foo.debugName), @@ -71,29 +77,35 @@ class SwipeToSceneTest { scene( TestScenes.SceneA, userActions = - mapOf( - Swipe.Left to TestScenes.SceneB, - Swipe.Down to TestScenes.SceneC, - Swipe.Up to TestScenes.SceneB, - ), + if (swipesEnabled()) + mapOf( + Swipe.Left to TestScenes.SceneB, + Swipe.Down to TestScenes.SceneC, + Swipe.Up to TestScenes.SceneB, + ) + else emptyMap(), ) { Box(Modifier.fillMaxSize()) } scene( TestScenes.SceneB, - userActions = mapOf(Swipe.Right to TestScenes.SceneA), + userActions = + if (swipesEnabled()) mapOf(Swipe.Right to TestScenes.SceneA) else emptyMap(), ) { Box(Modifier.fillMaxSize()) } scene( TestScenes.SceneC, userActions = - mapOf( - Swipe.Down to TestScenes.SceneA, - Swipe(SwipeDirection.Down, pointerCount = 2) to TestScenes.SceneB, - Swipe(SwipeDirection.Right, fromSource = Edge.Left) to TestScenes.SceneB, - Swipe(SwipeDirection.Down, fromSource = Edge.Top) to TestScenes.SceneB, - ), + if (swipesEnabled()) + mapOf( + Swipe.Down to TestScenes.SceneA, + Swipe(SwipeDirection.Down, pointerCount = 2) to TestScenes.SceneB, + Swipe(SwipeDirection.Right, fromSource = Edge.Left) to + TestScenes.SceneB, + Swipe(SwipeDirection.Down, fromSource = Edge.Top) to TestScenes.SceneB, + ) + else emptyMap(), ) { Box(Modifier.fillMaxSize()) } @@ -431,4 +443,36 @@ class SwipeToSceneTest { assertThat(transition).isNotNull() assertThat(transition?.toScene).isEqualTo(TestScenes.SceneB) } + + @Test + fun swipeEnabledLater() { + val layoutState = MutableSceneTransitionLayoutState(TestScenes.SceneA) + var swipesEnabled by mutableStateOf(false) + var touchSlop = 0f + rule.setContent { + touchSlop = LocalViewConfiguration.current.touchSlop + TestContent(layoutState, swipesEnabled = { swipesEnabled }) + } + + // Drag down from the middle. This should not do anything, because swipes are disabled. + rule.onRoot().performTouchInput { + down(middle) + moveBy(Offset(0f, touchSlop), delayMillis = 1_000) + } + assertThat(layoutState.currentTransition).isNull() + + // Release finger. + rule.onRoot().performTouchInput { up() } + + // Enable swipes. + swipesEnabled = true + rule.waitForIdle() + + // Drag down from the middle. Now it should start a transition. + rule.onRoot().performTouchInput { + down(middle) + moveBy(Offset(0f, touchSlop), delayMillis = 1_000) + } + assertThat(layoutState.currentTransition).isNotNull() + } } |