1 /*
2 * Copyright 2024 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 com.android.cts.input.inputeventmatchers
18
19 import android.graphics.Point
20 import android.graphics.PointF
21 import android.view.KeyEvent
22 import android.view.KeyEvent.keyCodeToString
23 import android.view.MotionEvent
24 import android.view.MotionEvent.PointerCoords
25 import kotlin.math.abs
26 import org.hamcrest.Description
27 import org.hamcrest.Matcher
28 import org.hamcrest.TypeSafeMatcher
29
30 const val EPSILON = 0.001f
31
withCoordsnull32 fun withCoords(pt: PointF, epsilon: Float = EPSILON):
33 Matcher<MotionEvent> = object : TypeSafeMatcher<MotionEvent>() {
34 override fun describeTo(description: Description) {
35 description.appendText("With coords = $pt")
36 }
37
38 override fun matchesSafely(event: MotionEvent): Boolean {
39 return (abs(event.x - pt.x) < epsilon) &&
40 (abs(event.y - pt.y) < epsilon)
41 }
42 }
43
withPointerCountnull44 fun withPointerCount(count: Int): Matcher<MotionEvent> = object : TypeSafeMatcher<MotionEvent>() {
45 override fun describeTo(description: Description) {
46 description.appendText("With pointer count = $count")
47 }
48
49 override fun matchesSafely(event: MotionEvent): Boolean {
50 return event.pointerCount == count
51 }
52 }
53
withCoordsnull54 fun withCoords(pt: Point, epsilon: Float = EPSILON): Matcher<MotionEvent> = withCoords(
55 PointF(pt),
56 epsilon
57 )
58
59 fun withCoordsForPointerIndex(index: Int, pt: PointF, epsilon: Float = EPSILON):
60 Matcher<MotionEvent> = object : TypeSafeMatcher<MotionEvent>() {
61 override fun describeTo(description: Description) {
62 description.appendText("With coords = $pt for pointer index = $index")
63 }
64
65 override fun matchesSafely(event: MotionEvent): Boolean {
66 return (abs(event.getX(index)) - pt.x < epsilon) &&
67 (abs(event.getY(index)) - pt.y < epsilon)
68 }
69 }
70
withRawCoordsnull71 fun withRawCoords(pt: PointF, epsilon: Float = EPSILON):
72 Matcher<MotionEvent> = object : TypeSafeMatcher<MotionEvent>() {
73 override fun describeTo(description: Description) {
74 description.appendText("With coords = $pt")
75 }
76
77 override fun matchesSafely(event: MotionEvent): Boolean {
78 return (abs(event.rawX) - pt.x < epsilon) &&
79 (abs(event.rawY) - pt.y < epsilon)
80 }
81
82 override fun describeMismatchSafely(event: MotionEvent, mismatchDescription: Description) {
83 mismatchDescription.appendText("Got raw coords = {${event.rawX}, ${event.rawY}}")
84 }
85 }
86
hasResampledSamplesnull87 private fun hasResampledSamples(event: MotionEvent): Boolean {
88 for (h in 0 until event.historySize) {
89 for (p in 0 until event.pointerCount) {
90 if (PointerCoords().also { event.getHistoricalPointerCoords(p, h, it) }.isResampled()) {
91 return true
92 }
93 }
94 }
95 for (p in 0 until event.pointerCount) {
96 if (PointerCoords().also { event.getPointerCoords(p, it) }.isResampled()) {
97 return true
98 }
99 }
100 return false
101 }
102
103 // A MotionEvent is treated as "resampled" if it contains any resampled pointers.
isResamplednull104 fun isResampled(isResampled: Boolean): Matcher<MotionEvent> =
105 object : TypeSafeMatcher<MotionEvent>() {
106 override fun describeTo(description: Description) {
107 description.appendText("Is resampled = $isResampled")
108 }
109
110 override fun matchesSafely(event: MotionEvent): Boolean {
111 return hasResampledSamples(event) == isResampled
112 }
113
114 override fun describeMismatchSafely(event: MotionEvent, mismatchDescription: Description) {
115 mismatchDescription.appendText(
116 "Event was " + if (hasResampledSamples(event)) "" else "not" + " resampled"
117 )
118 }
119 }
120
withMotionActionnull121 fun withMotionAction(action: Int): Matcher<MotionEvent> = object : TypeSafeMatcher<MotionEvent>() {
122 override fun describeTo(description: Description) {
123 description.appendText("With action = ${MotionEvent.actionToString(action)}")
124 }
125
126 override fun matchesSafely(event: MotionEvent): Boolean {
127 if (action == MotionEvent.ACTION_CANCEL) {
128 if (event.flags and MotionEvent.FLAG_CANCELED != MotionEvent.FLAG_CANCELED) {
129 return false
130 }
131 }
132 return event.action == action
133 }
134 }
135
withMotionActionnull136 fun withMotionAction(action: Int, index: Int): Matcher<MotionEvent> =
137 object : TypeSafeMatcher<MotionEvent>() {
138 override fun describeTo(description: Description) {
139 description.appendText(
140 "With action = ${MotionEvent.actionToString(action)}, index = $index"
141 )
142 }
143
144 override fun matchesSafely(event: MotionEvent): Boolean {
145 if (action != MotionEvent.ACTION_POINTER_DOWN && action != MotionEvent.ACTION_POINTER_UP) {
146 throw Exception(
147 "Matcher should only be used with ACTION_POINTER_DOWN or ACTION_POINTER_UP"
148 )
149 }
150 return event.actionMasked == action && event.actionIndex == index
151 }
152 }
153
withRawCoordsnull154 fun withRawCoords(pt: Point, epsilon: Float = EPSILON): Matcher<MotionEvent> {
155 return withRawCoords(PointF(pt), epsilon)
156 }
157
withSourcenull158 fun withSource(source: Int): Matcher<MotionEvent> = object : TypeSafeMatcher<MotionEvent>() {
159 override fun describeTo(description: Description) {
160 description.appendText("With source = $source")
161 }
162
163 override fun matchesSafely(event: MotionEvent): Boolean {
164 return event.source == source
165 }
166 }
167
withButtonStatenull168 fun withButtonState(buttonState: Int): Matcher<MotionEvent> =
169 object : TypeSafeMatcher<MotionEvent>() {
170 override fun describeTo(description: Description) {
171 description.appendText("With buttonState = $buttonState")
172 }
173
174 override fun matchesSafely(event: MotionEvent): Boolean {
175 return event.buttonState == buttonState
176 }
177 }
178
withDeviceIdnull179 fun withDeviceId(deviceId: Int): Matcher<MotionEvent> = object : TypeSafeMatcher<MotionEvent>() {
180 override fun describeTo(description: Description) {
181 description.appendText("With deviceId = $deviceId")
182 }
183
184 override fun matchesSafely(event: MotionEvent): Boolean {
185 return event.deviceId == deviceId
186 }
187 }
188
withEventTimenull189 fun withEventTime(eventTime: Long): Matcher<MotionEvent> = object : TypeSafeMatcher<MotionEvent>() {
190 override fun describeTo(description: Description) {
191 description.appendText("With eventTime = $eventTime")
192 }
193
194 override fun matchesSafely(event: MotionEvent): Boolean {
195 return event.eventTime == eventTime
196 }
197 }
198
withFlagsnull199 fun withFlags(flags: Int): Matcher<MotionEvent> = object : TypeSafeMatcher<MotionEvent>() {
200 override fun describeTo(description: Description) {
201 description.appendText("With flags = $flags")
202 }
203
204 override fun matchesSafely(event: MotionEvent): Boolean {
205 return event.flags and flags == flags
206 }
207 }
208
withToolTypenull209 fun withToolType(toolType: Int): Matcher<MotionEvent> = object : TypeSafeMatcher<MotionEvent>() {
210 override fun describeTo(description: Description) {
211 description.appendText("With tool type = $toolType")
212 }
213
214 override fun matchesSafely(event: MotionEvent): Boolean {
215 for (p in 0..<event.pointerCount) {
216 if (event.getToolType(p) != toolType) {
217 return false
218 }
219 }
220 return true
221 }
222 }
223
withKeyCodenull224 fun withKeyCode(keyCode: Int): Matcher<KeyEvent> = object : TypeSafeMatcher<KeyEvent>() {
225 override fun describeTo(description: Description) {
226 description.appendText("With key code = " + keyCodeToString(keyCode))
227 }
228
229 override fun matchesSafely(event: KeyEvent): Boolean {
230 return event.keyCode == keyCode
231 }
232 }
233
withModifierStatenull234 fun withModifierState(modifierState: Int): Matcher<KeyEvent> =
235 object : TypeSafeMatcher<KeyEvent>() {
236 override fun describeTo(description: Description) {
237 description.appendText("With modifier state = $modifierState")
238 }
239
240 override fun matchesSafely(event: KeyEvent): Boolean {
241 return (event.metaState and modifierState) == modifierState
242 }
243 }
244
withKeyActionnull245 fun withKeyAction(keyAction: Int): Matcher<KeyEvent> = object : TypeSafeMatcher<KeyEvent>() {
246 override fun describeTo(description: Description) {
247 description.appendText("With key action = $keyAction")
248 }
249
250 override fun matchesSafely(event: KeyEvent): Boolean {
251 return event.action == keyAction
252 }
253 }
254
withEdgeFlagsnull255 fun withEdgeFlags(edgeFlags: Int): Matcher<MotionEvent> = object : TypeSafeMatcher<MotionEvent>() {
256 override fun describeTo(description: Description) {
257 description.appendText("With edge flags = 0x${edgeFlags.toString(16)}")
258 }
259
260 override fun matchesSafely(event: MotionEvent): Boolean {
261 return (event.edgeFlags and edgeFlags) == edgeFlags
262 }
263 }
264
withDownTimenull265 fun withDownTime(downTime: Long): Matcher<MotionEvent> = object : TypeSafeMatcher<MotionEvent>() {
266 override fun describeTo(description: Description) {
267 description.appendText("With down time = $downTime")
268 }
269
270 override fun matchesSafely(event: MotionEvent): Boolean {
271 return event.downTime == downTime
272 }
273 }
274
withMetaStatenull275 fun withMetaState(metaState: Int): Matcher<MotionEvent> = object : TypeSafeMatcher<MotionEvent>() {
276 override fun describeTo(description: Description) {
277 description.appendText("With meta state = 0x${metaState.toString(16)}")
278 }
279
280 override fun matchesSafely(event: MotionEvent): Boolean {
281 return event.metaState == metaState
282 }
283 }
284
withPressurenull285 fun withPressure(pressure: Float): Matcher<MotionEvent> = object : TypeSafeMatcher<MotionEvent>() {
286 override fun describeTo(description: Description) {
287 description.appendText("With pressure = $pressure")
288 }
289
290 override fun matchesSafely(event: MotionEvent): Boolean {
291 return abs(event.pressure - pressure) < EPSILON
292 }
293
294 override fun describeMismatchSafely(event: MotionEvent, mismatchDescription: Description) {
295 mismatchDescription.appendText("Got pressure ${event.pressure}")
296 }
297 }
298
withHistorySizenull299 fun withHistorySize(historySize: Int): Matcher<MotionEvent> =
300 object : TypeSafeMatcher<MotionEvent>() {
301 override fun describeTo(description: Description) {
302 description.appendText("With history size = $historySize")
303 }
304
305 override fun matchesSafely(event: MotionEvent): Boolean {
306 return event.historySize == historySize
307 }
308
309 override fun describeMismatchSafely(event: MotionEvent, mismatchDescription: Description) {
310 mismatchDescription.appendText("Got history size ${event.historySize}")
311 }
312 }
313
withSizenull314 fun withSize(size: Float): Matcher<MotionEvent> = object : TypeSafeMatcher<MotionEvent>() {
315 override fun describeTo(description: Description) {
316 description.appendText("With size = $size")
317 }
318
319 override fun matchesSafely(event: MotionEvent): Boolean {
320 return abs(event.size - size) < EPSILON
321 }
322 }
323
withXPrecisionnull324 fun withXPrecision(xPrecision: Float):
325 Matcher<MotionEvent> = object : TypeSafeMatcher<MotionEvent>() {
326 override fun describeTo(description: Description) {
327 description.appendText("With xPrecision = $xPrecision")
328 }
329
330 override fun matchesSafely(event: MotionEvent): Boolean {
331 return abs(event.xPrecision - xPrecision) < EPSILON
332 }
333 }
334
withYPrecisionnull335 fun withYPrecision(yPrecision: Float):
336 Matcher<MotionEvent> = object : TypeSafeMatcher<MotionEvent>() {
337 override fun describeTo(description: Description) {
338 description.appendText("With yPrecision = $yPrecision")
339 }
340
341 override fun matchesSafely(event: MotionEvent): Boolean {
342 return abs(event.yPrecision - yPrecision) < EPSILON
343 }
344 }
345