Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0d4d5181 authored by Jordan Demeulenaere's avatar Jordan Demeulenaere Committed by Android (Google) Code Review
Browse files

Merge "Revert "Don't install a pointer input when there are no user actions"" into main

parents 26d17f19 7bab68f2
Loading
Loading
Loading
Loading
+41 −1
Original line number Diff line number Diff line
@@ -42,8 +42,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
@@ -77,6 +79,7 @@ import kotlinx.coroutines.launch
@Stable
internal fun Modifier.multiPointerDraggable(
    orientation: Orientation,
    enabled: () -> Boolean,
    startDragImmediately: (startedPosition: Offset) -> Boolean,
    onDragStarted: (startedPosition: Offset, overSlop: Float, pointersDown: Int) -> DragController,
    onFirstPointerDown: () -> Unit = {},
@@ -86,6 +89,7 @@ internal fun Modifier.multiPointerDraggable(
    this.then(
        MultiPointerDraggableElement(
            orientation,
            enabled,
            startDragImmediately,
            onDragStarted,
            onFirstPointerDown,
@@ -96,6 +100,7 @@ internal fun Modifier.multiPointerDraggable(

private data class MultiPointerDraggableElement(
    private val orientation: Orientation,
    private val enabled: () -> Boolean,
    private val startDragImmediately: (startedPosition: Offset) -> Boolean,
    private val onDragStarted:
        (startedPosition: Offset, overSlop: Float, pointersDown: Int) -> DragController,
@@ -106,6 +111,7 @@ private data class MultiPointerDraggableElement(
    override fun create(): MultiPointerDraggableNode =
        MultiPointerDraggableNode(
            orientation = orientation,
            enabled = enabled,
            startDragImmediately = startDragImmediately,
            onDragStarted = onDragStarted,
            onFirstPointerDown = onFirstPointerDown,
@@ -115,6 +121,7 @@ private data class MultiPointerDraggableElement(

    override fun update(node: MultiPointerDraggableNode) {
        node.orientation = orientation
        node.enabled = enabled
        node.startDragImmediately = startDragImmediately
        node.onDragStarted = onDragStarted
        node.onFirstPointerDown = onFirstPointerDown
@@ -124,6 +131,7 @@ private data class MultiPointerDraggableElement(

internal class MultiPointerDraggableNode(
    orientation: Orientation,
    enabled: () -> Boolean,
    var startDragImmediately: (startedPosition: Offset) -> Boolean,
    var onDragStarted:
        (startedPosition: Offset, overSlop: Float, pointersDown: Int) -> DragController,
@@ -134,10 +142,21 @@ internal class MultiPointerDraggableNode(
    DelegatingNode(),
    PointerInputModifierNode,
    CompositionLocalConsumerModifierNode,
    ObserverModifierNode,
    SpaceVectorConverter {
    private val pointerTracker = delegate(SuspendingPointerInputModifierNode { pointerTracker() })
    private val pointerInput = delegate(SuspendingPointerInputModifierNode { pointerInput() })
    private val velocityTracker = VelocityTracker()
    private var previousEnabled: Boolean = false

    var enabled: () -> Boolean = enabled
        set(value) {
            // Reset the pointer input whenever enabled changed.
            if (value != field) {
                field = value
                pointerInput.resetPointerInputHandler()
            }
        }

    private var converter = SpaceVectorConverter(orientation)

@@ -159,6 +178,21 @@ internal class MultiPointerDraggableNode(
            }
        }

    override fun onAttach() {
        previousEnabled = enabled()
        onObservedReadsChanged()
    }

    override fun onObservedReadsChanged() {
        observeReads {
            val newEnabled = enabled()
            if (newEnabled != previousEnabled) {
                pointerInput.resetPointerInputHandler()
            }
            previousEnabled = newEnabled
        }
    }

    override fun onCancelPointerInput() {
        pointerTracker.onCancelPointerInput()
        pointerInput.onCancelPointerInput()
@@ -220,8 +254,10 @@ internal class MultiPointerDraggableNode(
                        velocityTracker.resetTracking()
                        velocityTracker.addPointerInputChange(firstPointerDown)
                        startedPosition = firstPointerDown.position
                        if (enabled()) {
                            onFirstPointerDown()
                        }
                    }

                    // Changes with at least one pointer
                    else -> {
@@ -259,6 +295,10 @@ internal class MultiPointerDraggableNode(
    }

    private suspend fun PointerInputScope.pointerInput() {
        if (!enabled()) {
            return
        }

        val currentContext = currentCoroutineContext()
        awaitPointerEventScope {
            while (currentContext.isActive) {
+19 −23
Original line number Diff line number Diff line
@@ -41,28 +41,7 @@ internal fun Modifier.swipeToScene(
    draggableHandler: DraggableHandlerImpl,
    swipeDetector: SwipeDetector,
): Modifier {
    return if (draggableHandler.enabled()) {
        this.then(SwipeToSceneElement(draggableHandler, swipeDetector))
    } else {
        this
    }
}

private fun DraggableHandlerImpl.enabled(): Boolean {
    return isDrivingTransition || contentForSwipes().shouldEnableSwipes(orientation)
}

private fun DraggableHandlerImpl.contentForSwipes(): Content {
    return layoutImpl.contentForUserActions()
}

/** Whether swipe should be enabled in the given [orientation]. */
private fun Content.shouldEnableSwipes(orientation: Orientation): Boolean {
    if (userActions.isEmpty()) {
        return false
    }

    return userActions.keys.any { it is Swipe.Resolved && it.direction.orientation == orientation }
    return this.then(SwipeToSceneElement(draggableHandler, swipeDetector))
}

private data class SwipeToSceneElement(
@@ -85,6 +64,7 @@ private class SwipeToSceneNode(
        delegate(
            MultiPointerDraggableNode(
                orientation = draggableHandler.orientation,
                enabled = ::enabled,
                startDragImmediately = ::startDragImmediately,
                onDragStarted = draggableHandler::onDragStarted,
                onFirstPointerDown = ::onFirstPointerDown,
@@ -144,6 +124,22 @@ private class SwipeToSceneNode(

    override fun onCancelPointerInput() = multiPointerDraggableNode.onCancelPointerInput()

    private fun enabled(): Boolean {
        return draggableHandler.isDrivingTransition ||
            contentForSwipes().shouldEnableSwipes(multiPointerDraggableNode.orientation)
    }

    private fun contentForSwipes(): Content {
        return draggableHandler.layoutImpl.contentForUserActions()
    }

    /** Whether swipe should be enabled in the given [orientation]. */
    private fun Content.shouldEnableSwipes(orientation: Orientation): Boolean {
        return userActions.keys.any {
            it is Swipe.Resolved && it.direction.orientation == orientation
        }
    }

    private fun startDragImmediately(startedPosition: Offset): Boolean {
        // Immediately start the drag if the user can't swipe in the other direction and the gesture
        // handler can intercept it.
@@ -156,7 +152,7 @@ private class SwipeToSceneNode(
                Orientation.Vertical -> Orientation.Horizontal
                Orientation.Horizontal -> Orientation.Vertical
            }
        return draggableHandler.contentForSwipes().shouldEnableSwipes(oppositeOrientation)
        return contentForSwipes().shouldEnableSwipes(oppositeOrientation)
    }
}

+21 −15
Original line number Diff line number Diff line
@@ -45,7 +45,6 @@ import androidx.compose.ui.test.performTouchInput
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Velocity
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.compose.modifiers.thenIf
import com.android.compose.nestedscroll.SuspendedValue
import com.google.common.truth.Truth.assertThat
import kotlin.properties.Delegates
@@ -95,9 +94,9 @@ class MultiPointerDraggableTest {
            Box(
                Modifier.size(with(LocalDensity.current) { Size(size, size).toDpSize() })
                    .nestedScrollDispatcher()
                    .thenIf(enabled) {
                        Modifier.multiPointerDraggable(
                    .multiPointerDraggable(
                        orientation = Orientation.Vertical,
                        enabled = { enabled },
                        startDragImmediately = { false },
                        onDragStarted = { _, _, _ ->
                            started = true
@@ -108,7 +107,6 @@ class MultiPointerDraggableTest {
                        },
                        dispatcher = defaultDispatcher,
                    )
                    }
            )
        }

@@ -166,6 +164,7 @@ class MultiPointerDraggableTest {
                    .nestedScrollDispatcher()
                    .multiPointerDraggable(
                        orientation = Orientation.Vertical,
                        enabled = { true },
                        // We want to start a drag gesture immediately
                        startDragImmediately = { true },
                        onDragStarted = { _, _, _ ->
@@ -239,6 +238,7 @@ class MultiPointerDraggableTest {
                    .nestedScrollDispatcher()
                    .multiPointerDraggable(
                        orientation = Orientation.Vertical,
                        enabled = { true },
                        startDragImmediately = { false },
                        onDragStarted = { _, _, _ ->
                            started = true
@@ -358,6 +358,7 @@ class MultiPointerDraggableTest {
                    .nestedScrollDispatcher()
                    .multiPointerDraggable(
                        orientation = Orientation.Vertical,
                        enabled = { true },
                        startDragImmediately = { false },
                        onDragStarted = { _, _, _ ->
                            started = true
@@ -463,6 +464,7 @@ class MultiPointerDraggableTest {
                    .nestedScrollDispatcher()
                    .multiPointerDraggable(
                        orientation = Orientation.Vertical,
                        enabled = { true },
                        startDragImmediately = { false },
                        onDragStarted = { _, _, _ ->
                            verticalStarted = true
@@ -475,6 +477,7 @@ class MultiPointerDraggableTest {
                    )
                    .multiPointerDraggable(
                        orientation = Orientation.Horizontal,
                        enabled = { true },
                        startDragImmediately = { false },
                        onDragStarted = { _, _, _ ->
                            horizontalStarted = true
@@ -567,6 +570,7 @@ class MultiPointerDraggableTest {
                    .nestedScrollDispatcher()
                    .multiPointerDraggable(
                        orientation = Orientation.Vertical,
                        enabled = { true },
                        startDragImmediately = { false },
                        swipeDetector =
                            object : SwipeDetector {
@@ -668,6 +672,7 @@ class MultiPointerDraggableTest {
                    .nestedScrollDispatcher()
                    .multiPointerDraggable(
                        orientation = Orientation.Vertical,
                        enabled = { true },
                        startDragImmediately = { false },
                        onDragStarted = { _, _, _ ->
                            SimpleDragController(
@@ -739,6 +744,7 @@ class MultiPointerDraggableTest {
                    .nestedScrollDispatcher()
                    .multiPointerDraggable(
                        orientation = Orientation.Vertical,
                        enabled = { true },
                        startDragImmediately = { false },
                        onDragStarted = { _, _, _ ->
                            SimpleDragController(
+0 −32
Original line number Diff line number Diff line
@@ -22,15 +22,11 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
@@ -40,11 +36,8 @@ import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.platform.LocalViewConfiguration
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.assertPositionInRootIsEqualTo
import androidx.compose.ui.test.assertTextEquals
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onRoot
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performTouchInput
import androidx.compose.ui.test.swipeWithVelocity
import androidx.compose.ui.unit.Density
@@ -851,29 +844,4 @@ class SwipeToSceneTest {
        assertThat(transition.progress).isEqualTo(1f)
        assertThat(availableOnPostScroll).isEqualTo(ovescrollPx)
    }

    @Test
    fun sceneWithoutSwipesDoesNotConsumeGestures() {
        val buttonTag = "button"

        rule.setContent {
            Box {
                var count by remember { mutableStateOf(0) }
                Button(onClick = { count++ }, Modifier.testTag(buttonTag).align(Alignment.Center)) {
                    Text("Count: $count")
                }

                SceneTransitionLayout(remember { MutableSceneTransitionLayoutState(SceneA) }) {
                    scene(SceneA) { Box(Modifier.fillMaxSize()) }
                }
            }
        }

        rule.onNodeWithTag(buttonTag).assertTextEquals("Count: 0")

        // Click on the root at its center, where the button is located. Clicks should go through
        // the STL and reach the button given that there is no swipes for the current scene.
        repeat(3) { rule.onRoot().performClick() }
        rule.onNodeWithTag(buttonTag).assertTextEquals("Count: 3")
    }
}