xref: /aosp_15_r20/cts/tests/input/src/android/input/cts/TouchScreenTest.kt (revision b7c941bb3fa97aba169d73cee0bed2de8ac964bf)
1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.input.cts
18 
19 import android.cts.input.EventVerifier
20 import android.graphics.Point
21 import android.hardware.input.InputManager
22 import android.view.MotionEvent
23 import android.view.MotionEvent.ACTION_MOVE
24 import androidx.test.ext.junit.runners.AndroidJUnit4
25 import androidx.test.filters.MediumTest
26 import androidx.test.platform.app.InstrumentationRegistry
27 import com.android.cts.input.DebugInputRule
28 import com.android.cts.input.UinputTouchDevice
29 import com.android.cts.input.UinputTouchScreen
30 import com.android.cts.input.VirtualDisplayActivityScenario
31 import com.android.cts.input.VirtualDisplayActivityScenario.Companion.DEFAULT_HEIGHT
32 import com.android.cts.input.VirtualDisplayActivityScenario.Companion.DEFAULT_WIDTH
33 import com.android.cts.input.VirtualDisplayActivityScenario.Companion.ORIENTATION_0
34 import com.android.cts.input.VirtualDisplayActivityScenario.Companion.ORIENTATION_180
35 import com.android.cts.input.VirtualDisplayActivityScenario.Companion.ORIENTATION_270
36 import com.android.cts.input.VirtualDisplayActivityScenario.Companion.ORIENTATION_90
37 import com.android.cts.input.inputeventmatchers.withCoords
38 import com.android.cts.input.inputeventmatchers.withFlags
39 import com.android.cts.input.inputeventmatchers.withMotionAction
40 import com.android.cts.input.inputeventmatchers.withPointerCount
41 import org.hamcrest.Description
42 import org.hamcrest.Matchers.allOf
43 import org.hamcrest.TypeSafeMatcher
44 import org.junit.After
45 import org.junit.Assert.assertNull
46 import org.junit.Before
47 import org.junit.Rule
48 import org.junit.Test
49 import org.junit.rules.TestName
50 import org.junit.runner.RunWith
51 
52 @MediumTest
53 @RunWith(AndroidJUnit4::class)
54 class TouchScreenTest {
55     private val instrumentation = InstrumentationRegistry.getInstrumentation()
56     private lateinit var touchScreen: UinputTouchDevice
57     private lateinit var verifier: EventVerifier
58 
59     @get:Rule
60     val debugInputRule = DebugInputRule()
61     @get:Rule
62     val testName = TestName()
63     @get:Rule
64     val virtualDisplayRule = VirtualDisplayActivityScenario.Rule<CaptureEventActivity>(testName)
65 
66     @Before
setUpnull67     fun setUp() {
68         touchScreen = UinputTouchScreen(instrumentation, virtualDisplayRule.virtualDisplay.display)
69         verifier = EventVerifier(virtualDisplayRule.activity::getInputEvent)
70     }
71 
72     @After
tearDownnull73     fun tearDown() {
74         if (this::touchScreen.isInitialized) {
75             touchScreen.close()
76         }
77     }
78 
79     @Test
testHostUsiVersionIsNullnull80     fun testHostUsiVersionIsNull() {
81         assertNull(
82             instrumentation.targetContext.getSystemService(InputManager::class.java)
83                 .getHostUsiVersion(virtualDisplayRule.virtualDisplay.display)
84         )
85     }
86 
87     @DebugInputRule.DebugInput(bug = 288321659)
88     @Test
testSingleTouchnull89     fun testSingleTouch() {
90         val x = 100
91         val y = 100
92 
93         val pointer = touchScreen.touchDown(x, y)
94         verifier.assertReceivedMotion(withMotionAction(MotionEvent.ACTION_DOWN))
95 
96         pointer.moveTo(x + 1, y + 1)
97         verifier.assertReceivedMotion(withMotionAction(ACTION_MOVE))
98 
99         pointer.lift()
100         verifier.assertReceivedMotion(withMotionAction(MotionEvent.ACTION_UP))
101     }
102 
103     @DebugInputRule.DebugInput(bug = 288321659)
104     @Test
testMultiTouchnull105     fun testMultiTouch() {
106         val location1 = Point(100, 100)
107         val location2 = Point(150, 150)
108 
109         // ACTION_DOWN
110         val pointer1 = touchScreen.touchDown(location1.x, location1.y)
111         verifier.assertReceivedMotion(withMotionAction(MotionEvent.ACTION_DOWN))
112 
113         // ACTION_POINTER_DOWN
114         val pointer2 = touchScreen.touchDown(location2.x, location2.y)
115         verifier.assertReceivedMotion(withMotionAction(MotionEvent.ACTION_POINTER_DOWN, 1))
116 
117         // ACTION_MOVE
118         location1.offset(1, 1)
119         pointer1.moveTo(location1.x, location1.y)
120         verifier.assertReceivedMotion(withMotionAction(ACTION_MOVE))
121 
122         // ACTION_POINTER_UP
123         pointer1.lift()
124         verifier.assertReceivedMotion(withMotionAction(MotionEvent.ACTION_POINTER_UP, 0))
125 
126         // ACTION_UP
127         pointer2.lift()
128         verifier.assertReceivedMotion(withMotionAction(MotionEvent.ACTION_UP))
129     }
130 
131     @DebugInputRule.DebugInput(bug = 288321659)
132     @Test
testDeviceCancelnull133     fun testDeviceCancel() {
134         val location = Point(100, 100)
135 
136         // ACTION_DOWN
137         val pointer = touchScreen.touchDown(location.x, location.y)
138         verifier.assertReceivedMotion(withMotionAction(MotionEvent.ACTION_DOWN))
139 
140         // ACTION_MOVE
141         location.offset(1, 1)
142         pointer.moveTo(location.x, location.y)
143         verifier.assertReceivedMotion(withMotionAction(ACTION_MOVE))
144 
145         // ACTION_CANCEL
146         pointer.close()
147         verifier.assertReceivedMotion(withMotionAction(MotionEvent.ACTION_CANCEL))
148 
149         // No event
150         virtualDisplayRule.activity.assertNoEvents()
151     }
152 
153     /**
154      * Check that pointer cancel is received by the activity via uinput device.
155      */
156     @DebugInputRule.DebugInput(bug = 288321659)
157     @Test
testDevicePointerCancelnull158     fun testDevicePointerCancel() {
159         val location1 = Point(100, 100)
160         val location2 = Point(150, 150)
161 
162         // ACTION_DOWN
163         val pointer1 = touchScreen.touchDown(location1.x, location1.y)
164         verifier.assertReceivedMotion(withMotionAction(MotionEvent.ACTION_DOWN))
165 
166         // ACTION_MOVE
167         location1.offset(1, 1)
168         pointer1.moveTo(location1.x, location1.y)
169         verifier.assertReceivedMotion(withMotionAction(ACTION_MOVE))
170 
171         // ACTION_POINTER_DOWN(1)
172         val pointer2 = touchScreen.touchDown(location2.x, location2.y)
173         verifier.assertReceivedMotion(withMotionAction(MotionEvent.ACTION_POINTER_DOWN, 1))
174 
175         // ACTION_POINTER_UP(1) with cancel flag
176         pointer2.close()
177         verifier.assertReceivedMotion(
178             allOf(
179                 withMotionAction(MotionEvent.ACTION_POINTER_UP, 1),
180                 withFlags(MotionEvent.FLAG_CANCELED)
181             )
182         )
183 
184         // ACTION_UP
185         pointer1.lift()
186         // TODO(b/341773832): Fix the implementation so that this unexpected MOVE is not generated.
187         verifier.assertReceivedMotion(allOf(withMotionAction(ACTION_MOVE), withPointerCount(1)))
188         verifier.assertReceivedMotion(withMotionAction(MotionEvent.ACTION_UP))
189     }
190 
191     @Test
testTouchScreenPrecisionOrientation0null192     fun testTouchScreenPrecisionOrientation0() {
193         virtualDisplayRule.runInDisplayOrientation(ORIENTATION_0) {
194             verifyTapsOnFourCorners(
195                 arrayOf(
196                     Point(0, 0),
197                     Point(DEFAULT_WIDTH - 1, 0),
198                     Point(DEFAULT_WIDTH - 1, DEFAULT_HEIGHT - 1),
199                     Point(0, DEFAULT_HEIGHT - 1),
200                 )
201             )
202         }
203     }
204 
205     @Test
testTouchScreenPrecisionOrientation90null206     fun testTouchScreenPrecisionOrientation90() {
207         virtualDisplayRule.runInDisplayOrientation(ORIENTATION_90) {
208             verifyTapsOnFourCorners(
209                 arrayOf(
210                     Point(0, DEFAULT_WIDTH - 1),
211                     Point(0, 0),
212                     Point(DEFAULT_HEIGHT - 1, 0),
213                     Point(DEFAULT_HEIGHT - 1, DEFAULT_WIDTH - 1),
214                 )
215             )
216         }
217     }
218 
219     @Test
testTouchScreenPrecisionOrientation180null220     fun testTouchScreenPrecisionOrientation180() {
221         virtualDisplayRule.runInDisplayOrientation(ORIENTATION_180) {
222             verifyTapsOnFourCorners(
223                 arrayOf(
224                     Point(DEFAULT_WIDTH - 1, DEFAULT_HEIGHT - 1),
225                     Point(0, DEFAULT_HEIGHT - 1),
226                     Point(0, 0),
227                     Point(DEFAULT_WIDTH - 1, 0),
228                 )
229             )
230         }
231     }
232 
233     @Test
testTouchScreenPrecisionOrientation270null234     fun testTouchScreenPrecisionOrientation270() {
235         virtualDisplayRule.runInDisplayOrientation(ORIENTATION_270) {
236             verifyTapsOnFourCorners(
237                 arrayOf(
238                     Point(DEFAULT_HEIGHT - 1, 0),
239                     Point(DEFAULT_HEIGHT - 1, DEFAULT_WIDTH - 1),
240                     Point(0, DEFAULT_WIDTH - 1),
241                     Point(0, 0),
242                 )
243             )
244         }
245     }
246 
247     @Test
testEventTimenull248     fun testEventTime() {
249         val location = Point(100, 100)
250 
251         val withConsistentEventTime = object : TypeSafeMatcher<MotionEvent>() {
252             override fun describeTo(description: Description) {
253                 description.appendText("getEventTimeNanos() is consistent with getEventTime()")
254             }
255 
256             override fun matchesSafely(event: MotionEvent): Boolean {
257                 return event.eventTimeNanos / 1_000_000 == event.eventTime
258             }
259         }
260 
261        // ACTION_DOWN
262         val pointer = touchScreen.touchDown(location.x, location.y)
263         verifier.assertReceivedMotion(
264             allOf(withMotionAction(MotionEvent.ACTION_DOWN), withConsistentEventTime)
265         )
266 
267         // ACTION_MOVE
268         location.offset(1, 1)
269         pointer.moveTo(location.x, location.y)
270         verifier.assertReceivedMotion(
271             allOf(withMotionAction(ACTION_MOVE), withConsistentEventTime)
272         )
273 
274         // ACTION_UP
275         pointer.lift()
276         verifier.assertReceivedMotion(
277             allOf(withMotionAction(MotionEvent.ACTION_UP), withConsistentEventTime)
278         )
279     }
280 
281     // Verifies that each of the four corners of the touch screen (lt, rt, rb, lb) map to the
282     // given four points by tapping on the corners in order and asserting the location of the
283     // received events match the provided values.
verifyTapsOnFourCornersnull284     private fun verifyTapsOnFourCorners(expectedPoints: Array<Point>) {
285         val pointerId = 0
286         for (i in 0 until 4) {
287             touchScreen.sendBtnTouch(true)
288             touchScreen.sendDown(pointerId, CORNERS[i])
289             touchScreen.sync()
290             verifier.assertReceivedMotion(
291                 allOf(withMotionAction(MotionEvent.ACTION_DOWN), withCoords(expectedPoints[i]))
292             )
293 
294             touchScreen.sendBtnTouch(false)
295             touchScreen.sendUp(pointerId)
296             touchScreen.sync()
297             verifier.assertReceivedMotion(withMotionAction(MotionEvent.ACTION_UP))
298         }
299     }
300 
301     companion object {
302         // The four corners of the touchscreen: lt, rt, rb, lb
303         val CORNERS = arrayOf(
304             Point(0, 0),
305             Point(DEFAULT_WIDTH - 1, 0),
306             Point(DEFAULT_WIDTH - 1, DEFAULT_HEIGHT - 1),
307             Point(0, DEFAULT_HEIGHT - 1),
308         )
309     }
310 }
311