1 /*
<lambda>null2 * 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 com.android.systemui.biometrics.udfps
18
19 import android.graphics.Rect
20 import android.view.MotionEvent
21 import android.view.MotionEvent.INVALID_POINTER_ID
22 import android.view.MotionEvent.PointerProperties
23 import android.view.Surface
24 import android.view.Surface.Rotation
25 import androidx.test.filters.SmallTest
26 import com.android.systemui.SysuiTestCase
27 import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
28 import com.google.common.truth.Truth.assertThat
29 import org.junit.Test
30 import platform.test.runner.parameterized.ParameterizedAndroidJunit4
31 import platform.test.runner.parameterized.Parameters
32 import org.junit.runner.RunWith
33
34 @SmallTest
35 @RunWith(ParameterizedAndroidJunit4::class)
36 class SinglePointerTouchProcessorTest(val testCase: TestCase) : SysuiTestCase() {
37 private val overlapDetector = FakeOverlapDetector()
38 private val underTest = SinglePointerTouchProcessor(overlapDetector)
39
40 @Test
41 fun processTouch() {
42 overlapDetector.shouldReturn =
43 testCase.currentPointers.associate { pointer -> pointer.id to pointer.onSensor }
44
45 val actual =
46 underTest.processTouch(
47 testCase.event,
48 testCase.previousPointerOnSensorId,
49 testCase.overlayParams,
50 )
51
52 assertThat(actual).isInstanceOf(testCase.expected.javaClass)
53 if (actual is TouchProcessorResult.ProcessedTouch) {
54 assertThat(actual).isEqualTo(testCase.expected)
55 }
56 }
57
58 data class TestCase(
59 val event: MotionEvent,
60 val currentPointers: List<TestPointer>,
61 val previousPointerOnSensorId: Int,
62 val overlayParams: UdfpsOverlayParams,
63 val expected: TouchProcessorResult,
64 ) {
65 override fun toString(): String {
66 val expectedOutput =
67 if (expected is TouchProcessorResult.ProcessedTouch) {
68 expected.event.toString() +
69 ", (x: ${expected.touchData.x}, y: ${expected.touchData.y})" +
70 ", pointerOnSensorId: ${expected.pointerOnSensorId}" +
71 ", ..."
72 } else {
73 TouchProcessorResult.Failure().toString()
74 }
75 return "{" +
76 MotionEvent.actionToString(event.action) +
77 ", (x: ${event.x}, y: ${event.y})" +
78 ", scale: ${overlayParams.scaleFactor}" +
79 ", rotation: " +
80 Surface.rotationToString(overlayParams.rotation) +
81 ", previousPointerOnSensorId: $previousPointerOnSensorId" +
82 ", ...} expected: {$expectedOutput}"
83 }
84 }
85
86 companion object {
87 @Parameters(name = "{0}")
88 @JvmStatic
89 fun data(): List<TestCase> =
90 listOf(
91 // MotionEvent.ACTION_DOWN
92 genPositiveTestCases(
93 motionEventAction = MotionEvent.ACTION_DOWN,
94 previousPointerOnSensorId = INVALID_POINTER_ID,
95 currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = true)),
96 expectedInteractionEvent = InteractionEvent.DOWN,
97 expectedPointerOnSensorId = POINTER_ID_1,
98 ),
99 genPositiveTestCases(
100 motionEventAction = MotionEvent.ACTION_DOWN,
101 previousPointerOnSensorId = INVALID_POINTER_ID,
102 currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = false)),
103 expectedInteractionEvent = InteractionEvent.UNCHANGED,
104 expectedPointerOnSensorId = INVALID_POINTER_ID,
105 ),
106 genPositiveTestCases(
107 motionEventAction = MotionEvent.ACTION_DOWN,
108 previousPointerOnSensorId = POINTER_ID_1,
109 currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = false)),
110 expectedInteractionEvent = InteractionEvent.UP,
111 expectedPointerOnSensorId = INVALID_POINTER_ID,
112 ),
113 // MotionEvent.ACTION_HOVER_ENTER
114 genPositiveTestCases(
115 motionEventAction = MotionEvent.ACTION_HOVER_ENTER,
116 previousPointerOnSensorId = INVALID_POINTER_ID,
117 currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = true)),
118 expectedInteractionEvent = InteractionEvent.DOWN,
119 expectedPointerOnSensorId = POINTER_ID_1,
120 ),
121 genPositiveTestCases(
122 motionEventAction = MotionEvent.ACTION_HOVER_ENTER,
123 previousPointerOnSensorId = INVALID_POINTER_ID,
124 currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = false)),
125 expectedInteractionEvent = InteractionEvent.UNCHANGED,
126 expectedPointerOnSensorId = INVALID_POINTER_ID,
127 ),
128 genPositiveTestCases(
129 motionEventAction = MotionEvent.ACTION_HOVER_ENTER,
130 previousPointerOnSensorId = POINTER_ID_1,
131 currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = false)),
132 expectedInteractionEvent = InteractionEvent.UP,
133 expectedPointerOnSensorId = INVALID_POINTER_ID,
134 ),
135 // MotionEvent.ACTION_MOVE
136 genPositiveTestCases(
137 motionEventAction = MotionEvent.ACTION_MOVE,
138 previousPointerOnSensorId = INVALID_POINTER_ID,
139 currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = true)),
140 expectedInteractionEvent = InteractionEvent.DOWN,
141 expectedPointerOnSensorId = POINTER_ID_1,
142 ),
143 genPositiveTestCases(
144 motionEventAction = MotionEvent.ACTION_MOVE,
145 previousPointerOnSensorId = POINTER_ID_1,
146 currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = true)),
147 expectedInteractionEvent = InteractionEvent.UNCHANGED,
148 expectedPointerOnSensorId = POINTER_ID_1,
149 ),
150 genPositiveTestCases(
151 motionEventAction = MotionEvent.ACTION_MOVE,
152 previousPointerOnSensorId = INVALID_POINTER_ID,
153 currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = false)),
154 expectedInteractionEvent = InteractionEvent.UNCHANGED,
155 expectedPointerOnSensorId = INVALID_POINTER_ID,
156 ),
157 genPositiveTestCases(
158 motionEventAction = MotionEvent.ACTION_MOVE,
159 previousPointerOnSensorId = POINTER_ID_1,
160 currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = false)),
161 expectedInteractionEvent = InteractionEvent.UP,
162 expectedPointerOnSensorId = INVALID_POINTER_ID,
163 ),
164 genPositiveTestCases(
165 motionEventAction = MotionEvent.ACTION_MOVE,
166 previousPointerOnSensorId = INVALID_POINTER_ID,
167 currentPointers =
168 listOf(
169 TestPointer(id = POINTER_ID_1, onSensor = false),
170 TestPointer(id = POINTER_ID_2, onSensor = true)
171 ),
172 expectedInteractionEvent = InteractionEvent.DOWN,
173 expectedPointerOnSensorId = POINTER_ID_2,
174 ),
175 genPositiveTestCases(
176 motionEventAction = MotionEvent.ACTION_MOVE,
177 previousPointerOnSensorId = POINTER_ID_1,
178 currentPointers =
179 listOf(
180 TestPointer(id = POINTER_ID_1, onSensor = false),
181 TestPointer(id = POINTER_ID_2, onSensor = true)
182 ),
183 expectedInteractionEvent = InteractionEvent.UNCHANGED,
184 expectedPointerOnSensorId = POINTER_ID_2,
185 ),
186 // MotionEvent.ACTION_HOVER_MOVE
187 genPositiveTestCases(
188 motionEventAction = MotionEvent.ACTION_HOVER_MOVE,
189 previousPointerOnSensorId = INVALID_POINTER_ID,
190 currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = true)),
191 expectedInteractionEvent = InteractionEvent.DOWN,
192 expectedPointerOnSensorId = POINTER_ID_1,
193 ),
194 genPositiveTestCases(
195 motionEventAction = MotionEvent.ACTION_HOVER_MOVE,
196 previousPointerOnSensorId = POINTER_ID_1,
197 currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = true)),
198 expectedInteractionEvent = InteractionEvent.UNCHANGED,
199 expectedPointerOnSensorId = POINTER_ID_1,
200 ),
201 genPositiveTestCases(
202 motionEventAction = MotionEvent.ACTION_HOVER_MOVE,
203 previousPointerOnSensorId = INVALID_POINTER_ID,
204 currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = false)),
205 expectedInteractionEvent = InteractionEvent.UNCHANGED,
206 expectedPointerOnSensorId = INVALID_POINTER_ID,
207 ),
208 genPositiveTestCases(
209 motionEventAction = MotionEvent.ACTION_HOVER_MOVE,
210 previousPointerOnSensorId = POINTER_ID_1,
211 currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = false)),
212 expectedInteractionEvent = InteractionEvent.UP,
213 expectedPointerOnSensorId = INVALID_POINTER_ID,
214 ),
215 // MotionEvent.ACTION_UP
216 genPositiveTestCases(
217 motionEventAction = MotionEvent.ACTION_UP,
218 previousPointerOnSensorId = INVALID_POINTER_ID,
219 currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = true)),
220 expectedInteractionEvent = InteractionEvent.UP,
221 expectedPointerOnSensorId = INVALID_POINTER_ID,
222 ),
223 genPositiveTestCases(
224 motionEventAction = MotionEvent.ACTION_UP,
225 previousPointerOnSensorId = POINTER_ID_1,
226 currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = true)),
227 expectedInteractionEvent = InteractionEvent.UP,
228 expectedPointerOnSensorId = INVALID_POINTER_ID,
229 ),
230 genPositiveTestCases(
231 motionEventAction = MotionEvent.ACTION_UP,
232 previousPointerOnSensorId = INVALID_POINTER_ID,
233 currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = false)),
234 expectedInteractionEvent = InteractionEvent.UNCHANGED,
235 expectedPointerOnSensorId = INVALID_POINTER_ID,
236 ),
237 // MotionEvent.ACTION_HOVER_EXIT
238 genPositiveTestCases(
239 motionEventAction = MotionEvent.ACTION_HOVER_EXIT,
240 previousPointerOnSensorId = INVALID_POINTER_ID,
241 currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = true)),
242 expectedInteractionEvent = InteractionEvent.UP,
243 expectedPointerOnSensorId = INVALID_POINTER_ID,
244 ),
245 genPositiveTestCases(
246 motionEventAction = MotionEvent.ACTION_HOVER_EXIT,
247 previousPointerOnSensorId = POINTER_ID_1,
248 currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = true)),
249 expectedInteractionEvent = InteractionEvent.UP,
250 expectedPointerOnSensorId = INVALID_POINTER_ID,
251 ),
252 genPositiveTestCases(
253 motionEventAction = MotionEvent.ACTION_HOVER_EXIT,
254 previousPointerOnSensorId = INVALID_POINTER_ID,
255 currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = false)),
256 expectedInteractionEvent = InteractionEvent.UNCHANGED,
257 expectedPointerOnSensorId = INVALID_POINTER_ID,
258 ),
259 // MotionEvent.ACTION_CANCEL
260 genPositiveTestCases(
261 motionEventAction = MotionEvent.ACTION_CANCEL,
262 previousPointerOnSensorId = INVALID_POINTER_ID,
263 currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = true)),
264 expectedInteractionEvent = InteractionEvent.CANCEL,
265 expectedPointerOnSensorId = INVALID_POINTER_ID,
266 ),
267 genPositiveTestCases(
268 motionEventAction = MotionEvent.ACTION_CANCEL,
269 previousPointerOnSensorId = POINTER_ID_1,
270 currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = true)),
271 expectedInteractionEvent = InteractionEvent.CANCEL,
272 expectedPointerOnSensorId = INVALID_POINTER_ID,
273 ),
274 genPositiveTestCases(
275 motionEventAction = MotionEvent.ACTION_CANCEL,
276 previousPointerOnSensorId = INVALID_POINTER_ID,
277 currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = false)),
278 expectedInteractionEvent = InteractionEvent.CANCEL,
279 expectedPointerOnSensorId = INVALID_POINTER_ID,
280 ),
281 genPositiveTestCases(
282 motionEventAction = MotionEvent.ACTION_CANCEL,
283 previousPointerOnSensorId = POINTER_ID_1,
284 currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = false)),
285 expectedInteractionEvent = InteractionEvent.CANCEL,
286 expectedPointerOnSensorId = INVALID_POINTER_ID,
287 ),
288 // MotionEvent.ACTION_POINTER_DOWN
289 genPositiveTestCases(
290 motionEventAction =
291 MotionEvent.ACTION_POINTER_DOWN +
292 (1 shl MotionEvent.ACTION_POINTER_INDEX_SHIFT),
293 previousPointerOnSensorId = INVALID_POINTER_ID,
294 currentPointers =
295 listOf(
296 TestPointer(id = POINTER_ID_1, onSensor = true),
297 TestPointer(id = POINTER_ID_2, onSensor = false)
298 ),
299 expectedInteractionEvent = InteractionEvent.DOWN,
300 expectedPointerOnSensorId = POINTER_ID_1,
301 ),
302 genPositiveTestCases(
303 motionEventAction =
304 MotionEvent.ACTION_POINTER_DOWN +
305 (1 shl MotionEvent.ACTION_POINTER_INDEX_SHIFT),
306 previousPointerOnSensorId = INVALID_POINTER_ID,
307 currentPointers =
308 listOf(
309 TestPointer(id = POINTER_ID_1, onSensor = false),
310 TestPointer(id = POINTER_ID_2, onSensor = true)
311 ),
312 expectedInteractionEvent = InteractionEvent.DOWN,
313 expectedPointerOnSensorId = POINTER_ID_2
314 ),
315 genPositiveTestCases(
316 motionEventAction =
317 MotionEvent.ACTION_POINTER_DOWN +
318 (1 shl MotionEvent.ACTION_POINTER_INDEX_SHIFT),
319 previousPointerOnSensorId = POINTER_ID_1,
320 currentPointers =
321 listOf(
322 TestPointer(id = POINTER_ID_1, onSensor = true),
323 TestPointer(id = POINTER_ID_2, onSensor = false)
324 ),
325 expectedInteractionEvent = InteractionEvent.UNCHANGED,
326 expectedPointerOnSensorId = POINTER_ID_1,
327 ),
328 // MotionEvent.ACTION_POINTER_UP
329 genPositiveTestCases(
330 motionEventAction =
331 MotionEvent.ACTION_POINTER_UP +
332 (1 shl MotionEvent.ACTION_POINTER_INDEX_SHIFT),
333 previousPointerOnSensorId = INVALID_POINTER_ID,
334 currentPointers =
335 listOf(
336 TestPointer(id = POINTER_ID_1, onSensor = false),
337 TestPointer(id = POINTER_ID_2, onSensor = false)
338 ),
339 expectedInteractionEvent = InteractionEvent.UNCHANGED,
340 expectedPointerOnSensorId = INVALID_POINTER_ID
341 ),
342 genPositiveTestCases(
343 motionEventAction =
344 MotionEvent.ACTION_POINTER_UP +
345 (1 shl MotionEvent.ACTION_POINTER_INDEX_SHIFT),
346 previousPointerOnSensorId = POINTER_ID_2,
347 currentPointers =
348 listOf(
349 TestPointer(id = POINTER_ID_1, onSensor = false),
350 TestPointer(id = POINTER_ID_2, onSensor = true)
351 ),
352 expectedInteractionEvent = InteractionEvent.UP,
353 expectedPointerOnSensorId = INVALID_POINTER_ID
354 ),
355 genPositiveTestCases(
356 motionEventAction = MotionEvent.ACTION_POINTER_UP,
357 previousPointerOnSensorId = POINTER_ID_1,
358 currentPointers =
359 listOf(
360 TestPointer(id = POINTER_ID_1, onSensor = true),
361 TestPointer(id = POINTER_ID_2, onSensor = false)
362 ),
363 expectedInteractionEvent = InteractionEvent.UP,
364 expectedPointerOnSensorId = INVALID_POINTER_ID
365 ),
366 genPositiveTestCases(
367 motionEventAction =
368 MotionEvent.ACTION_POINTER_UP +
369 (1 shl MotionEvent.ACTION_POINTER_INDEX_SHIFT),
370 previousPointerOnSensorId = POINTER_ID_1,
371 currentPointers =
372 listOf(
373 TestPointer(id = POINTER_ID_1, onSensor = true),
374 TestPointer(id = POINTER_ID_2, onSensor = false)
375 ),
376 expectedInteractionEvent = InteractionEvent.UNCHANGED,
377 expectedPointerOnSensorId = POINTER_ID_1
378 ),
379 genPositiveTestCases(
380 motionEventAction = MotionEvent.ACTION_POINTER_UP,
381 previousPointerOnSensorId = POINTER_ID_2,
382 currentPointers =
383 listOf(
384 TestPointer(id = POINTER_ID_1, onSensor = false),
385 TestPointer(id = POINTER_ID_2, onSensor = true)
386 ),
387 expectedInteractionEvent = InteractionEvent.UNCHANGED,
388 expectedPointerOnSensorId = POINTER_ID_2
389 )
390 )
391 .flatten()
392 }
393 }
394
395 data class TestPointer(val id: Int, val onSensor: Boolean)
396
397 /* Display dimensions in native resolution and natural orientation. */
398 private const val ROTATION_0_NATIVE_DISPLAY_WIDTH = 400
399 private const val ROTATION_0_NATIVE_DISPLAY_HEIGHT = 600
400
401 /* Placeholder touch parameters. */
402 private const val POINTER_ID_1 = 42
403 private const val POINTER_ID_2 = 43
404 private const val NATIVE_MINOR = 2.71828f
405 private const val NATIVE_MAJOR = 3.14f
406 private const val ORIENTATION = 1.2345f
407 private const val TIME = 12345699L
408 private const val GESTURE_START = 12345600L
409
410 /*
411 * ROTATION_0 map:
412 * _ _ _ _
413 * _ _ O _
414 * _ _ _ _
415 * _ S _ _
416 * _ S _ _
417 * _ _ _ _
418 *
419 * (_) empty space
420 * (S) sensor
421 * (O) touch outside of the sensor
422 */
423 private val ROTATION_0_NATIVE_SENSOR_BOUNDS =
424 Rect(
425 100, /* left */
426 300, /* top */
427 200, /* right */
428 500, /* bottom */
429 )
430 private val ROTATION_0_INPUTS =
431 OrientationBasedInputs(
432 rotation = Surface.ROTATION_0,
433 nativeOrientation = ORIENTATION,
434 nativeXWithinSensor = ROTATION_0_NATIVE_SENSOR_BOUNDS.exactCenterX(),
435 nativeYWithinSensor = ROTATION_0_NATIVE_SENSOR_BOUNDS.exactCenterY(),
436 nativeXOutsideSensor = 250f,
437 nativeYOutsideSensor = 150f,
438 )
439
440 /*
441 * ROTATION_90 map:
442 * _ _ _ _ _ _
443 * _ O _ _ _ _
444 * _ _ _ S S _
445 * _ _ _ _ _ _
446 *
447 * (_) empty space
448 * (S) sensor
449 * (O) touch outside of the sensor
450 */
451 private val ROTATION_90_NATIVE_SENSOR_BOUNDS =
452 Rect(
453 300, /* left */
454 200, /* top */
455 500, /* right */
456 300, /* bottom */
457 )
458 private val ROTATION_90_INPUTS =
459 OrientationBasedInputs(
460 rotation = Surface.ROTATION_90,
461 nativeOrientation = (ORIENTATION - Math.PI.toFloat() / 2),
462 nativeXWithinSensor = ROTATION_90_NATIVE_SENSOR_BOUNDS.exactCenterX(),
463 nativeYWithinSensor = ROTATION_90_NATIVE_SENSOR_BOUNDS.exactCenterY(),
464 nativeXOutsideSensor = 150f,
465 nativeYOutsideSensor = 150f,
466 )
467
468 /*
469 * ROTATION_180 map:
470 * _ _ _ _
471 * _ _ s _
472 * _ _ s _
473 * _ _ _ _
474 * _ O _ _
475 * _ _ _ _
476 *
477 * (_) empty space
478 * (S) sensor
479 * (O) touch outside of the sensor
480 */
481 private val ROTATION_180_NATIVE_SENSOR_BOUNDS =
482 Rect(
483 200, /* left */
484 100, /* top */
485 300, /* right */
486 300, /* bottom */
487 )
488 private val ROTATION_180_INPUTS =
489 OrientationBasedInputs(
490 rotation = Surface.ROTATION_180,
491 nativeOrientation = (ORIENTATION - Math.PI.toFloat() / 2),
492 nativeXWithinSensor = ROTATION_180_NATIVE_SENSOR_BOUNDS.exactCenterX(),
493 nativeYWithinSensor = ROTATION_180_NATIVE_SENSOR_BOUNDS.exactCenterY(),
494 nativeXOutsideSensor = 150f,
495 nativeYOutsideSensor = 450f,
496 )
497
498 /*
499 * ROTATION_270 map:
500 * _ _ _ _ _ _
501 * _ S S _ _ _
502 * _ _ _ _ O _
503 * _ _ _ _ _ _
504 *
505 * (_) empty space
506 * (S) sensor
507 * (O) touch outside of the sensor
508 */
509 private val ROTATION_270_NATIVE_SENSOR_BOUNDS =
510 Rect(
511 100, /* left */
512 100, /* top */
513 300, /* right */
514 200, /* bottom */
515 )
516 private val ROTATION_270_INPUTS =
517 OrientationBasedInputs(
518 rotation = Surface.ROTATION_270,
519 nativeOrientation = (ORIENTATION + Math.PI.toFloat() / 2),
520 nativeXWithinSensor = ROTATION_270_NATIVE_SENSOR_BOUNDS.exactCenterX(),
521 nativeYWithinSensor = ROTATION_270_NATIVE_SENSOR_BOUNDS.exactCenterY(),
522 nativeXOutsideSensor = 450f,
523 nativeYOutsideSensor = 250f,
524 )
525
526 /* Template [MotionEvent]. */
527 private val MOTION_EVENT =
528 obtainMotionEvent(
529 action = 0,
530 pointerId = POINTER_ID_1,
531 x = 0f,
532 y = 0f,
533 minor = 0f,
534 major = 0f,
535 orientation = ORIENTATION,
536 time = TIME,
537 gestureStart = GESTURE_START,
538 )
539
540 /* Template [NormalizedTouchData]. */
541 private val NORMALIZED_TOUCH_DATA =
542 NormalizedTouchData(
543 POINTER_ID_1,
544 x = 0f,
545 y = 0f,
546 NATIVE_MINOR,
547 NATIVE_MAJOR,
548 ORIENTATION,
549 TIME,
550 GESTURE_START
551 )
552
553 /*
554 * Contains test inputs that are tied to a particular device orientation.
555 *
556 * "native" means in native resolution (not scaled).
557 */
558 private data class OrientationBasedInputs(
559 @Rotation val rotation: Int,
560 val nativeOrientation: Float,
561 val nativeXWithinSensor: Float,
562 val nativeYWithinSensor: Float,
563 val nativeXOutsideSensor: Float,
564 val nativeYOutsideSensor: Float,
565 ) {
566
toOverlayParamsnull567 fun toOverlayParams(scaleFactor: Float): UdfpsOverlayParams =
568 UdfpsOverlayParams(
569 sensorBounds = ROTATION_0_NATIVE_SENSOR_BOUNDS.scaled(scaleFactor),
570 overlayBounds = ROTATION_0_NATIVE_SENSOR_BOUNDS.scaled(scaleFactor),
571 naturalDisplayHeight = (ROTATION_0_NATIVE_DISPLAY_HEIGHT * scaleFactor).toInt(),
572 naturalDisplayWidth = (ROTATION_0_NATIVE_DISPLAY_WIDTH * scaleFactor).toInt(),
573 scaleFactor = scaleFactor,
574 rotation = rotation
575 )
576
577 fun getNativeX(isWithinSensor: Boolean): Float {
578 return if (isWithinSensor) nativeXWithinSensor else nativeXOutsideSensor
579 }
580
getNativeYnull581 fun getNativeY(isWithinSensor: Boolean): Float {
582 return if (isWithinSensor) nativeYWithinSensor else nativeYOutsideSensor
583 }
584 }
585
genPositiveTestCasesnull586 private fun genPositiveTestCases(
587 motionEventAction: Int,
588 previousPointerOnSensorId: Int,
589 currentPointers: List<TestPointer>,
590 expectedInteractionEvent: InteractionEvent,
591 expectedPointerOnSensorId: Int
592 ): List<SinglePointerTouchProcessorTest.TestCase> {
593 val scaleFactors = listOf(0.75f, 1f, 1.5f)
594 val orientations =
595 listOf(
596 ROTATION_0_INPUTS,
597 ROTATION_90_INPUTS,
598 ROTATION_180_INPUTS,
599 ROTATION_270_INPUTS,
600 )
601 return scaleFactors.flatMap { scaleFactor ->
602 orientations.map { orientation ->
603 val overlayParams = orientation.toOverlayParams(scaleFactor)
604
605 val pointerProperties =
606 currentPointers
607 .map { pointer ->
608 val pp = MotionEvent.PointerProperties()
609 pp.id = pointer.id
610 pp
611 }
612 .toList()
613
614 val pointerCoords =
615 currentPointers
616 .map { pointer ->
617 val pc = MotionEvent.PointerCoords()
618 pc.x = orientation.getNativeX(pointer.onSensor) * scaleFactor
619 pc.y = orientation.getNativeY(pointer.onSensor) * scaleFactor
620 pc.touchMinor = NATIVE_MINOR * scaleFactor
621 pc.touchMajor = NATIVE_MAJOR * scaleFactor
622 pc.orientation = orientation.nativeOrientation
623 pc
624 }
625 .toList()
626
627 val event =
628 MOTION_EVENT.copy(
629 action = motionEventAction,
630 pointerProperties = pointerProperties,
631 pointerCoords = pointerCoords
632 )
633
634 val expectedTouchDataPointer =
635 currentPointers.find { it.id == expectedPointerOnSensorId }
636 ?: currentPointers.find { it.id == previousPointerOnSensorId }
637 ?: currentPointers[0]
638 val expectedTouchData =
639 if (motionEventAction != MotionEvent.ACTION_CANCEL) {
640 NORMALIZED_TOUCH_DATA.copy(
641 pointerId = expectedTouchDataPointer.id,
642 x = ROTATION_0_INPUTS.getNativeX(expectedTouchDataPointer.onSensor),
643 y = ROTATION_0_INPUTS.getNativeY(expectedTouchDataPointer.onSensor)
644 )
645 } else {
646 NormalizedTouchData()
647 }
648
649 val expected =
650 TouchProcessorResult.ProcessedTouch(
651 event = expectedInteractionEvent,
652 pointerOnSensorId = expectedPointerOnSensorId,
653 touchData = expectedTouchData,
654 )
655 SinglePointerTouchProcessorTest.TestCase(
656 event = event,
657 currentPointers = currentPointers,
658 previousPointerOnSensorId = previousPointerOnSensorId,
659 overlayParams = overlayParams,
660 expected = expected,
661 )
662 }
663 }
664 }
665
obtainMotionEventnull666 private fun obtainMotionEvent(
667 action: Int,
668 pointerId: Int,
669 x: Float,
670 y: Float,
671 minor: Float,
672 major: Float,
673 orientation: Float,
674 time: Long,
675 gestureStart: Long,
676 ): MotionEvent {
677 val pp = PointerProperties()
678 pp.id = pointerId
679 val pc = MotionEvent.PointerCoords()
680 pc.x = x
681 pc.y = y
682 pc.touchMinor = minor
683 pc.touchMajor = major
684 pc.orientation = orientation
685 return obtainMotionEvent(action, arrayOf(pp), arrayOf(pc), time, gestureStart)
686 }
687
obtainMotionEventnull688 private fun obtainMotionEvent(
689 action: Int,
690 pointerProperties: Array<MotionEvent.PointerProperties>,
691 pointerCoords: Array<MotionEvent.PointerCoords>,
692 time: Long,
693 gestureStart: Long,
694 ): MotionEvent {
695 return MotionEvent.obtain(
696 gestureStart /* downTime */,
697 time /* eventTime */,
698 action /* action */,
699 pointerCoords.size /* pointerCount */,
700 pointerProperties /* pointerProperties */,
701 pointerCoords /* pointerCoords */,
702 0 /* metaState */,
703 0 /* buttonState */,
704 1f /* xPrecision */,
705 1f /* yPrecision */,
706 0 /* deviceId */,
707 0 /* edgeFlags */,
708 0 /* source */,
709 0 /* flags */
710 )
711 }
712
copynull713 private fun MotionEvent.copy(
714 action: Int = this.action,
715 pointerId: Int = this.getPointerId(0),
716 x: Float = this.rawX,
717 y: Float = this.rawY,
718 minor: Float = this.touchMinor,
719 major: Float = this.touchMajor,
720 orientation: Float = this.orientation,
721 time: Long = this.eventTime,
722 gestureStart: Long = this.downTime,
723 ) = obtainMotionEvent(action, pointerId, x, y, minor, major, orientation, time, gestureStart)
724
725 private fun MotionEvent.copy(
726 action: Int = this.action,
727 pointerProperties: List<MotionEvent.PointerProperties>,
728 pointerCoords: List<MotionEvent.PointerCoords>,
729 time: Long = this.eventTime,
730 gestureStart: Long = this.downTime
731 ) =
732 obtainMotionEvent(
733 action,
734 pointerProperties.toTypedArray(),
735 pointerCoords.toTypedArray(),
736 time,
737 gestureStart
738 )
739
740 private fun Rect.scaled(scaleFactor: Float) = Rect(this).apply { scale(scaleFactor) }
741