[go: nahoru, domu]

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