Implement cancel for the remainder of
the gesture detectors.
Bug: 146581189
Test: ./gradlew ui:ui-framework:test
Test: ./gradlew ui:ui-core:test
Test: ./gradlew ui:ui-framework:connectedCheck
Test: ./gradlew ui:ui-core:connectedCheck
Change-Id: I349f477ad8112046e916e23d00f8ea2ad850ed11
diff --git a/ui/ui-framework/src/androidTest/java/androidx/ui/core/gesture/LongPressDragGestureDetectorTest.kt b/ui/ui-framework/src/androidTest/java/androidx/ui/core/gesture/LongPressDragGestureDetectorTest.kt
index 8335953..cc272a8 100644
--- a/ui/ui-framework/src/androidTest/java/androidx/ui/core/gesture/LongPressDragGestureDetectorTest.kt
+++ b/ui/ui-framework/src/androidTest/java/androidx/ui/core/gesture/LongPressDragGestureDetectorTest.kt
@@ -18,11 +18,17 @@
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
+import androidx.test.filters.LargeTest
import androidx.test.rule.ActivityTestRule
+import androidx.ui.core.Layout
+import androidx.ui.core.PxPosition
import androidx.ui.core.ipx
import androidx.ui.core.setContent
import androidx.ui.framework.test.TestActivity
import com.nhaarman.mockitokotlin2.any
+import com.nhaarman.mockitokotlin2.inOrder
+import com.nhaarman.mockitokotlin2.spy
+import com.nhaarman.mockitokotlin2.times
import com.nhaarman.mockitokotlin2.verify
import com.nhaarman.mockitokotlin2.verifyNoMoreInteractions
import org.junit.Assert.assertTrue
@@ -33,12 +39,10 @@
import org.junit.runners.JUnit4
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
-import androidx.ui.core.PxPosition
-import com.nhaarman.mockitokotlin2.reset
-import com.nhaarman.mockitokotlin2.spy
-import com.nhaarman.mockitokotlin2.times
-import androidx.test.filters.LargeTest
-import androidx.ui.core.Layout
+
+// TODO(shepshapard): Figure out how to test composite gesture detectors better. There should be
+// more tests, but hopefully they can also be easier to write then what is currently available.
+// Should possibly wait till we have host side testing that does not require the Android runtime.
@LargeTest
@RunWith(JUnit4::class)
@@ -78,8 +82,6 @@
assertTrue(setupLatch.await(1000, TimeUnit.SECONDS))
}
- // Tests that verify conditions under which nothing will be called.
-
@Test
fun ui_downMoveUpBeforeLongPressTimeout_noCallbacksCalled() {
@@ -116,10 +118,44 @@
verifyNoMoreInteractions(longPressDragObserver)
}
- // Tests that verify conditions under which onLongPress will only be called.
+ @Test
+ fun ui_downMoveCancelBeforeLongPressTimeout_noCallbacksCalled() {
+
+ val down = MotionEvent(
+ 0,
+ MotionEvent.ACTION_DOWN,
+ 1,
+ 0,
+ arrayOf(PointerProperties(0)),
+ arrayOf(PointerCoords(50f, 50f))
+ )
+ val move = MotionEvent(
+ 0,
+ MotionEvent.ACTION_MOVE,
+ 1,
+ 0,
+ arrayOf(PointerProperties(0)),
+ arrayOf(PointerCoords(51f, 50f))
+ )
+ val cancel = MotionEvent(
+ 0,
+ MotionEvent.ACTION_CANCEL,
+ 1,
+ 0,
+ arrayOf(PointerProperties(0)),
+ arrayOf(PointerCoords(51f, 50f))
+ )
+ activityTestRule.runOnUiThreadIR {
+ view.dispatchTouchEvent(down)
+ view.dispatchTouchEvent(move)
+ view.dispatchTouchEvent(cancel)
+ }
+
+ verifyNoMoreInteractions(longPressDragObserver)
+ }
@Test
- fun ui_downWaitForLongPress_onLongPressCalled() {
+ fun ui_downWaitForLongPress_onlyOnLongPressCalled() {
val down = MotionEvent(
0,
@@ -137,49 +173,9 @@
verifyNoMoreInteractions(longPressDragObserver)
}
- // Tests that verify conditions under which onDragStart and onDrag will be called.
-
@Test
- fun ui_downWaitForLongPressMove_onDragStartAndOnDragCalled() {
+ fun ui_downWaitForLongPressMove_callbacksCorrect() {
- // Arrange.
- val down = MotionEvent(
- 0,
- MotionEvent.ACTION_DOWN,
- 1,
- 0,
- arrayOf(PointerProperties(0)),
- arrayOf(PointerCoords(50f, 50f))
- )
- waitForLongPress {
- view.dispatchTouchEvent(down)
- }
- reset(longPressDragObserver)
-
- // Act.
- val move = MotionEvent(
- 0,
- MotionEvent.ACTION_MOVE,
- 1,
- 0,
- arrayOf(PointerProperties(0)),
- arrayOf(PointerCoords(51f, 50f))
- )
- view.dispatchTouchEvent(move)
-
- // Assert.
- verify(longPressDragObserver).onDragStart()
- // Twice because DragGestureDetector dispatches onDrag during 2 passes.
- verify(longPressDragObserver, times(2)).onDrag(any())
- verifyNoMoreInteractions(longPressDragObserver)
- }
-
- // Tests that verify conditions under which onStop will be called.
-
- @Test
- fun ui_downWaitForLongPressMoveUp_onDragStopCalled() {
-
- // Arrange.
val down = MotionEvent(
0,
MotionEvent.ACTION_DOWN,
@@ -200,28 +196,19 @@
arrayOf(PointerCoords(51f, 50f))
)
view.dispatchTouchEvent(move)
- reset(longPressDragObserver)
- // Act.
- val up = MotionEvent(
- 0,
- MotionEvent.ACTION_UP,
- 1,
- 0,
- arrayOf(PointerProperties(0)),
- arrayOf(PointerCoords(51f, 50f))
- )
- view.dispatchTouchEvent(up)
-
- // Assert.
- verify(longPressDragObserver).onStop(any())
+ inOrder(longPressDragObserver) {
+ verify(longPressDragObserver).onLongPress(any())
+ verify(longPressDragObserver).onDragStart()
+ // Twice because DragGestureDetector dispatches onDrag during 2 passes.
+ verify(longPressDragObserver, times(2)).onDrag(any())
+ }
verifyNoMoreInteractions(longPressDragObserver)
}
@Test
- fun ui_downWaitForLongPressUp_onDragStopCalled() {
+ fun ui_downWaitForLongPressUp_callbacksCorrect() {
- // Arrange.
val down = MotionEvent(
0,
MotionEvent.ACTION_DOWN,
@@ -233,9 +220,6 @@
waitForLongPress {
view.dispatchTouchEvent(down)
}
- reset(longPressDragObserver)
-
- // Act.
val up = MotionEvent(
0,
MotionEvent.ACTION_UP,
@@ -247,7 +231,127 @@
view.dispatchTouchEvent(up)
// Assert.
- verify(longPressDragObserver).onStop(any())
+ inOrder(longPressDragObserver) {
+ verify(longPressDragObserver).onLongPress(any())
+ verify(longPressDragObserver).onStop(any())
+ }
+ verifyNoMoreInteractions(longPressDragObserver)
+ }
+
+ @Test
+ fun ui_downWaitForLongPressMoveUp_callbacksCorrect() {
+
+ val down = MotionEvent(
+ 0,
+ MotionEvent.ACTION_DOWN,
+ 1,
+ 0,
+ arrayOf(PointerProperties(0)),
+ arrayOf(PointerCoords(50f, 50f))
+ )
+ waitForLongPress {
+ view.dispatchTouchEvent(down)
+ }
+ val move = MotionEvent(
+ 0,
+ MotionEvent.ACTION_MOVE,
+ 1,
+ 0,
+ arrayOf(PointerProperties(0)),
+ arrayOf(PointerCoords(51f, 50f))
+ )
+ view.dispatchTouchEvent(move)
+ val up = MotionEvent(
+ 0,
+ MotionEvent.ACTION_UP,
+ 1,
+ 0,
+ arrayOf(PointerProperties(0)),
+ arrayOf(PointerCoords(51f, 50f))
+ )
+ view.dispatchTouchEvent(up)
+
+ inOrder(longPressDragObserver) {
+ verify(longPressDragObserver).onLongPress(any())
+ verify(longPressDragObserver).onDragStart()
+ // Twice because DragGestureDetector dispatches onDrag during 2 passes.
+ verify(longPressDragObserver, times(2)).onDrag(any())
+ verify(longPressDragObserver).onStop(any())
+ }
+ verifyNoMoreInteractions(longPressDragObserver)
+ }
+
+ @Test
+ fun ui_downWaitForLongPressCancel_callbacksCorrect() {
+
+ val down = MotionEvent(
+ 0,
+ MotionEvent.ACTION_DOWN,
+ 1,
+ 0,
+ arrayOf(PointerProperties(0)),
+ arrayOf(PointerCoords(50f, 50f))
+ )
+ waitForLongPress {
+ view.dispatchTouchEvent(down)
+ }
+ val cancel = MotionEvent(
+ 0,
+ MotionEvent.ACTION_CANCEL,
+ 1,
+ 0,
+ arrayOf(PointerProperties(0)),
+ arrayOf(PointerCoords(50f, 50f))
+ )
+ view.dispatchTouchEvent(cancel)
+
+ inOrder(longPressDragObserver) {
+ verify(longPressDragObserver).onLongPress(any())
+ verify(longPressDragObserver).onCancel()
+ }
+ verifyNoMoreInteractions(longPressDragObserver)
+ }
+
+ @Test
+ fun ui_downWaitForLongPressMoveCancel_callbacksCorrect() {
+
+ val down = MotionEvent(
+ 0,
+ MotionEvent.ACTION_DOWN,
+ 1,
+ 0,
+ arrayOf(PointerProperties(0)),
+ arrayOf(PointerCoords(50f, 50f))
+ )
+ waitForLongPress {
+ view.dispatchTouchEvent(down)
+ }
+ val move = MotionEvent(
+ 0,
+ MotionEvent.ACTION_MOVE,
+ 1,
+ 0,
+ arrayOf(PointerProperties(0)),
+ arrayOf(PointerCoords(51f, 50f))
+ )
+ view.dispatchTouchEvent(move)
+ val cancel = MotionEvent(
+ 0,
+ MotionEvent.ACTION_CANCEL,
+ 1,
+ 0,
+ arrayOf(PointerProperties(0)),
+ arrayOf(PointerCoords(51f, 50f))
+ )
+ view.dispatchTouchEvent(cancel)
+
+ inOrder(longPressDragObserver) {
+ verify(longPressDragObserver).onLongPress(any())
+ verify(longPressDragObserver).onDragStart()
+ // Twice because DragGestureDetector dispatches onDrag during 2 passes.
+ verify(longPressDragObserver, times(2)).onDrag(any())
+ verify(longPressDragObserver).onCancel()
+ }
verifyNoMoreInteractions(longPressDragObserver)
}
@@ -264,11 +368,11 @@
onLongPress()
}
- override fun onDragStart() { }
+ override fun onDragStart() {}
override fun onDrag(dragDistance: PxPosition): PxPosition {
return super.onDrag(dragDistance)
}
- override fun onStop(velocity: PxPosition) { }
+ override fun onStop(velocity: PxPosition) {}
}
\ No newline at end of file
diff --git a/ui/ui-framework/src/androidTest/java/androidx/ui/core/gesture/PressGestureDetectorTest.kt b/ui/ui-framework/src/androidTest/java/androidx/ui/core/gesture/PressGestureDetectorTest.kt
index aeb2396..0bd52ae 100644
--- a/ui/ui-framework/src/androidTest/java/androidx/ui/core/gesture/PressGestureDetectorTest.kt
+++ b/ui/ui-framework/src/androidTest/java/androidx/ui/core/gesture/PressGestureDetectorTest.kt
@@ -166,6 +166,36 @@
verify(onRelease, never()).invoke()
}
+ // Verify when onCancel should be called.
+
+ @Test
+ fun ui_downCancel_onCancelCalled() {
+ compose()
+
+ val down = MotionEvent(
+ 0,
+ MotionEvent.ACTION_DOWN,
+ 1,
+ 0,
+ arrayOf(PointerProperties(0)),
+ arrayOf(PointerCoords(50f, 50f))
+ )
+ val cancel = MotionEvent(
+ 10,
+ MotionEvent.ACTION_CANCEL,
+ 1,
+ 0,
+ arrayOf(PointerProperties(0)),
+ arrayOf(PointerCoords(50f, 50f))
+ )
+ activityTestRule.runOnUiThreadIR {
+ view.dispatchTouchEvent(down)
+ view.dispatchTouchEvent(cancel)
+ }
+
+ verify(onCancel).invoke()
+ }
+
private fun compose(enabled: Boolean? = null) {
val setupLatch = CountDownLatch(2)
activityTestRule.runOnUiThreadIR {
diff --git a/ui/ui-framework/src/androidTest/java/androidx/ui/core/gesture/ScaleGestureDetectorTest.kt b/ui/ui-framework/src/androidTest/java/androidx/ui/core/gesture/ScaleGestureDetectorTest.kt
index 65b0025..0fd04b3 100644
--- a/ui/ui-framework/src/androidTest/java/androidx/ui/core/gesture/ScaleGestureDetectorTest.kt
+++ b/ui/ui-framework/src/androidTest/java/androidx/ui/core/gesture/ScaleGestureDetectorTest.kt
@@ -141,7 +141,7 @@
}
@Test
- fun ui_pointerMovementBeyondTouchSlop_callbacksCalled() {
+ fun ui_pointerMovementBeyondTouchSlop_correctCallbacksInOrder() {
val touchSlopFloat = touchSlop.value.toFloat()
@@ -219,6 +219,75 @@
verify().onStop()
}
}
+
+ // Verify when onCancel should be called.
+
+ @Test
+ fun ui_downMoveBeyondSlopCancel_correctCallbacksInOrder() {
+
+ val touchSlopFloat = touchSlop.value.toFloat()
+
+ val down1 = MotionEvent(
+ 0,
+ MotionEvent.ACTION_DOWN,
+ 1,
+ 0,
+ arrayOf(PointerProperties(0)),
+ arrayOf(PointerCoords(touchSlopFloat, 50f))
+ )
+ val down2 = MotionEvent(
+ 10,
+ MotionEvent.ACTION_POINTER_DOWN,
+ 2,
+ 1,
+ arrayOf(PointerProperties(0), PointerProperties(1)),
+ arrayOf(
+ PointerCoords(touchSlopFloat, 50f),
+ PointerCoords(touchSlopFloat * 3, 50f)
+ )
+ )
+ val move = MotionEvent(
+ 20,
+ MotionEvent.ACTION_MOVE,
+ 2,
+ 0,
+ arrayOf(
+ PointerProperties(0),
+ PointerProperties(1)
+ ),
+ arrayOf(
+ PointerCoords(-touchSlopFloat * 2, 50f),
+ PointerCoords(touchSlopFloat * 6, 50f)
+ )
+ )
+ val cancel = MotionEvent(
+ 30,
+ MotionEvent.ACTION_CANCEL,
+ 2,
+ 0,
+ arrayOf(
+ PointerProperties(0),
+ PointerProperties(1)
+ ),
+ arrayOf(
+ PointerCoords(-touchSlopFloat * 2, 50f),
+ PointerCoords(touchSlopFloat * 6, 50f)
+ )
+ )
+
+ activityTestRule.runOnUiThreadIR {
+ view.dispatchTouchEvent(down1)
+ view.dispatchTouchEvent(down2)
+ view.dispatchTouchEvent(move)
+ view.dispatchTouchEvent(cancel)
+ }
+
+ scaleObserver.inOrder {
+ verify().onStart()
+ verify().onScale(4f)
+ verify().onCancel()
+ }
+ }
}
@Suppress("RedundantOverride")
diff --git a/ui/ui-framework/src/androidTest/java/androidx/ui/core/gesture/TouchSlopDragGestureDetectorTest.kt b/ui/ui-framework/src/androidTest/java/androidx/ui/core/gesture/TouchSlopDragGestureDetectorTest.kt
index e46f8bb..151bea9 100644
--- a/ui/ui-framework/src/androidTest/java/androidx/ui/core/gesture/TouchSlopDragGestureDetectorTest.kt
+++ b/ui/ui-framework/src/androidTest/java/androidx/ui/core/gesture/TouchSlopDragGestureDetectorTest.kt
@@ -93,7 +93,7 @@
}
@Test
- fun ui_pointerMovementBeyondTouchSlop_callbacksCalled() {
+ fun ui_pointerDownMovementBeyondTouchSlopUp_correctCallbacksInOrder() {
setup(false)
val touchSlopFloat = touchSlop.value.toFloat()
@@ -140,6 +140,53 @@
}
@Test
+ fun ui_pointerDownMovementBeyondTouchSlopCancel_correctCallbacksInOrder() {
+ setup(false)
+
+ val touchSlopFloat = touchSlop.value.toFloat()
+
+ val down = MotionEvent(
+ 0,
+ MotionEvent.ACTION_DOWN,
+ 1,
+ 0,
+ arrayOf(PointerProperties(0)),
+ arrayOf(PointerCoords(50f, 50f))
+ )
+ val move = MotionEvent(
+ 20,
+ MotionEvent.ACTION_MOVE,
+ 1,
+ 0,
+ arrayOf(PointerProperties(0)),
+ arrayOf(PointerCoords(50f + touchSlopFloat + 1, 50f))
+ )
+ val cancel = MotionEvent(
+ 30,
+ MotionEvent.ACTION_CANCEL,
+ 1,
+ 0,
+ arrayOf(PointerProperties(0)),
+ arrayOf(PointerCoords(50f + touchSlopFloat, 50f))
+ )
+
+ activityTestRule.runOnUiThreadIR {
+ view.dispatchTouchEvent(down)
+ view.dispatchTouchEvent(move)
+ view.dispatchTouchEvent(cancel)
+ }
+
+ dragObserver.inOrder {
+ verify().onStart(PxPosition(50.ipx, 50.ipx))
+ // Twice because RawDragGestureDetector calls the callback on both postUp and postDown
+ // and nothing consumes the drag distance.
+ verify(dragObserver, times(2))
+ .onDrag(PxPosition(Px(touchSlopFloat + 1), 0.px))
+ verify().onCancel()
+ }
+ }
+
+ @Test
fun ui_startDragImmediatelyTrueDown_callbacksCalled() {
setup(true)