diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2024-03-12 16:22:08 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2024-03-12 16:22:08 +0000 |
commit | bfb862660aac23b337cdbdffc2aeb4c3196fa53c (patch) | |
tree | bc6c8e1865e427423fd95e3f8aed3df1ff4b1c47 | |
parent | 39960f640de8a0c8cf2717bb65fb5f7e7a3537e4 (diff) | |
parent | d455802eb4b319ec0fe30a4a9f74fdd075b12f49 (diff) | |
download | support-bfb862660aac23b337cdbdffc2aeb4c3196fa53c.tar.gz |
Merge cherrypicks of ['android-review.googlesource.com/2989991'] into androidx-compose-release.
Change-Id: I7303995b052647e84491a08c28602fb2b3f56232
2 files changed, 53 insertions, 16 deletions
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/selection/gestures/MultiTextMinTouchBoundsSelectionGesturesTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/selection/gestures/MultiTextMinTouchBoundsSelectionGesturesTest.kt index 4fecf01bc6a..73d02767aa0 100644 --- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/selection/gestures/MultiTextMinTouchBoundsSelectionGesturesTest.kt +++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/selection/gestures/MultiTextMinTouchBoundsSelectionGesturesTest.kt @@ -36,6 +36,7 @@ import androidx.compose.foundation.text.selection.gestures.MultiTextMinTouchBoun import androidx.compose.foundation.text.selection.gestures.MultiTextMinTouchBoundsSelectionGesturesTest.TestVertical.OVERLAP_BELONGS_TO_FIRST import androidx.compose.foundation.text.selection.gestures.MultiTextMinTouchBoundsSelectionGesturesTest.TestVertical.OVERLAP_BELONGS_TO_SECOND import androidx.compose.foundation.text.selection.gestures.MultiTextMinTouchBoundsSelectionGesturesTest.TestVertical.OVERLAP_EQUIDISTANT +import androidx.compose.foundation.text.selection.gestures.util.longPress import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.mutableStateOf @@ -87,22 +88,30 @@ internal class MultiTextMinTouchBoundsSelectionGesturesTest( */ private val touchTargetDpLen = dpLen + 12.dp * 2 - enum class TestHorizontal(val x: Float) { - LEFT(-6f), - CENTER(10f), - RIGHT(26f) + enum class TestHorizontal( + val x: Float, + /** The x-value we would coerce to in order to get the x coordinate onto a box. */ + val coercedX: Float, + ) { + LEFT(x = -6f, coercedX = 1f), + CENTER(x = 10f, coercedX = 10f), + RIGHT(x = 26f, coercedX = 19f) } - enum class TestVertical(val y: Float) { - ABOVE(-6f), - ON_FIRST(10f), - NO_OVERLAP_BELONGS_TO_FIRST(25f), - OVERLAP_BELONGS_TO_FIRST(29f), - OVERLAP_EQUIDISTANT(30f), - OVERLAP_BELONGS_TO_SECOND(31f), - NO_OVERLAP_BELONGS_TO_SECOND(35f), - ON_SECOND(50f), - BELOW(66f), + enum class TestVertical( + val y: Float, + /** The y-value we would coerce to in order to get the y coordinate onto a box. */ + val coercedY: Float, + ) { + ABOVE(y = -6f, coercedY = 1f), + ON_FIRST(y = 10f, coercedY = 10f), + NO_OVERLAP_BELONGS_TO_FIRST(y = 25f, coercedY = 19f), + OVERLAP_BELONGS_TO_FIRST(y = 29f, coercedY = 19f), + OVERLAP_EQUIDISTANT(y = 30f, coercedY = 19f), + OVERLAP_BELONGS_TO_SECOND(y = 31f, coercedY = 41f), + NO_OVERLAP_BELONGS_TO_SECOND(y = 35f, coercedY = 41f), + ON_SECOND(y = 50f, coercedY = 50f), + BELOW(y = 66f, coercedY = 59f); } enum class ExpectedText(val selectableId: Long?) { @@ -170,8 +179,24 @@ internal class MultiTextMinTouchBoundsSelectionGesturesTest( } @Test - fun minTouchTargetSelectionGestureTest() { + fun minTouchTargetSelectionGestureTest() = runTest { performTouchGesture { longClick(Offset(horizontal.x, vertical.y)) } + } + + // Regression test for b/325307463 + @Test + fun dragIntoMinTouchTargetSelectionGestureTest() = runTest { + performTouchGesture { + longPress(Offset(horizontal.coercedX, vertical.coercedY)) + // The crash involved a quick drag from on the text to off the text + // causing a race of some state not being set before the drag is executed, + // so we want to force the moveTo immediately after the long press finishes. + moveTo(Offset(horizontal.x, vertical.y), delayMillis = 0L) + } + } + + fun runTest(block: () -> Unit) { + block() val expectedSelectableId = expectedText.selectableId if (expectedSelectableId == null) { diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionManager.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionManager.kt index ec504b57bdf..b293c9c14bd 100644 --- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionManager.kt +++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionManager.kt @@ -98,7 +98,19 @@ internal class SelectionManager(private val selectionRegistrar: SelectionRegistr * change. The expectation is that this callback will end up causing `setSelection` to get * called. This is what makes this a "controlled component". */ - var onSelectionChange: (Selection?) -> Unit = {} + var onSelectionChange: (Selection?) -> Unit = { selection = it } + set(newOnSelectionChange) { + // Wrap the given lambda with one that sets the selection immediately. + // The onSelectionChange loop requires a composition to happen for the selection + // to be updated, so we want to shorten that loop for gesture use cases where + // multiple selection changing events can be acted on within a single composition + // loop. Previous selection is used as part of that loop so keeping it up to date + // is important. + field = { newSelection -> + selection = newSelection + newOnSelectionChange(newSelection) + } + } /** * [HapticFeedback] handle to perform haptic feedback. |