Fix cut-out gesture bugs.
Fixed issue where MotionEvents generated
for testing were not accurate due to
unexpected positioning of the Views they
were getting dispatched to.
Fixes: 161430531
Test: ./gradlew compose:ui:ui:connectedCheck
Change-Id: Idc6bc9164ef47ecd3d8d7c519bbd8282a2700aba
diff --git a/ui/ui-core/src/androidAndroidTest/kotlin/androidx/ui/core/gesture/LongPressDragGestureFilterTest.kt b/ui/ui-core/src/androidAndroidTest/kotlin/androidx/ui/core/gesture/LongPressDragGestureFilterTest.kt
index d4816af..89ea6d5 100644
--- a/ui/ui-core/src/androidAndroidTest/kotlin/androidx/ui/core/gesture/LongPressDragGestureFilterTest.kt
+++ b/ui/ui-core/src/androidAndroidTest/kotlin/androidx/ui/core/gesture/LongPressDragGestureFilterTest.kt
@@ -98,7 +98,8 @@
1,
0,
arrayOf(PointerProperties(0)),
- arrayOf(PointerCoords(50f, 50f))
+ arrayOf(PointerCoords(50f, 50f)),
+ view
)
val move = MotionEvent(
0,
@@ -106,7 +107,8 @@
1,
0,
arrayOf(PointerProperties(0)),
- arrayOf(PointerCoords(51f, 50f))
+ arrayOf(PointerCoords(51f, 50f)),
+ view
)
val up = MotionEvent(
0,
@@ -114,7 +116,8 @@
1,
0,
arrayOf(PointerProperties(0)),
- arrayOf(PointerCoords(51f, 50f))
+ arrayOf(PointerCoords(51f, 50f)),
+ view
)
activityTestRule.runOnUiThreadIR {
view.dispatchTouchEvent(down)
@@ -134,7 +137,8 @@
1,
0,
arrayOf(PointerProperties(0)),
- arrayOf(PointerCoords(50f, 50f))
+ arrayOf(PointerCoords(50f, 50f)),
+ view
)
val move = MotionEvent(
0,
@@ -142,7 +146,8 @@
1,
0,
arrayOf(PointerProperties(0)),
- arrayOf(PointerCoords(51f, 50f))
+ arrayOf(PointerCoords(51f, 50f)),
+ view
)
val cancel = MotionEvent(
0,
@@ -150,7 +155,8 @@
1,
0,
arrayOf(PointerProperties(0)),
- arrayOf(PointerCoords(51f, 50f))
+ arrayOf(PointerCoords(51f, 50f)),
+ view
)
activityTestRule.runOnUiThreadIR {
view.dispatchTouchEvent(down)
@@ -170,7 +176,8 @@
1,
0,
arrayOf(PointerProperties(0)),
- arrayOf(PointerCoords(50f, 50f))
+ arrayOf(PointerCoords(50f, 50f)),
+ view
)
waitForLongPress {
view.dispatchTouchEvent(down)
@@ -189,7 +196,8 @@
1,
0,
arrayOf(PointerProperties(0)),
- arrayOf(PointerCoords(50f, 50f))
+ arrayOf(PointerCoords(50f, 50f)),
+ view
)
waitForLongPress {
view.dispatchTouchEvent(down)
@@ -200,7 +208,8 @@
1,
0,
arrayOf(PointerProperties(0)),
- arrayOf(PointerCoords(51f, 50f))
+ arrayOf(PointerCoords(51f, 50f)),
+ view
)
view.dispatchTouchEvent(move)
@@ -222,7 +231,8 @@
1,
0,
arrayOf(PointerProperties(0)),
- arrayOf(PointerCoords(50f, 50f))
+ arrayOf(PointerCoords(50f, 50f)),
+ view
)
waitForLongPress {
view.dispatchTouchEvent(down)
@@ -233,7 +243,8 @@
1,
0,
arrayOf(PointerProperties(0)),
- arrayOf(PointerCoords(51f, 50f))
+ arrayOf(PointerCoords(51f, 50f)),
+ view
)
view.dispatchTouchEvent(up)
@@ -254,7 +265,8 @@
1,
0,
arrayOf(PointerProperties(0)),
- arrayOf(PointerCoords(50f, 50f))
+ arrayOf(PointerCoords(50f, 50f)),
+ view
)
waitForLongPress {
view.dispatchTouchEvent(down)
@@ -265,7 +277,8 @@
1,
0,
arrayOf(PointerProperties(0)),
- arrayOf(PointerCoords(51f, 50f))
+ arrayOf(PointerCoords(51f, 50f)),
+ view
)
view.dispatchTouchEvent(move)
val up = MotionEvent(
@@ -274,7 +287,8 @@
1,
0,
arrayOf(PointerProperties(0)),
- arrayOf(PointerCoords(51f, 50f))
+ arrayOf(PointerCoords(51f, 50f)),
+ view
)
view.dispatchTouchEvent(up)
@@ -297,7 +311,8 @@
1,
0,
arrayOf(PointerProperties(0)),
- arrayOf(PointerCoords(50f, 50f))
+ arrayOf(PointerCoords(50f, 50f)),
+ view
)
waitForLongPress {
view.dispatchTouchEvent(down)
@@ -308,7 +323,8 @@
1,
0,
arrayOf(PointerProperties(0)),
- arrayOf(PointerCoords(50f, 50f))
+ arrayOf(PointerCoords(50f, 50f)),
+ view
)
view.dispatchTouchEvent(cancel)
@@ -328,7 +344,8 @@
1,
0,
arrayOf(PointerProperties(0)),
- arrayOf(PointerCoords(50f, 50f))
+ arrayOf(PointerCoords(50f, 50f)),
+ view
)
waitForLongPress {
view.dispatchTouchEvent(down)
@@ -339,7 +356,8 @@
1,
0,
arrayOf(PointerProperties(0)),
- arrayOf(PointerCoords(51f, 50f))
+ arrayOf(PointerCoords(51f, 50f)),
+ view
)
view.dispatchTouchEvent(move)
val cancel = MotionEvent(
@@ -348,7 +366,8 @@
1,
0,
arrayOf(PointerProperties(0)),
- arrayOf(PointerCoords(51f, 50f))
+ arrayOf(PointerCoords(51f, 50f)),
+ view
)
view.dispatchTouchEvent(cancel)
diff --git a/ui/ui-core/src/androidAndroidTest/kotlin/androidx/ui/core/gesture/ScaleGestureFilterTest.kt b/ui/ui-core/src/androidAndroidTest/kotlin/androidx/ui/core/gesture/ScaleGestureFilterTest.kt
index a879adb..a45de17 100644
--- a/ui/ui-core/src/androidAndroidTest/kotlin/androidx/ui/core/gesture/ScaleGestureFilterTest.kt
+++ b/ui/ui-core/src/androidAndroidTest/kotlin/androidx/ui/core/gesture/ScaleGestureFilterTest.kt
@@ -101,7 +101,8 @@
1,
0,
arrayOf(PointerProperties(0)),
- arrayOf(PointerCoords(touchSlop * 1, 50f))
+ arrayOf(PointerCoords(touchSlop * 1, 50f)),
+ view
)
val down2 = MotionEvent(
10,
@@ -112,7 +113,8 @@
arrayOf(
PointerCoords(touchSlop * 1, 50f),
PointerCoords(touchSlop * 3, 50f)
- )
+ ),
+ view
)
val move = MotionEvent(
20,
@@ -126,7 +128,8 @@
arrayOf(
PointerCoords(touchSlop * 0 + TinyNum, 50f),
PointerCoords(touchSlop * 4 - TinyNum, 50f)
- )
+ ),
+ view
)
val up = MotionEvent(
30,
@@ -140,7 +143,8 @@
arrayOf(
PointerCoords(touchSlop * 0 + TinyNum, 50f),
PointerCoords(touchSlop * 4 - TinyNum, 50f)
- )
+ ),
+ view
)
val up2 = MotionEvent(
40,
@@ -152,7 +156,8 @@
),
arrayOf(
PointerCoords(touchSlop * 4 - TinyNum, 50f)
- )
+ ),
+ view
)
activityTestRule.runOnUiThreadIR {
@@ -177,7 +182,8 @@
1,
0,
arrayOf(PointerProperties(0)),
- arrayOf(PointerCoords(touchSlop * 1, 50f))
+ arrayOf(PointerCoords(touchSlop * 1, 50f)),
+ view
)
val down2 = MotionEvent(
10,
@@ -188,7 +194,8 @@
arrayOf(
PointerCoords(touchSlop * 1, 50f),
PointerCoords(touchSlop * 3, 50f)
- )
+ ),
+ view
)
val move = MotionEvent(
20,
@@ -202,7 +209,8 @@
arrayOf(
PointerCoords(touchSlop * 0 - TinyNum, 50f),
PointerCoords(touchSlop * 4 + TinyNum, 50f)
- )
+ ),
+ view
)
val up = MotionEvent(
30,
@@ -216,7 +224,8 @@
arrayOf(
PointerCoords(touchSlop * 0 - TinyNum, 50f),
PointerCoords(touchSlop * 4 + TinyNum, 50f)
- )
+ ),
+ view
)
val up2 = MotionEvent(
40,
@@ -228,7 +237,8 @@
),
arrayOf(
PointerCoords(touchSlop * 4 + TinyNum, 50f)
- )
+ ),
+ view
)
activityTestRule.runOnUiThreadIR {
@@ -258,7 +268,8 @@
1,
0,
arrayOf(PointerProperties(0)),
- arrayOf(PointerCoords(touchSlop * 1, 50f))
+ arrayOf(PointerCoords(touchSlop * 1, 50f)),
+ view
)
val down2 = MotionEvent(
10,
@@ -269,7 +280,8 @@
arrayOf(
PointerCoords(touchSlop * 1, 50f),
PointerCoords(touchSlop * 3, 50f)
- )
+ ),
+ view
)
val move = MotionEvent(
20,
@@ -283,7 +295,8 @@
arrayOf(
PointerCoords(touchSlop * 0 - TinyNum, 50f),
PointerCoords(touchSlop * 4 + TinyNum, 50f)
- )
+ ),
+ view
)
val cancel = MotionEvent(
30,
@@ -297,7 +310,8 @@
arrayOf(
PointerCoords(touchSlop * 0 - TinyNum, 50f),
PointerCoords(touchSlop * 4 + TinyNum, 50f)
- )
+ ),
+ view
)
activityTestRule.runOnUiThreadIR {
@@ -326,7 +340,8 @@
1,
0,
arrayOf(PointerProperties(0)),
- arrayOf(PointerCoords(touchSlop * 1, 50f))
+ arrayOf(PointerCoords(touchSlop * 1, 50f)),
+ view
)
val down2 = MotionEvent(
10,
@@ -337,7 +352,8 @@
arrayOf(
PointerCoords(touchSlop * 1, 50f),
PointerCoords(touchSlop * 3, 50f)
- )
+ ),
+ view
)
val move = MotionEvent(
20,
@@ -351,7 +367,8 @@
arrayOf(
PointerCoords(touchSlop * -1, 50f),
PointerCoords(touchSlop * 5, 50f)
- )
+ ),
+ view
)
activityTestRule.runOnUiThreadIR {
@@ -374,7 +391,8 @@
1,
0,
arrayOf(PointerProperties(0)),
- arrayOf(PointerCoords(touchSlop * 1, 50f))
+ arrayOf(PointerCoords(touchSlop * 1, 50f)),
+ view
)
val down2 = MotionEvent(
10,
@@ -388,7 +406,8 @@
arrayOf(
PointerCoords(touchSlop * 1, 50f),
PointerCoords(touchSlop * 6, 50f)
- )
+ ),
+ view
)
val move = MotionEvent(
20,
@@ -402,7 +421,8 @@
arrayOf(
PointerCoords(touchSlop * 3, 50f),
PointerCoords(touchSlop * 4, 50f)
- )
+ ),
+ view
)
activityTestRule.runOnUiThreadIR {
diff --git a/ui/ui-core/src/androidAndroidTest/kotlin/androidx/ui/core/gesture/TouchSlopDragGestureFilterTest.kt b/ui/ui-core/src/androidAndroidTest/kotlin/androidx/ui/core/gesture/TouchSlopDragGestureFilterTest.kt
index 74f79cb..83919d5 100644
--- a/ui/ui-core/src/androidAndroidTest/kotlin/androidx/ui/core/gesture/TouchSlopDragGestureFilterTest.kt
+++ b/ui/ui-core/src/androidAndroidTest/kotlin/androidx/ui/core/gesture/TouchSlopDragGestureFilterTest.kt
@@ -66,7 +66,8 @@
1,
0,
arrayOf(PointerProperties(0)),
- arrayOf(PointerCoords(50f, 50f))
+ arrayOf(PointerCoords(50f, 50f)),
+ view
)
val move = MotionEvent(
20,
@@ -74,7 +75,8 @@
1,
0,
arrayOf(PointerProperties(0)),
- arrayOf(PointerCoords(50f + touchSlop - TinyNum, 50f))
+ arrayOf(PointerCoords(50f + touchSlop - TinyNum, 50f)),
+ view
)
val up = MotionEvent(
30,
@@ -82,7 +84,8 @@
1,
0,
arrayOf(PointerProperties(0)),
- arrayOf(PointerCoords(50f + touchSlop - TinyNum, 50f))
+ arrayOf(PointerCoords(50f + touchSlop - TinyNum, 50f)),
+ view
)
activityTestRule.runOnUiThreadIR {
@@ -105,7 +108,8 @@
1,
0,
arrayOf(PointerProperties(0)),
- arrayOf(PointerCoords(50f, 50f))
+ arrayOf(PointerCoords(50f, 50f)),
+ view
)
val move = MotionEvent(
20,
@@ -113,7 +117,8 @@
1,
0,
arrayOf(PointerProperties(0)),
- arrayOf(PointerCoords(50f + touchSlop + TinyNum, 50f))
+ arrayOf(PointerCoords(50f + touchSlop + TinyNum, 50f)),
+ view
)
val up = MotionEvent(
30,
@@ -121,7 +126,8 @@
1,
0,
arrayOf(PointerProperties(0)),
- arrayOf(PointerCoords(50f + touchSlop + TinyNum, 50f))
+ arrayOf(PointerCoords(50f + touchSlop + TinyNum, 50f)),
+ view
)
activityTestRule.runOnUiThreadIR {
@@ -152,7 +158,8 @@
1,
0,
arrayOf(PointerProperties(0)),
- arrayOf(PointerCoords(50f, 50f))
+ arrayOf(PointerCoords(50f, 50f)),
+ view
)
val move = MotionEvent(
20,
@@ -160,7 +167,8 @@
1,
0,
arrayOf(PointerProperties(0)),
- arrayOf(PointerCoords(50f + touchSlop + TinyNum, 50f))
+ arrayOf(PointerCoords(50f + touchSlop + TinyNum, 50f)),
+ view
)
val cancel = MotionEvent(
30,
@@ -168,7 +176,8 @@
1,
0,
arrayOf(PointerProperties(0)),
- arrayOf(PointerCoords(50f + touchSlop, 50f))
+ arrayOf(PointerCoords(50f + touchSlop, 50f)),
+ view
)
activityTestRule.runOnUiThreadIR {
@@ -197,7 +206,8 @@
1,
0,
arrayOf(PointerProperties(0)),
- arrayOf(PointerCoords(50f, 50f))
+ arrayOf(PointerCoords(50f, 50f)),
+ view
)
activityTestRule.runOnUiThreadIR {
@@ -223,7 +233,8 @@
1,
0,
arrayOf(PointerProperties(0)),
- arrayOf(PointerCoords(50f, 50f))
+ arrayOf(PointerCoords(50f, 50f)),
+ view
)
val move = MotionEvent(
20,
@@ -231,7 +242,8 @@
1,
0,
arrayOf(PointerProperties(0)),
- arrayOf(PointerCoords(50f + movement, 50f))
+ arrayOf(PointerCoords(50f + movement, 50f)),
+ view
)
activityTestRule.runOnUiThreadIR {
diff --git a/ui/ui-core/src/androidAndroidTest/kotlin/androidx/ui/core/gesture/utils.kt b/ui/ui-core/src/androidAndroidTest/kotlin/androidx/ui/core/gesture/utils.kt
index d50e43b..c7fea86 100644
--- a/ui/ui-core/src/androidAndroidTest/kotlin/androidx/ui/core/gesture/utils.kt
+++ b/ui/ui-core/src/androidAndroidTest/kotlin/androidx/ui/core/gesture/utils.kt
@@ -17,6 +17,7 @@
package androidx.ui.core.gesture
import android.view.MotionEvent
+import android.view.View
// We only need this because IR compiler doesn't like converting lambdas to Runnables
@Suppress("DEPRECATION")
@@ -29,29 +30,57 @@
runOnUiThread(runnable)
}
+/**
+ * Creates a simple [MotionEvent].
+ *
+ * @param dispatchTarget The [View] that the [MotionEvent] is going to be dispatched to. This
+ * guarantees that the MotionEvent is created correctly for both Compose (which relies on raw
+ * coordinates being correct) and Android (which requires that local coordinates are correct).
+ */
internal fun MotionEvent(
eventTime: Int,
action: Int,
numPointers: Int,
actionIndex: Int,
pointerProperties: Array<MotionEvent.PointerProperties>,
- pointerCoords: Array<MotionEvent.PointerCoords>
-) = MotionEvent.obtain(
- 0,
- eventTime.toLong(),
- action + (actionIndex shl MotionEvent.ACTION_POINTER_INDEX_SHIFT),
- numPointers,
- pointerProperties,
- pointerCoords,
- 0,
- 0,
- 0f,
- 0f,
- 0,
- 0,
- 0,
- 0
-)
+ pointerCoords: Array<MotionEvent.PointerCoords>,
+ dispatchTarget: View
+): MotionEvent {
+
+ val locationOnScreen = IntArray(2) { 0 }
+ dispatchTarget.getLocationOnScreen(locationOnScreen)
+
+ pointerCoords.forEach {
+ it.x += locationOnScreen[0]
+ it.y += locationOnScreen[1]
+ }
+
+ val motionEvent = MotionEvent.obtain(
+ 0,
+ eventTime.toLong(),
+ action + (actionIndex shl MotionEvent.ACTION_POINTER_INDEX_SHIFT),
+ numPointers,
+ pointerProperties,
+ pointerCoords,
+ 0,
+ 0,
+ 0f,
+ 0f,
+ 0,
+ 0,
+ 0,
+ 0
+ ).apply {
+ offsetLocation(-locationOnScreen[0].toFloat(), -locationOnScreen[1].toFloat())
+ }
+
+ pointerCoords.forEach {
+ it.x -= locationOnScreen[0]
+ it.y -= locationOnScreen[1]
+ }
+
+ return motionEvent
+}
@Suppress("RemoveRedundantQualifierName")
internal fun PointerProperties(id: Int) =
diff --git a/ui/ui-core/src/androidAndroidTest/kotlin/androidx/ui/core/test/ClipPointerInputTest.kt b/ui/ui-core/src/androidAndroidTest/kotlin/androidx/ui/core/test/ClipPointerInputTest.kt
index f897944..f2f687d 100644
--- a/ui/ui-core/src/androidAndroidTest/kotlin/androidx/ui/core/test/ClipPointerInputTest.kt
+++ b/ui/ui-core/src/androidAndroidTest/kotlin/androidx/ui/core/test/ClipPointerInputTest.kt
@@ -79,13 +79,13 @@
* 4 . t . . t .
* ......... .........
*
- * 4 LayoutNodes with PointerInputModifiers that are positioned by their parent LayoutNode
- * and where pointer input is clipped by a modifier on the parent. 4 touches touch just inside
- * the parent LayoutNode and inside the child LayoutNodes. 8 touches touch just outside the
+ * 4 LayoutNodes with PointerInputModifiers that are positioned by offset modifiers and where
+ * pointer input is clipped by a modifier on the parent. 4 touches touch just inside the
+ * parent LayoutNode and inside the child LayoutNodes. 8 touches touch just outside the
* parent LayoutNode but inside the child LayoutNodes.
*
- * Because layout node bounds are not used to clip pointer input hit testing, all pointers
- * should hit.
+ * Because clipToBounds is being used on the parent LayoutNode, only the 4 touches inside the
+ * parent LayoutNode should hit.
*/
@Test
fun clipToBounds_childrenOffsetViaLayout_onlyCorrectPointersHit() {
@@ -169,7 +169,8 @@
1,
0,
arrayOf(PointerProperties(0)),
- arrayOf(PointerCoords(value.x, value.y))
+ arrayOf(PointerCoords(value.x, value.y)),
+ view
)
)
}
@@ -184,6 +185,9 @@
// Assert
assertThat(loggingPim1.log).isEqualTo(listOf(Offset(1f, 1f)))
+ assertThat(loggingPim2.log).isEqualTo(listOf(Offset(0f, 1f)))
+ assertThat(loggingPim3.log).isEqualTo(listOf(Offset(1f, 0f)))
+ assertThat(loggingPim4.log).isEqualTo(listOf(Offset(0f, 0f)))
}
/**
@@ -207,11 +211,11 @@
* parent LayoutNode and inside the child LayoutNodes. 8 touches touch just outside the
* parent LayoutNode but inside the child LayoutNodes.
*
- * Because layout node bounds are not used to clip pointer input hit testing, all pointers
- * should hit.
+ * Because clipToBounds is being used on the parent LayoutNode, only the 4 touches inside the
+ * parent LayoutNode should hit.
*/
@Test
- fun childrenOffsetViaModifier_onlyCorrectPointersHit() {
+ fun clipToBounds_childrenOffsetViaModifier_onlyCorrectPointersHit() {
val setupLatch = CountDownLatch(2)
@@ -292,7 +296,8 @@
1,
0,
arrayOf(PointerProperties(0)),
- arrayOf(PointerCoords(value.x, value.y))
+ arrayOf(PointerCoords(value.x, value.y)),
+ view
)
)
}
@@ -307,6 +312,9 @@
// Assert
assertThat(loggingPim1.log).isEqualTo(listOf(Offset(1f, 1f)))
+ assertThat(loggingPim2.log).isEqualTo(listOf(Offset(0f, 1f)))
+ assertThat(loggingPim3.log).isEqualTo(listOf(Offset(1f, 0f)))
+ assertThat(loggingPim4.log).isEqualTo(listOf(Offset(0f, 0f)))
}
@Composable