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 kotlin.math.abs
25 import org.hamcrest.Description
26 import org.hamcrest.Matcher
27 import org.hamcrest.TypeSafeMatcher
28 
29 private const val EPSILON = 0.001f
30 
withCoordsnull31 fun withCoords(pt: PointF): Matcher<MotionEvent> = object : TypeSafeMatcher<MotionEvent>() {
32     override fun describeTo(description: Description) {
33         description.appendText("With coords = $pt")
34     }
35 
36     override fun matchesSafely(event: MotionEvent): Boolean {
37         return (abs(event.x - pt.x) < EPSILON) &&
38                 (abs(event.y - pt.y) < EPSILON)
39     }
40 }
41 
withPointerCountnull42 fun withPointerCount(count: Int): Matcher<MotionEvent> = object : TypeSafeMatcher<MotionEvent>() {
43     override fun describeTo(description: Description) {
44         description.appendText("With pointer count = $count")
45     }
46 
47     override fun matchesSafely(event: MotionEvent): Boolean {
48         return event.pointerCount == count
49     }
50 }
51 
withCoordsnull52 fun withCoords(pt: Point): Matcher<MotionEvent> = withCoords(PointF(pt))
53 
54 fun withCoordsForPointerIndex(index: Int, pt: PointF): Matcher<MotionEvent> =
55         object : TypeSafeMatcher<MotionEvent>() {
56     override fun describeTo(description: Description) {
57         description.appendText("With coords = $pt for pointer index = $index")
58     }
59 
60     override fun matchesSafely(event: MotionEvent): Boolean {
61         return (abs(event.getX(index)) - pt.x < EPSILON) &&
62                 (abs(event.getY(index)) - pt.y < EPSILON)
63     }
64 }
65 
withRawCoordsnull66 fun withRawCoords(pt: PointF): Matcher<MotionEvent> = object : TypeSafeMatcher<MotionEvent>() {
67     override fun describeTo(description: Description) {
68         description.appendText("With coords = $pt")
69     }
70 
71     override fun matchesSafely(event: MotionEvent): Boolean {
72         return (abs(event.rawX) - pt.x < EPSILON) &&
73                 (abs(event.rawY) - pt.y < EPSILON)
74     }
75 
76     override fun describeMismatchSafely(event: MotionEvent, mismatchDescription: Description) {
77         mismatchDescription.appendText("Got raw coords = {${event.getRawX()}, ${event.getRawY()}}")
78     }
79 }
80 
withMotionActionnull81 fun withMotionAction(action: Int): Matcher<MotionEvent> = object : TypeSafeMatcher<MotionEvent>() {
82     override fun describeTo(description: Description) {
83         description.appendText("With action = ${MotionEvent.actionToString(action)}")
84     }
85 
86     override fun matchesSafely(event: MotionEvent): Boolean {
87         if (action == MotionEvent.ACTION_CANCEL) {
88             if (event.flags and MotionEvent.FLAG_CANCELED != MotionEvent.FLAG_CANCELED) {
89                 return false
90             }
91         }
92         return event.action == action
93     }
94 }
95 
withMotionActionnull96 fun withMotionAction(action: Int, index: Int): Matcher<MotionEvent> =
97         object : TypeSafeMatcher<MotionEvent>() {
98     override fun describeTo(description: Description) {
99         description.appendText(
100             "With action = ${MotionEvent.actionToString(action)}, index = $index"
101         )
102     }
103 
104     override fun matchesSafely(event: MotionEvent): Boolean {
105         if (action != MotionEvent.ACTION_POINTER_DOWN && action != MotionEvent.ACTION_POINTER_UP) {
106             throw Exception(
107                 "Matcher should only be used with ACTION_POINTER_DOWN or ACTION_POINTER_UP"
108             )
109         }
110         return event.actionMasked == action && event.actionIndex == index
111     }
112 }
113 
withRawCoordsnull114 fun withRawCoords(pt: Point): Matcher<MotionEvent> {
115     return withRawCoords(PointF(pt))
116 }
117 
withSourcenull118 fun withSource(source: Int): Matcher<MotionEvent> = object : TypeSafeMatcher<MotionEvent>() {
119     override fun describeTo(description: Description) {
120         description.appendText("With source = $source")
121     }
122 
123     override fun matchesSafely(event: MotionEvent): Boolean {
124         return event.getSource() == source
125     }
126 }
127 
withDeviceIdnull128 fun withDeviceId(deviceId: Int): Matcher<MotionEvent> = object : TypeSafeMatcher<MotionEvent>() {
129     override fun describeTo(description: Description) {
130         description.appendText("With deviceId = $deviceId")
131     }
132 
133     override fun matchesSafely(event: MotionEvent): Boolean {
134         return event.getDeviceId() == deviceId
135     }
136 }
137 
withEventTimenull138 fun withEventTime(eventTime: Long): Matcher<MotionEvent> = object : TypeSafeMatcher<MotionEvent>() {
139     override fun describeTo(description: Description) {
140         description.appendText("With eventTime = $eventTime")
141     }
142 
143     override fun matchesSafely(event: MotionEvent): Boolean {
144         return event.getEventTime() == eventTime
145     }
146 }
147 
withFlagsnull148 fun withFlags(flags: Int): Matcher<MotionEvent> = object : TypeSafeMatcher<MotionEvent>() {
149     override fun describeTo(description: Description) {
150         description.appendText("With flags = $flags")
151     }
152 
153     override fun matchesSafely(event: MotionEvent): Boolean {
154         return event.flags and flags == flags
155     }
156 }
157 
withToolTypenull158 fun withToolType(toolType: Int): Matcher<MotionEvent> = object : TypeSafeMatcher<MotionEvent>() {
159     override fun describeTo(description: Description) {
160         description.appendText("With tool type = $toolType")
161     }
162 
163     override fun matchesSafely(event: MotionEvent): Boolean {
164         for (p in 0..<event.getPointerCount()) {
165             if (event.getToolType(p) != toolType) {
166                 return false
167             }
168         }
169         return true
170     }
171 }
172 
withKeyCodenull173 fun withKeyCode(keyCode: Int): Matcher<KeyEvent> = object : TypeSafeMatcher<KeyEvent>() {
174     override fun describeTo(description: Description) {
175         description.appendText("With key code = " + keyCodeToString(keyCode))
176     }
177 
178     override fun matchesSafely(event: KeyEvent): Boolean {
179         return event.keyCode == keyCode
180     }
181 }
182 
withEdgeFlagsnull183 fun withEdgeFlags(edgeFlags: Int): Matcher<MotionEvent> = object : TypeSafeMatcher<MotionEvent>() {
184     override fun describeTo(description: Description) {
185         description.appendText("With edge flags = 0x${edgeFlags.toString(16)}")
186     }
187 
188     override fun matchesSafely(event: MotionEvent): Boolean {
189         return (event.edgeFlags and edgeFlags) == edgeFlags
190     }
191 }
192 
withDownTimenull193 fun withDownTime(downTime: Long): Matcher<MotionEvent> = object : TypeSafeMatcher<MotionEvent>() {
194     override fun describeTo(description: Description) {
195         description.appendText("With down time = $downTime")
196     }
197 
198     override fun matchesSafely(event: MotionEvent): Boolean {
199         return event.downTime == downTime
200     }
201 }
202 
withMetaStatenull203 fun withMetaState(metaState: Int): Matcher<MotionEvent> = object : TypeSafeMatcher<MotionEvent>() {
204     override fun describeTo(description: Description) {
205         description.appendText("With meta state = 0x${metaState.toString(16)}")
206     }
207 
208     override fun matchesSafely(event: MotionEvent): Boolean {
209         return event.metaState == metaState
210     }
211 }
212 
withPressurenull213 fun withPressure(pressure: Float): Matcher<MotionEvent> = object : TypeSafeMatcher<MotionEvent>() {
214     override fun describeTo(description: Description) {
215         description.appendText("With pressure = $pressure")
216     }
217 
218     override fun matchesSafely(event: MotionEvent): Boolean {
219         return abs(event.pressure - pressure) < EPSILON
220     }
221 }
222 
withSizenull223 fun withSize(size: Float): Matcher<MotionEvent> = object : TypeSafeMatcher<MotionEvent>() {
224     override fun describeTo(description: Description) {
225         description.appendText("With size = $size")
226     }
227 
228     override fun matchesSafely(event: MotionEvent): Boolean {
229         return abs(event.size - size) < EPSILON
230     }
231 }
232 
withXPrecisionnull233 fun withXPrecision(xPrecision: Float):
234         Matcher<MotionEvent> = object : TypeSafeMatcher<MotionEvent>() {
235     override fun describeTo(description: Description) {
236         description.appendText("With xPrecision = $xPrecision")
237     }
238 
239     override fun matchesSafely(event: MotionEvent): Boolean {
240         return abs(event.xPrecision - xPrecision) < EPSILON
241     }
242 }
243 
withYPrecisionnull244 fun withYPrecision(yPrecision: Float):
245         Matcher<MotionEvent> = object : TypeSafeMatcher<MotionEvent>() {
246     override fun describeTo(description: Description) {
247         description.appendText("With yPrecision = $yPrecision")
248     }
249 
250     override fun matchesSafely(event: MotionEvent): Boolean {
251         return abs(event.yPrecision - yPrecision) < EPSILON
252     }
253 }
254