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 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.settingslib.udfps.UdfpsOverlayParams 27 import com.android.systemui.SysuiTestCase 28 import com.google.common.truth.Truth.assertThat 29 import org.junit.Test 30 import org.junit.runner.RunWith 31 import org.junit.runners.Parameterized 32 import org.junit.runners.Parameterized.Parameters 33 34 @SmallTest 35 @RunWith(Parameterized::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 /* ROTATION_180 is not supported. It's treated the same as ROTATION_0. */ 469 private val ROTATION_180_INPUTS = 470 ROTATION_0_INPUTS.copy( 471 rotation = Surface.ROTATION_180, 472 ) 473 474 /* 475 * ROTATION_270 map: 476 * _ _ _ _ _ _ 477 * _ S S _ _ _ 478 * _ _ _ _ O _ 479 * _ _ _ _ _ _ 480 * 481 * (_) empty space 482 * (S) sensor 483 * (O) touch outside of the sensor 484 */ 485 private val ROTATION_270_NATIVE_SENSOR_BOUNDS = 486 Rect( 487 100, /* left */ 488 100, /* top */ 489 300, /* right */ 490 200, /* bottom */ 491 ) 492 private val ROTATION_270_INPUTS = 493 OrientationBasedInputs( 494 rotation = Surface.ROTATION_270, 495 nativeOrientation = (ORIENTATION + Math.PI.toFloat() / 2), 496 nativeXWithinSensor = ROTATION_270_NATIVE_SENSOR_BOUNDS.exactCenterX(), 497 nativeYWithinSensor = ROTATION_270_NATIVE_SENSOR_BOUNDS.exactCenterY(), 498 nativeXOutsideSensor = 450f, 499 nativeYOutsideSensor = 250f, 500 ) 501 502 /* Template [MotionEvent]. */ 503 private val MOTION_EVENT = 504 obtainMotionEvent( 505 action = 0, 506 pointerId = POINTER_ID_1, 507 x = 0f, 508 y = 0f, 509 minor = 0f, 510 major = 0f, 511 orientation = ORIENTATION, 512 time = TIME, 513 gestureStart = GESTURE_START, 514 ) 515 516 /* Template [NormalizedTouchData]. */ 517 private val NORMALIZED_TOUCH_DATA = 518 NormalizedTouchData( 519 POINTER_ID_1, 520 x = 0f, 521 y = 0f, 522 NATIVE_MINOR, 523 NATIVE_MAJOR, 524 ORIENTATION, 525 TIME, 526 GESTURE_START 527 ) 528 529 /* 530 * Contains test inputs that are tied to a particular device orientation. 531 * 532 * "native" means in native resolution (not scaled). 533 */ 534 private data class OrientationBasedInputs( 535 @Rotation val rotation: Int, 536 val nativeOrientation: Float, 537 val nativeXWithinSensor: Float, 538 val nativeYWithinSensor: Float, 539 val nativeXOutsideSensor: Float, 540 val nativeYOutsideSensor: Float, 541 ) { 542 543 fun toOverlayParams(scaleFactor: Float): UdfpsOverlayParams = 544 UdfpsOverlayParams( 545 sensorBounds = ROTATION_0_NATIVE_SENSOR_BOUNDS.scaled(scaleFactor), 546 overlayBounds = ROTATION_0_NATIVE_SENSOR_BOUNDS.scaled(scaleFactor), 547 naturalDisplayHeight = (ROTATION_0_NATIVE_DISPLAY_HEIGHT * scaleFactor).toInt(), 548 naturalDisplayWidth = (ROTATION_0_NATIVE_DISPLAY_WIDTH * scaleFactor).toInt(), 549 scaleFactor = scaleFactor, 550 rotation = rotation 551 ) 552 553 fun getNativeX(isWithinSensor: Boolean): Float { 554 return if (isWithinSensor) nativeXWithinSensor else nativeXOutsideSensor 555 } 556 557 fun getNativeY(isWithinSensor: Boolean): Float { 558 return if (isWithinSensor) nativeYWithinSensor else nativeYOutsideSensor 559 } 560 } 561 562 private fun genPositiveTestCases( 563 motionEventAction: Int, 564 previousPointerOnSensorId: Int, 565 currentPointers: List<TestPointer>, 566 expectedInteractionEvent: InteractionEvent, 567 expectedPointerOnSensorId: Int 568 ): List<SinglePointerTouchProcessorTest.TestCase> { 569 val scaleFactors = listOf(0.75f, 1f, 1.5f) 570 val orientations = 571 listOf( 572 ROTATION_0_INPUTS, 573 ROTATION_90_INPUTS, 574 ROTATION_180_INPUTS, 575 ROTATION_270_INPUTS, 576 ) 577 return scaleFactors.flatMap { scaleFactor -> 578 orientations.map { orientation -> 579 val overlayParams = orientation.toOverlayParams(scaleFactor) 580 581 val pointerProperties = 582 currentPointers 583 .map { pointer -> 584 val pp = MotionEvent.PointerProperties() 585 pp.id = pointer.id 586 pp 587 } 588 .toList() 589 590 val pointerCoords = 591 currentPointers 592 .map { pointer -> 593 val pc = MotionEvent.PointerCoords() 594 pc.x = orientation.getNativeX(pointer.onSensor) * scaleFactor 595 pc.y = orientation.getNativeY(pointer.onSensor) * scaleFactor 596 pc.touchMinor = NATIVE_MINOR * scaleFactor 597 pc.touchMajor = NATIVE_MAJOR * scaleFactor 598 pc.orientation = orientation.nativeOrientation 599 pc 600 } 601 .toList() 602 603 val event = 604 MOTION_EVENT.copy( 605 action = motionEventAction, 606 pointerProperties = pointerProperties, 607 pointerCoords = pointerCoords 608 ) 609 610 val expectedTouchDataPointer = 611 currentPointers.find { it.id == expectedPointerOnSensorId } 612 ?: currentPointers.find { it.id == previousPointerOnSensorId } 613 ?: currentPointers[0] 614 val expectedTouchData = 615 if (motionEventAction != MotionEvent.ACTION_CANCEL) { 616 NORMALIZED_TOUCH_DATA.copy( 617 pointerId = expectedTouchDataPointer.id, 618 x = ROTATION_0_INPUTS.getNativeX(expectedTouchDataPointer.onSensor), 619 y = ROTATION_0_INPUTS.getNativeY(expectedTouchDataPointer.onSensor) 620 ) 621 } else { 622 NormalizedTouchData() 623 } 624 625 val expected = 626 TouchProcessorResult.ProcessedTouch( 627 event = expectedInteractionEvent, 628 pointerOnSensorId = expectedPointerOnSensorId, 629 touchData = expectedTouchData, 630 ) 631 SinglePointerTouchProcessorTest.TestCase( 632 event = event, 633 currentPointers = currentPointers, 634 previousPointerOnSensorId = previousPointerOnSensorId, 635 overlayParams = overlayParams, 636 expected = expected, 637 ) 638 } 639 } 640 } 641 642 private fun genTestCasesForUnsupportedAction( 643 motionEventAction: Int 644 ): List<SinglePointerTouchProcessorTest.TestCase> { 645 val isGoodOverlap = true 646 val previousPointerOnSensorIds = listOf(INVALID_POINTER_ID, POINTER_ID_1) 647 return previousPointerOnSensorIds.map { previousPointerOnSensorId -> 648 val overlayParams = ROTATION_0_INPUTS.toOverlayParams(scaleFactor = 1f) 649 val nativeX = ROTATION_0_INPUTS.getNativeX(isGoodOverlap) 650 val nativeY = ROTATION_0_INPUTS.getNativeY(isGoodOverlap) 651 val event = 652 MOTION_EVENT.copy( 653 action = motionEventAction, 654 x = nativeX, 655 y = nativeY, 656 minor = NATIVE_MINOR, 657 major = NATIVE_MAJOR, 658 ) 659 SinglePointerTouchProcessorTest.TestCase( 660 event = event, 661 currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = isGoodOverlap)), 662 previousPointerOnSensorId = previousPointerOnSensorId, 663 overlayParams = overlayParams, 664 expected = TouchProcessorResult.Failure(), 665 ) 666 } 667 } 668 669 private fun obtainMotionEvent( 670 action: Int, 671 pointerId: Int, 672 x: Float, 673 y: Float, 674 minor: Float, 675 major: Float, 676 orientation: Float, 677 time: Long, 678 gestureStart: Long, 679 ): MotionEvent { 680 val pp = PointerProperties() 681 pp.id = pointerId 682 val pc = MotionEvent.PointerCoords() 683 pc.x = x 684 pc.y = y 685 pc.touchMinor = minor 686 pc.touchMajor = major 687 pc.orientation = orientation 688 return obtainMotionEvent(action, arrayOf(pp), arrayOf(pc), time, gestureStart) 689 } 690 691 private fun obtainMotionEvent( 692 action: Int, 693 pointerProperties: Array<MotionEvent.PointerProperties>, 694 pointerCoords: Array<MotionEvent.PointerCoords>, 695 time: Long, 696 gestureStart: Long, 697 ): MotionEvent { 698 return MotionEvent.obtain( 699 gestureStart /* downTime */, 700 time /* eventTime */, 701 action /* action */, 702 pointerCoords.size /* pointerCount */, 703 pointerProperties /* pointerProperties */, 704 pointerCoords /* pointerCoords */, 705 0 /* metaState */, 706 0 /* buttonState */, 707 1f /* xPrecision */, 708 1f /* yPrecision */, 709 0 /* deviceId */, 710 0 /* edgeFlags */, 711 0 /* source */, 712 0 /* flags */ 713 ) 714 } 715 716 private fun MotionEvent.copy( 717 action: Int = this.action, 718 pointerId: Int = this.getPointerId(0), 719 x: Float = this.rawX, 720 y: Float = this.rawY, 721 minor: Float = this.touchMinor, 722 major: Float = this.touchMajor, 723 orientation: Float = this.orientation, 724 time: Long = this.eventTime, 725 gestureStart: Long = this.downTime, 726 ) = obtainMotionEvent(action, pointerId, x, y, minor, major, orientation, time, gestureStart) 727 728 private fun MotionEvent.copy( 729 action: Int = this.action, 730 pointerProperties: List<MotionEvent.PointerProperties>, 731 pointerCoords: List<MotionEvent.PointerCoords>, 732 time: Long = this.eventTime, 733 gestureStart: Long = this.downTime 734 ) = 735 obtainMotionEvent( 736 action, 737 pointerProperties.toTypedArray(), 738 pointerCoords.toTypedArray(), 739 time, 740 gestureStart 741 ) 742 743 private fun Rect.scaled(scaleFactor: Float) = Rect(this).apply { scale(scaleFactor) } 744