Add sendLongClick methods to GestureScope
Adds two overloads, one without arguments that performs a long click in
the middle of the component, and one that accepts a position where the
long click must be performed.
Bug: 146553670
Test: ./gradlew ui:ui-test:cC
Change-Id: Ic7db9ad532d241f1f3e354cd7b7e55dac6eaaded
diff --git a/ui/ui-test/api/0.1.0-dev04.txt b/ui/ui-test/api/0.1.0-dev04.txt
index 71ca2c3..98a513c 100644
--- a/ui/ui-test/api/0.1.0-dev04.txt
+++ b/ui/ui-test/api/0.1.0-dev04.txt
@@ -146,6 +146,8 @@
public final class GestureScopeKt {
method public static void sendClick(androidx.ui.test.GestureScope, androidx.ui.core.PxPosition position);
method public static void sendClick(androidx.ui.test.GestureScope);
+ method public static void sendLongClick(androidx.ui.test.GestureScope, androidx.ui.core.PxPosition position);
+ method public static void sendLongClick(androidx.ui.test.GestureScope);
method public static void sendSwipe(androidx.ui.test.GestureScope, androidx.ui.core.PxPosition start, androidx.ui.core.PxPosition end, androidx.ui.core.Duration duration = 200.milliseconds);
method public static void sendSwipeDown(androidx.ui.test.GestureScope);
method public static void sendSwipeLeft(androidx.ui.test.GestureScope);
diff --git a/ui/ui-test/api/current.txt b/ui/ui-test/api/current.txt
index 71ca2c3..98a513c 100644
--- a/ui/ui-test/api/current.txt
+++ b/ui/ui-test/api/current.txt
@@ -146,6 +146,8 @@
public final class GestureScopeKt {
method public static void sendClick(androidx.ui.test.GestureScope, androidx.ui.core.PxPosition position);
method public static void sendClick(androidx.ui.test.GestureScope);
+ method public static void sendLongClick(androidx.ui.test.GestureScope, androidx.ui.core.PxPosition position);
+ method public static void sendLongClick(androidx.ui.test.GestureScope);
method public static void sendSwipe(androidx.ui.test.GestureScope, androidx.ui.core.PxPosition start, androidx.ui.core.PxPosition end, androidx.ui.core.Duration duration = 200.milliseconds);
method public static void sendSwipeDown(androidx.ui.test.GestureScope);
method public static void sendSwipeLeft(androidx.ui.test.GestureScope);
diff --git a/ui/ui-test/api/public_plus_experimental_0.1.0-dev04.txt b/ui/ui-test/api/public_plus_experimental_0.1.0-dev04.txt
index 71ca2c3..98a513c 100644
--- a/ui/ui-test/api/public_plus_experimental_0.1.0-dev04.txt
+++ b/ui/ui-test/api/public_plus_experimental_0.1.0-dev04.txt
@@ -146,6 +146,8 @@
public final class GestureScopeKt {
method public static void sendClick(androidx.ui.test.GestureScope, androidx.ui.core.PxPosition position);
method public static void sendClick(androidx.ui.test.GestureScope);
+ method public static void sendLongClick(androidx.ui.test.GestureScope, androidx.ui.core.PxPosition position);
+ method public static void sendLongClick(androidx.ui.test.GestureScope);
method public static void sendSwipe(androidx.ui.test.GestureScope, androidx.ui.core.PxPosition start, androidx.ui.core.PxPosition end, androidx.ui.core.Duration duration = 200.milliseconds);
method public static void sendSwipeDown(androidx.ui.test.GestureScope);
method public static void sendSwipeLeft(androidx.ui.test.GestureScope);
diff --git a/ui/ui-test/api/public_plus_experimental_current.txt b/ui/ui-test/api/public_plus_experimental_current.txt
index 71ca2c3..98a513c 100644
--- a/ui/ui-test/api/public_plus_experimental_current.txt
+++ b/ui/ui-test/api/public_plus_experimental_current.txt
@@ -146,6 +146,8 @@
public final class GestureScopeKt {
method public static void sendClick(androidx.ui.test.GestureScope, androidx.ui.core.PxPosition position);
method public static void sendClick(androidx.ui.test.GestureScope);
+ method public static void sendLongClick(androidx.ui.test.GestureScope, androidx.ui.core.PxPosition position);
+ method public static void sendLongClick(androidx.ui.test.GestureScope);
method public static void sendSwipe(androidx.ui.test.GestureScope, androidx.ui.core.PxPosition start, androidx.ui.core.PxPosition end, androidx.ui.core.Duration duration = 200.milliseconds);
method public static void sendSwipeDown(androidx.ui.test.GestureScope);
method public static void sendSwipeLeft(androidx.ui.test.GestureScope);
diff --git a/ui/ui-test/api/restricted_0.1.0-dev04.txt b/ui/ui-test/api/restricted_0.1.0-dev04.txt
index 71ca2c3..98a513c 100644
--- a/ui/ui-test/api/restricted_0.1.0-dev04.txt
+++ b/ui/ui-test/api/restricted_0.1.0-dev04.txt
@@ -146,6 +146,8 @@
public final class GestureScopeKt {
method public static void sendClick(androidx.ui.test.GestureScope, androidx.ui.core.PxPosition position);
method public static void sendClick(androidx.ui.test.GestureScope);
+ method public static void sendLongClick(androidx.ui.test.GestureScope, androidx.ui.core.PxPosition position);
+ method public static void sendLongClick(androidx.ui.test.GestureScope);
method public static void sendSwipe(androidx.ui.test.GestureScope, androidx.ui.core.PxPosition start, androidx.ui.core.PxPosition end, androidx.ui.core.Duration duration = 200.milliseconds);
method public static void sendSwipeDown(androidx.ui.test.GestureScope);
method public static void sendSwipeLeft(androidx.ui.test.GestureScope);
diff --git a/ui/ui-test/api/restricted_current.txt b/ui/ui-test/api/restricted_current.txt
index 71ca2c3..98a513c 100644
--- a/ui/ui-test/api/restricted_current.txt
+++ b/ui/ui-test/api/restricted_current.txt
@@ -146,6 +146,8 @@
public final class GestureScopeKt {
method public static void sendClick(androidx.ui.test.GestureScope, androidx.ui.core.PxPosition position);
method public static void sendClick(androidx.ui.test.GestureScope);
+ method public static void sendLongClick(androidx.ui.test.GestureScope, androidx.ui.core.PxPosition position);
+ method public static void sendLongClick(androidx.ui.test.GestureScope);
method public static void sendSwipe(androidx.ui.test.GestureScope, androidx.ui.core.PxPosition start, androidx.ui.core.PxPosition end, androidx.ui.core.Duration duration = 200.milliseconds);
method public static void sendSwipeDown(androidx.ui.test.GestureScope);
method public static void sendSwipeLeft(androidx.ui.test.GestureScope);
diff --git a/ui/ui-test/src/androidTest/java/androidx/ui/test/SendLongClickTest.kt b/ui/ui-test/src/androidTest/java/androidx/ui/test/SendLongClickTest.kt
new file mode 100644
index 0000000..9d4ebb9
--- /dev/null
+++ b/ui/ui-test/src/androidTest/java/androidx/ui/test/SendLongClickTest.kt
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.test
+
+import androidx.compose.Composable
+import androidx.test.filters.MediumTest
+import androidx.ui.core.Alignment
+import androidx.ui.core.PointerInputWrapper
+import androidx.ui.core.PxPosition
+import androidx.ui.core.TestTag
+import androidx.ui.core.WithDensity
+import androidx.ui.core.gesture.LongPressGestureDetector
+import androidx.ui.core.gesture.LongPressTimeout
+import androidx.ui.core.milliseconds
+import androidx.ui.core.px
+import androidx.ui.foundation.shape.DrawShape
+import androidx.ui.foundation.shape.RectangleShape
+import androidx.ui.graphics.Color
+import androidx.ui.layout.Align
+import androidx.ui.layout.Container
+import androidx.ui.semantics.Semantics
+import androidx.ui.test.util.PointerInputRecorder
+import androidx.ui.test.util.areAlmostEqualTo
+import androidx.ui.test.util.assertOnlyLastEventIsUp
+import androidx.ui.test.util.assertTimestampsAreIncreasing
+import androidx.ui.test.util.recordedDuration
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.junit.runners.Parameterized
+
+private const val tag = "widget"
+private val width = 100.px
+private val height = 100.px
+private val expectedDuration = LongPressTimeout + 100.milliseconds
+
+@Composable
+private fun Ui(recorder: PointerInputRecorder, onLongPress: (PxPosition) -> Unit) {
+ Align(alignment = Alignment.BottomRight) {
+ TestTag(tag) {
+ Semantics {
+ LongPressGestureDetector( {
+ PointerInputWrapper(
+ pointerInputHandler = recorder::onPointerInput,
+ cancelHandler = {}
+ ) {
+ WithDensity {
+ Container(width = width.toDp(), height = height.toDp()) {
+ DrawShape(RectangleShape, Color.Yellow)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Tests [GestureScope.sendLongClick] without arguments. Verifies that the click is in the middle
+ * of the component, that the gesture has a duration of 600 milliseconds and that all input
+ * events were on the same location.
+ */
+@MediumTest
+@RunWith(JUnit4::class)
+class SendLongClickWithoutArgumentsTest {
+
+ @get:Rule
+ val composeTestRule = createComposeRule(disableTransitions = true)
+
+ private val recorder = PointerInputRecorder()
+ private val recordedLongClicks = mutableListOf<PxPosition>()
+ private val expectedPosition = PxPosition(width / 2, height / 2)
+
+ private fun recordLongPress(position: PxPosition) {
+ recordedLongClicks.add(position)
+ }
+
+ @Test
+ fun testLongClick() {
+ // Given some content
+ composeTestRule.setContent { Ui(recorder, ::recordLongPress) }
+
+ // When we inject a long click
+ findByTag(tag).doGesture { sendLongClick() }
+
+ // Then we record 1 long click
+ assertThat(recordedLongClicks).hasSize(1)
+
+ // And all events are at the click location
+ composeTestRule.runOnUiThread {
+ recorder.run {
+ assertTimestampsAreIncreasing()
+ assertOnlyLastEventIsUp()
+ events.areAlmostEqualTo(expectedPosition)
+ assertThat(recordedDuration).isEqualTo(expectedDuration)
+ }
+ }
+ }
+}
+
+/**
+ * Tests [GestureScope.sendLongClick] with arguments. Verifies that the click is in the middle
+ * of the component, that the gesture has a duration of 600 milliseconds and that all input
+ * events were on the same location.
+ */
+@MediumTest
+@RunWith(Parameterized::class)
+class SendLongClickWithArgumentsTest(private val config: TestConfig) {
+ data class TestConfig(
+ val position: PxPosition
+ )
+
+ companion object {
+ @JvmStatic
+ @Parameterized.Parameters(name = "{0}")
+ fun createTestSet(): List<TestConfig> {
+ return mutableListOf<TestConfig>().apply {
+ for (x in listOf(1.px, width / 4)) {
+ for (y in listOf(1.px, height / 4)) {
+ add(TestConfig(PxPosition(x, y)))
+ }
+ }
+ }
+ }
+ }
+
+ @get:Rule
+ val composeTestRule = createComposeRule(disableTransitions = true)
+
+ private val recorder = PointerInputRecorder()
+ private val recordedLongClicks = mutableListOf<PxPosition>()
+
+ private fun recordLongPress(position: PxPosition) {
+ recordedLongClicks.add(position)
+ }
+
+ @Test
+ fun testLongClick() {
+ // Given some content
+ composeTestRule.setContent { Ui(recorder, ::recordLongPress) }
+
+ // When we inject a long click
+ findByTag(tag).doGesture { sendLongClick(config.position) }
+
+ // Then we record 1 long click
+ assertThat(recordedLongClicks).hasSize(1)
+
+ // And all events are at the click location
+ composeTestRule.runOnUiThread {
+ recorder.run {
+ assertTimestampsAreIncreasing()
+ assertOnlyLastEventIsUp()
+ events.areAlmostEqualTo(config.position)
+ assertThat(recordedDuration).isEqualTo(expectedDuration)
+ }
+ }
+ }
+}
diff --git a/ui/ui-test/src/androidTest/java/androidx/ui/test/util/PointerInputs.kt b/ui/ui-test/src/androidTest/java/androidx/ui/test/util/PointerInputs.kt
index 0fd1343..e682b5b 100644
--- a/ui/ui-test/src/androidTest/java/androidx/ui/test/util/PointerInputs.kt
+++ b/ui/ui-test/src/androidTest/java/androidx/ui/test/util/PointerInputs.kt
@@ -86,3 +86,11 @@
map { it.x.value }.isMonotonousBetween(start.x.value, end.x.value, 1e-3f)
map { it.y.value }.isMonotonousBetween(start.y.value, end.y.value, 1e-3f)
}
+
+/**
+ * Verifies that all [DataPoint]s in the list are equal to the given position, with a tolerance
+ * of 0.001
+ */
+fun List<DataPoint>.areAlmostEqualTo(position: PxPosition) {
+ forEach { it.position.isAlmostEqualTo(position, 1e-3f) }
+}
diff --git a/ui/ui-test/src/main/java/androidx/ui/test/GestureScope.kt b/ui/ui-test/src/main/java/androidx/ui/test/GestureScope.kt
index c018f95..6886797 100644
--- a/ui/ui-test/src/main/java/androidx/ui/test/GestureScope.kt
+++ b/ui/ui-test/src/main/java/androidx/ui/test/GestureScope.kt
@@ -21,6 +21,7 @@
import androidx.ui.core.Px
import androidx.ui.core.PxPosition
import androidx.ui.core.SemanticsTreeNode
+import androidx.ui.core.gesture.LongPressTimeout
import androidx.ui.core.inMilliseconds
import androidx.ui.core.milliseconds
import androidx.ui.core.px
@@ -95,6 +96,31 @@
}
/**
+ * Performs a long click gesture on the given [position] on the associated component. There will
+ * be [LongPressTimeout] + 100 milliseconds time between the down and the up event. The
+ * [position] is in the component's local coordinate system.
+ *
+ * Throws [AssertionError] when the component doesn't have a bounding rectangle set
+ *
+ * @param position The position of the long click, in the component's local coordinate system
+ */
+fun GestureScope.sendLongClick(position: PxPosition) {
+ // Keep down for 100ms more than needed, to allow the long press logic to trigger
+ sendSwipe(position, position, LongPressTimeout + 100.milliseconds)
+}
+
+/**
+ * Performs a long click gesture on the middle of the associated component. There will
+ * be [LongPressTimeout] + 100 milliseconds time between the down and the up event.
+ *
+ * Throws [AssertionError] when the component doesn't have a bounding rectangle set
+ */
+fun GestureScope.sendLongClick() {
+ val bounds = getGlobalBounds()
+ sendLongClick(PxPosition(Px(bounds.width / 2), Px(bounds.height / 2)))
+}
+
+/**
* Performs the swipe gesture on the associated component. The motion events are linearly
* interpolated between [start] and [end]. The coordinates are in the component's local
* coordinate system, i.e. (0, 0) is the top left corner of the component. The default duration