aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2024-03-12 16:22:44 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2024-03-12 16:22:44 +0000
commit1cbb6ee3e46f538bb2dc7ef6ce8e9c4b3cbc4dc3 (patch)
treebc6c8e1865e427423fd95e3f8aed3df1ff4b1c47
parent39960f640de8a0c8cf2717bb65fb5f7e7a3537e4 (diff)
parentbfb862660aac23b337cdbdffc2aeb4c3196fa53c (diff)
downloadsupport-snap-temp-L77900030002867095.tar.gz
Merge "Merge cherrypicks of ['android-review.googlesource.com/2989991'] into androidx-compose-release." into androidx-compose-releasesnap-temp-L77900030002867095
-rw-r--r--compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/selection/gestures/MultiTextMinTouchBoundsSelectionGesturesTest.kt55
-rw-r--r--compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionManager.kt14
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.