diff options
2 files changed, 78 insertions, 13 deletions
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt index 3f6bce724b1b..f386cf5842e6 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt @@ -37,11 +37,7 @@ internal fun Modifier.swipeToScene( draggableHandler: DraggableHandlerImpl, swipeDetector: SwipeDetector, ): Modifier { - return if (draggableHandler.enabled()) { - this.then(SwipeToSceneElement(draggableHandler, swipeDetector)) - } else { - this - } + return then(SwipeToSceneElement(draggableHandler, swipeDetector, draggableHandler.enabled())) } private fun DraggableHandlerImpl.enabled(): Boolean { @@ -114,31 +110,59 @@ internal fun Content.findActionResultBestMatch(swipe: Swipe.Resolved): UserActio private data class SwipeToSceneElement( val draggableHandler: DraggableHandlerImpl, val swipeDetector: SwipeDetector, + val enabled: Boolean, ) : ModifierNodeElement<SwipeToSceneRootNode>() { override fun create(): SwipeToSceneRootNode = - SwipeToSceneRootNode(draggableHandler, swipeDetector) + SwipeToSceneRootNode(draggableHandler, swipeDetector, enabled) override fun update(node: SwipeToSceneRootNode) { - node.update(draggableHandler, swipeDetector) + node.update(draggableHandler, swipeDetector, enabled) } } private class SwipeToSceneRootNode( draggableHandler: DraggableHandlerImpl, swipeDetector: SwipeDetector, + enabled: Boolean, ) : DelegatingNode() { - private var delegateNode = delegate(SwipeToSceneNode(draggableHandler, swipeDetector)) + private var delegateNode = if (enabled) create(draggableHandler, swipeDetector) else null + + fun update( + draggableHandler: DraggableHandlerImpl, + swipeDetector: SwipeDetector, + enabled: Boolean, + ) { + // Disabled. + if (!enabled) { + delegateNode?.let { undelegate(it) } + delegateNode = null + return + } + + // Disabled => Enabled. + val nullableDelegate = delegateNode + if (nullableDelegate == null) { + delegateNode = create(draggableHandler, swipeDetector) + return + } - fun update(draggableHandler: DraggableHandlerImpl, swipeDetector: SwipeDetector) { - if (draggableHandler == delegateNode.draggableHandler) { + // Enabled => Enabled (update). + if (draggableHandler == nullableDelegate.draggableHandler) { // Simple update, just update the swipe detector directly and keep the node. - delegateNode.swipeDetector = swipeDetector + nullableDelegate.swipeDetector = swipeDetector } else { // The draggableHandler changed, force recreate the underlying SwipeToSceneNode. - undelegate(delegateNode) - delegateNode = delegate(SwipeToSceneNode(draggableHandler, swipeDetector)) + undelegate(nullableDelegate) + delegateNode = create(draggableHandler, swipeDetector) } } + + private fun create( + draggableHandler: DraggableHandlerImpl, + swipeDetector: SwipeDetector, + ): SwipeToSceneNode { + return delegate(SwipeToSceneNode(draggableHandler, swipeDetector)) + } } private class SwipeToSceneNode( 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 9135fdd15b3a..e80805a4e374 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 @@ -936,4 +936,45 @@ class SwipeToSceneTest { assertThat(state.transitionState).isIdle() assertThat(state.transitionState).hasCurrentScene(SceneC) } + + @Test + fun swipeToSceneNodeIsKeptWhenDisabled() { + var hasHorizontalActions by mutableStateOf(false) + val state = rule.runOnUiThread { MutableSceneTransitionLayoutState(SceneA) } + var touchSlop = 0f + rule.setContent { + touchSlop = LocalViewConfiguration.current.touchSlop + SceneTransitionLayout(state) { + scene( + SceneA, + userActions = + buildList { + add(Swipe.Down to SceneB) + + if (hasHorizontalActions) { + add(Swipe.Left to SceneC) + } + } + .toMap(), + ) { + Box(Modifier.fillMaxSize()) + } + scene(SceneB) { Box(Modifier.fillMaxSize()) } + } + } + + // Swipe down to start a transition to B. + rule.onRoot().performTouchInput { + down(middle) + moveBy(Offset(0f, touchSlop)) + } + + assertThat(state.transitionState).isSceneTransition() + + // Add new horizontal user actions. This should not stop the current transition, even if a + // new horizontal Modifier.swipeToScene() handler is introduced where the vertical one was. + hasHorizontalActions = true + rule.waitForIdle() + assertThat(state.transitionState).isSceneTransition() + } } |