1 package android.view; 2 3 import static android.os.Build.VERSION_CODES.N; 4 import static androidx.test.ext.truth.view.MotionEventSubject.assertThat; 5 import static androidx.test.ext.truth.view.PointerCoordsSubject.assertThat; 6 import static androidx.test.ext.truth.view.PointerPropertiesSubject.assertThat; 7 import static com.google.common.truth.Truth.assertThat; 8 import static org.junit.Assert.fail; 9 10 import android.graphics.Matrix; 11 import android.os.Build; 12 import android.os.Build.VERSION_CODES; 13 import android.os.Parcel; 14 import android.os.Parcelable; 15 import android.os.SystemClock; 16 import android.view.MotionEvent.PointerCoords; 17 import android.view.MotionEvent.PointerProperties; 18 import androidx.test.core.view.PointerCoordsBuilder; 19 import androidx.test.core.view.PointerPropertiesBuilder; 20 import androidx.test.ext.junit.runners.AndroidJUnit4; 21 import androidx.test.filters.SdkSuppress; 22 import com.google.common.truth.FailureMetadata; 23 import com.google.common.truth.Subject; 24 import com.google.common.truth.Truth; 25 import org.junit.After; 26 import org.junit.Before; 27 import org.junit.Test; 28 import org.junit.runner.RunWith; 29 import org.robolectric.annotation.internal.DoNotInstrument; 30 31 /** 32 * Test {@link android.view.MotionEvent}. 33 * 34 * <p>Baselined from Android cts/tests/tests/view/src/android/view/cts/MotionEventTest.java 35 */ 36 @DoNotInstrument 37 @RunWith(AndroidJUnit4.class) 38 public class MotionEventTest { 39 private MotionEvent motionEvent1; 40 private MotionEvent motionEvent2; 41 private MotionEvent motionEventDynamic; 42 private long downTime; 43 private long eventTime; 44 private static final float X_3F = 3.0f; 45 private static final float Y_4F = 4.0f; 46 private static final int META_STATE = KeyEvent.META_SHIFT_ON; 47 private static final float PRESSURE_1F = 1.0f; 48 private static final float SIZE_1F = 1.0f; 49 private static final float X_PRECISION_3F = 3.0f; 50 private static final float Y_PRECISION_4F = 4.0f; 51 private static final int DEVICE_ID_1 = 1; 52 private static final int EDGE_FLAGS = MotionEvent.EDGE_TOP; 53 private static final float TOLERANCE = 0.01f; 54 55 @Before setup()56 public void setup() { 57 downTime = SystemClock.uptimeMillis(); 58 eventTime = SystemClock.uptimeMillis(); 59 motionEvent1 = 60 MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE, X_3F, Y_4F, META_STATE); 61 motionEvent2 = 62 MotionEvent.obtain( 63 downTime, 64 eventTime, 65 MotionEvent.ACTION_MOVE, 66 X_3F, 67 Y_4F, 68 PRESSURE_1F, 69 SIZE_1F, 70 META_STATE, 71 X_PRECISION_3F, 72 Y_PRECISION_4F, 73 DEVICE_ID_1, 74 EDGE_FLAGS); 75 } 76 77 @After teardown()78 public void teardown() { 79 if (null != motionEvent1) { 80 motionEvent1.recycle(); 81 } 82 if (null != motionEvent2) { 83 motionEvent2.recycle(); 84 } 85 if (null != motionEventDynamic) { 86 motionEventDynamic.recycle(); 87 } 88 } 89 90 @Test obtainBasic()91 public void obtainBasic() { 92 motionEvent1 = 93 MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_DOWN, X_3F, Y_4F, META_STATE); 94 assertThat(motionEvent1).isNotNull(); 95 assertThat(motionEvent1).hasDownTime(downTime); 96 assertThat(motionEvent1).hasEventTime(eventTime); 97 assertThat(motionEvent1).hasAction(MotionEvent.ACTION_DOWN); 98 assertThat(motionEvent1).x().isWithin(TOLERANCE).of(X_3F); 99 assertThat(motionEvent1).y().isWithin(TOLERANCE).of(Y_4F); 100 assertThat(motionEvent1).rawX().isWithin(TOLERANCE).of(X_3F); 101 assertThat(motionEvent1).rawY().isWithin(TOLERANCE).of(Y_4F); 102 assertThat(motionEvent1).hasMetaState(META_STATE); 103 assertThat(motionEvent1).hasDeviceId(0); 104 assertThat(motionEvent1).hasEdgeFlags(0); 105 assertThat(motionEvent1).pressure().isWithin(TOLERANCE).of(PRESSURE_1F); 106 assertThat(motionEvent1).size().isWithin(TOLERANCE).of(SIZE_1F); 107 assertThat(motionEvent1).xPrecision().isWithin(TOLERANCE).of(1.0f); 108 assertThat(motionEvent1).yPrecision().isWithin(TOLERANCE).of(1.0f); 109 } 110 111 @Test testObtainFromMotionEvent()112 public void testObtainFromMotionEvent() { 113 motionEventDynamic = MotionEvent.obtain(motionEvent2); 114 assertThat(motionEventDynamic).isNotNull(); 115 MotionEventEqualitySubject.assertThat(motionEventDynamic) 116 .isEqualToWithinTolerance(motionEvent2, TOLERANCE); 117 } 118 119 @Test testObtainAllFields()120 public void testObtainAllFields() { 121 motionEventDynamic = 122 MotionEvent.obtain( 123 downTime, 124 eventTime, 125 MotionEvent.ACTION_DOWN, 126 X_3F, 127 Y_4F, 128 PRESSURE_1F, 129 SIZE_1F, 130 META_STATE, 131 X_PRECISION_3F, 132 Y_PRECISION_4F, 133 DEVICE_ID_1, 134 EDGE_FLAGS); 135 assertThat(motionEventDynamic).isNotNull(); 136 assertThat(motionEventDynamic).hasButtonState(0); 137 assertThat(motionEventDynamic).hasDownTime(downTime); 138 assertThat(motionEventDynamic).hasEventTime(eventTime); 139 assertThat(motionEventDynamic).hasAction(MotionEvent.ACTION_DOWN); 140 assertThat(motionEventDynamic).x().isWithin(TOLERANCE).of(X_3F); 141 assertThat(motionEventDynamic).y().isWithin(TOLERANCE).of(Y_4F); 142 assertThat(motionEventDynamic).rawX().isWithin(TOLERANCE).of(X_3F); 143 assertThat(motionEventDynamic).rawY().isWithin(TOLERANCE).of(Y_4F); 144 assertThat(motionEventDynamic).hasMetaState(META_STATE); 145 assertThat(motionEventDynamic).hasDeviceId(DEVICE_ID_1); 146 assertThat(motionEventDynamic).hasEdgeFlags(EDGE_FLAGS); 147 assertThat(motionEventDynamic).pressure().isWithin(TOLERANCE).of(PRESSURE_1F); 148 assertThat(motionEventDynamic).size().isWithin(TOLERANCE).of(SIZE_1F); 149 assertThat(motionEventDynamic).xPrecision().isWithin(TOLERANCE).of(X_PRECISION_3F); 150 assertThat(motionEventDynamic).yPrecision().isWithin(TOLERANCE).of(Y_PRECISION_4F); 151 } 152 153 @Test actionButton()154 public void actionButton() { 155 MotionEvent event = 156 MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_DOWN, X_3F, Y_4F, META_STATE); 157 if (Build.VERSION.SDK_INT < VERSION_CODES.M) { 158 try { 159 assertThat(event).hasActionButton(0); 160 fail("IllegalStateException not thrown"); 161 } catch (IllegalStateException e) { 162 // expected 163 } 164 } else { 165 assertThat(event).hasActionButton(0); 166 } 167 } 168 169 @Test testObtainFromRecycledEvent()170 public void testObtainFromRecycledEvent() { 171 PointerCoords coords0 = 172 PointerCoordsBuilder.newBuilder() 173 .setCoords(X_3F, Y_4F) 174 .setPressure(PRESSURE_1F) 175 .setSize(SIZE_1F) 176 .setTool(1.2f, 1.4f) 177 .build(); 178 PointerProperties properties0 = 179 PointerPropertiesBuilder.newBuilder() 180 .setId(0) 181 .setToolType(MotionEvent.TOOL_TYPE_FINGER) 182 .build(); 183 motionEventDynamic = 184 MotionEvent.obtain( 185 downTime, 186 eventTime, 187 MotionEvent.ACTION_MOVE, 188 1, 189 new PointerProperties[] {properties0}, 190 new PointerCoords[] {coords0}, 191 META_STATE, 192 0, 193 X_PRECISION_3F, 194 Y_PRECISION_4F, 195 DEVICE_ID_1, 196 EDGE_FLAGS, 197 InputDevice.SOURCE_TOUCHSCREEN, 198 0); 199 MotionEvent motionEventDynamicCopy = MotionEvent.obtain(motionEventDynamic); 200 assertThat(motionEventDynamic.getToolType(0)).isEqualTo(MotionEvent.TOOL_TYPE_FINGER); 201 assertThat(motionEventDynamicCopy.getToolType(0)).isEqualTo(MotionEvent.TOOL_TYPE_FINGER); 202 motionEventDynamic.recycle(); 203 204 PointerCoords coords1 = 205 PointerCoordsBuilder.newBuilder() 206 .setCoords(X_3F + 1.0f, Y_4F - 2.0f) 207 .setPressure(PRESSURE_1F + 0.2f) 208 .setSize(SIZE_1F + 0.5f) 209 .setTouch(2.2f, 0.6f) 210 .build(); 211 PointerProperties properties1 = 212 PointerPropertiesBuilder.newBuilder() 213 .setId(0) 214 .setToolType(MotionEvent.TOOL_TYPE_MOUSE) 215 .build(); 216 motionEventDynamic = 217 MotionEvent.obtain( 218 downTime, 219 eventTime, 220 MotionEvent.ACTION_MOVE, 221 1, 222 new PointerProperties[] {properties1}, 223 new PointerCoords[] {coords1}, 224 META_STATE, 225 0, 226 X_PRECISION_3F, 227 Y_PRECISION_4F, 228 DEVICE_ID_1, 229 EDGE_FLAGS, 230 InputDevice.SOURCE_TOUCHSCREEN, 231 0); 232 assertThat(motionEventDynamicCopy.getToolType(0)).isEqualTo(MotionEvent.TOOL_TYPE_FINGER); 233 assertThat(motionEventDynamic.getToolType(0)).isEqualTo(MotionEvent.TOOL_TYPE_MOUSE); 234 } 235 236 @Test testObtainFromPropertyArrays()237 public void testObtainFromPropertyArrays() { 238 PointerCoords coords0 = 239 PointerCoordsBuilder.newBuilder() 240 .setCoords(X_3F, Y_4F) 241 .setPressure(PRESSURE_1F) 242 .setSize(SIZE_1F) 243 .setTool(1.2f, 1.4f) 244 .build(); 245 PointerCoords coords1 = 246 PointerCoordsBuilder.newBuilder() 247 .setCoords(X_3F + 1.0f, Y_4F - 2.0f) 248 .setPressure(PRESSURE_1F + 0.2f) 249 .setSize(SIZE_1F + 0.5f) 250 .setTouch(2.2f, 0.6f) 251 .build(); 252 253 PointerProperties properties0 = 254 PointerPropertiesBuilder.newBuilder() 255 .setId(0) 256 .setToolType(MotionEvent.TOOL_TYPE_FINGER) 257 .build(); 258 PointerProperties properties1 = 259 PointerPropertiesBuilder.newBuilder() 260 .setId(1) 261 .setToolType(MotionEvent.TOOL_TYPE_FINGER) 262 .build(); 263 264 motionEventDynamic = 265 MotionEvent.obtain( 266 downTime, 267 eventTime, 268 MotionEvent.ACTION_MOVE, 269 2, 270 new PointerProperties[] {properties0, properties1}, 271 new PointerCoords[] {coords0, coords1}, 272 META_STATE, 273 0, 274 X_PRECISION_3F, 275 Y_PRECISION_4F, 276 DEVICE_ID_1, 277 EDGE_FLAGS, 278 InputDevice.SOURCE_TOUCHSCREEN, 279 0); 280 281 // We expect to have data for two pointers 282 assertThat(motionEventDynamic).hasPointerCount(2); 283 assertThat(motionEventDynamic).hasFlags(0); 284 assertThat(motionEventDynamic).pointerId(0).isEqualTo(0); 285 assertThat(motionEventDynamic).pointerId(1).isEqualTo(1); 286 MotionEventEqualitySubject.assertThat(motionEventDynamic) 287 .pointerCoords(0) 288 .isEqualToWithinTolerance(coords0, TOLERANCE); 289 MotionEventEqualitySubject.assertThat(motionEventDynamic) 290 .pointerCoords(1) 291 .isEqualToWithinTolerance(coords1, TOLERANCE); 292 assertThat(motionEventDynamic).pointerProperties(0).isEqualTo(properties0); 293 assertThat(motionEventDynamic).pointerProperties(1).isEqualTo(properties1); 294 } 295 296 @Test testObtainNoHistory()297 public void testObtainNoHistory() { 298 // Add two batch to one of our events 299 motionEvent2.addBatch(eventTime + 10, X_3F + 5.0f, Y_4F + 5.0f, 0.5f, 0.5f, 0); 300 motionEvent2.addBatch(eventTime + 20, X_3F + 10.0f, Y_4F + 15.0f, 2.0f, 3.0f, 0); 301 // The newly added batch should be the "new" values of the event 302 assertThat(motionEvent2).x().isWithin(TOLERANCE).of(X_3F + 10.0f); 303 assertThat(motionEvent2).y().isWithin(TOLERANCE).of(Y_4F + 15.0f); 304 assertThat(motionEvent2).pressure().isWithin(TOLERANCE).of(2.0f); 305 assertThat(motionEvent2).size().isWithin(TOLERANCE).of(3.0f); 306 assertThat(motionEvent2).hasEventTime(eventTime + 20); 307 308 // We should have history with 2 entries 309 assertThat(motionEvent2).hasHistorySize(2); 310 311 // The previous data should be history at index 1 312 assertThat(motionEvent2).historicalX(1).isWithin(TOLERANCE).of(X_3F + 5.0f); 313 assertThat(motionEvent2).historicalY(1).isWithin(TOLERANCE).of(Y_4F + 5.0f); 314 assertThat(motionEvent2).historicalPressure(1).isWithin(TOLERANCE).of(0.5f); 315 assertThat(motionEvent2).historicalSize(1).isWithin(TOLERANCE).of(0.5f); 316 assertThat(motionEvent2).historicalEventTime(1).isEqualTo(eventTime + 10); 317 318 // And the original data should be history at index 0 319 assertThat(motionEvent2).historicalX(0).isWithin(TOLERANCE).of(X_3F); 320 assertThat(motionEvent2).historicalY(0).isWithin(TOLERANCE).of(Y_4F); 321 assertThat(motionEvent2).historicalPressure(0).isWithin(TOLERANCE).of(1.0f); 322 assertThat(motionEvent2).historicalSize(0).isWithin(TOLERANCE).of(1.0f); 323 assertThat(motionEvent2).historicalEventTime(0).isEqualTo(eventTime); 324 325 motionEventDynamic = MotionEvent.obtainNoHistory(motionEvent2); 326 // The newly obtained event should have the matching current content and no history 327 assertThat(motionEventDynamic).x().isWithin(TOLERANCE).of(X_3F + 10.0f); 328 assertThat(motionEventDynamic).y().isWithin(TOLERANCE).of(Y_4F + 15.0f); 329 assertThat(motionEventDynamic).pressure().isWithin(TOLERANCE).of(2.0f); 330 assertThat(motionEventDynamic).size().isWithin(TOLERANCE).of(3.0f); 331 assertThat(motionEventDynamic).hasHistorySize(0); 332 } 333 334 @Test testAccessAction()335 public void testAccessAction() { 336 assertThat(motionEvent1).hasAction(MotionEvent.ACTION_MOVE); 337 338 motionEvent1.setAction(MotionEvent.ACTION_UP); 339 assertThat(motionEvent1).hasAction(MotionEvent.ACTION_UP); 340 } 341 342 @Test testDescribeContents()343 public void testDescribeContents() { 344 // make sure this method never throw any exception. 345 motionEvent2.describeContents(); 346 } 347 348 @Test testAccessEdgeFlags()349 public void testAccessEdgeFlags() { 350 assertThat(motionEvent2).hasEdgeFlags(EDGE_FLAGS); 351 352 motionEvent2.setEdgeFlags(10); 353 assertThat(motionEvent2).hasEdgeFlags(10); 354 } 355 356 @Test testWriteToParcel()357 public void testWriteToParcel() { 358 Parcel parcel = Parcel.obtain(); 359 motionEvent2.writeToParcel(parcel, Parcelable.PARCELABLE_WRITE_RETURN_VALUE); 360 parcel.setDataPosition(0); 361 362 MotionEvent motionEvent = MotionEvent.CREATOR.createFromParcel(parcel); 363 assertThat(motionEvent).rawY().isWithin(TOLERANCE).of(motionEvent2.getRawY()); 364 assertThat(motionEvent).rawX().isWithin(TOLERANCE).of(motionEvent2.getRawX()); 365 assertThat(motionEvent).y().isWithin(TOLERANCE).of(motionEvent2.getY()); 366 assertThat(motionEvent).x().isWithin(TOLERANCE).of(motionEvent2.getX()); 367 assertThat(motionEvent).hasAction(motionEvent2.getAction()); 368 assertThat(motionEvent).hasDownTime(motionEvent2.getDownTime()); 369 assertThat(motionEvent).hasEventTime(motionEvent2.getEventTime()); 370 assertThat(motionEvent).hasEdgeFlags(motionEvent2.getEdgeFlags()); 371 assertThat(motionEvent).hasDeviceId(motionEvent2.getDeviceId()); 372 } 373 374 @Test testReadFromParcelWithInvalidPointerCountSize()375 public void testReadFromParcelWithInvalidPointerCountSize() { 376 Parcel parcel = Parcel.obtain(); 377 motionEvent2.writeToParcel(parcel, Parcelable.PARCELABLE_WRITE_RETURN_VALUE); 378 379 // Move to pointer id count. 380 parcel.setDataPosition(4); 381 parcel.writeInt(17); 382 383 parcel.setDataPosition(0); 384 try { 385 MotionEvent.CREATOR.createFromParcel(parcel); 386 fail("deserialized invalid parcel"); 387 } catch (RuntimeException e) { 388 // Expected. 389 } 390 } 391 392 @Test 393 @SdkSuppress(minSdkVersion = N) testReadFromParcelWithInvalidSampleSize()394 public void testReadFromParcelWithInvalidSampleSize() { 395 Parcel parcel = Parcel.obtain(); 396 motionEvent2.writeToParcel(parcel, Parcelable.PARCELABLE_WRITE_RETURN_VALUE); 397 398 // Move to sample count. 399 parcel.setDataPosition(2 * 4); 400 parcel.writeInt(0x000f0000); 401 402 parcel.setDataPosition(0); 403 try { 404 MotionEvent.CREATOR.createFromParcel(parcel); 405 fail("deserialized invalid parcel"); 406 } catch (RuntimeException e) { 407 // Expected. 408 } 409 } 410 411 @Test testToString()412 public void testToString() { 413 // make sure this method never throw exception. 414 motionEvent2.toString(); 415 } 416 417 @Test testOffsetLocationForPointerSource()418 public void testOffsetLocationForPointerSource() { 419 assertThat(motionEvent2).x().isWithin(TOLERANCE).of(X_3F); 420 assertThat(motionEvent2).y().isWithin(TOLERANCE).of(Y_4F); 421 motionEvent2.setSource(InputDevice.SOURCE_TOUCHSCREEN); 422 423 float offsetX = 1.0f; 424 float offsetY = 1.0f; 425 motionEvent2.offsetLocation(offsetX, offsetY); 426 427 assertThat(motionEvent2).x().isWithin(TOLERANCE).of(X_3F + offsetX); 428 assertThat(motionEvent2).y().isWithin(TOLERANCE).of(Y_4F + offsetY); 429 } 430 431 @Test testSetLocationForPointerSource()432 public void testSetLocationForPointerSource() { 433 assertThat(motionEvent2).x().isWithin(TOLERANCE).of(X_3F); 434 assertThat(motionEvent2).y().isWithin(TOLERANCE).of(Y_4F); 435 motionEvent2.setSource(InputDevice.SOURCE_TOUCHSCREEN); 436 437 motionEvent2.setLocation(2.0f, 2.0f); 438 439 assertThat(motionEvent2).x().isWithin(TOLERANCE).of(2.0f); 440 assertThat(motionEvent2).y().isWithin(TOLERANCE).of(2.0f); 441 } 442 443 @Test testGetHistoricalData()444 public void testGetHistoricalData() { 445 assertThat(motionEvent2).hasHistorySize(0); 446 447 motionEvent2.addBatch(eventTime + 10, X_3F + 5.0f, Y_4F + 5.0f, 0.5f, 0.5f, 0); 448 // The newly added batch should be the "new" values of the event 449 assertThat(motionEvent2).x().isWithin(TOLERANCE).of(X_3F + 5.0f); 450 assertThat(motionEvent2).y().isWithin(TOLERANCE).of(Y_4F + 5.0f); 451 assertThat(motionEvent2).pressure().isWithin(TOLERANCE).of(0.5f); 452 assertThat(motionEvent2).size().isWithin(TOLERANCE).of(0.5f); 453 assertThat(motionEvent2).hasEventTime(eventTime + 10); 454 455 // We should have history with 1 entry 456 assertThat(motionEvent2).hasHistorySize(1); 457 // And the previous / original data should be history at index 0 458 assertThat(motionEvent2).historicalEventTime(0).isEqualTo(eventTime); 459 assertThat(motionEvent2).historicalX(0).isWithin(TOLERANCE).of(X_3F); 460 assertThat(motionEvent2).historicalY(0).isWithin(TOLERANCE).of(Y_4F); 461 assertThat(motionEvent2).historicalPressure(0).isWithin(TOLERANCE).of(1.0f); 462 assertThat(motionEvent2).historicalSize(0).isWithin(TOLERANCE).of(1.0f); 463 } 464 465 @Test testGetCurrentDataWithTwoPointers()466 public void testGetCurrentDataWithTwoPointers() { 467 PointerCoords coords0 = 468 PointerCoordsBuilder.newBuilder() 469 .setCoords(10.0f, 20.0f) 470 .setPressure(1.2f) 471 .setSize(2.0f) 472 .setTool(1.2f, 1.4f) 473 .build(); 474 PointerCoords coords1 = 475 PointerCoordsBuilder.newBuilder() 476 .setCoords(30.0f, 40.0f) 477 .setPressure(1.4f) 478 .setSize(3.0f) 479 .setTouch(2.2f, 0.6f) 480 .build(); 481 482 PointerProperties properties0 = 483 PointerPropertiesBuilder.newBuilder() 484 .setId(0) 485 .setToolType(MotionEvent.TOOL_TYPE_FINGER) 486 .build(); 487 PointerProperties properties1 = 488 PointerPropertiesBuilder.newBuilder() 489 .setId(1) 490 .setToolType(MotionEvent.TOOL_TYPE_FINGER) 491 .build(); 492 493 motionEventDynamic = 494 MotionEvent.obtain( 495 downTime, 496 eventTime, 497 MotionEvent.ACTION_MOVE, 498 2, 499 new PointerProperties[] {properties0, properties1}, 500 new PointerCoords[] {coords0, coords1}, 501 0, 502 0, 503 1.0f, 504 1.0f, 505 0, 506 0, 507 InputDevice.SOURCE_TOUCHSCREEN, 508 0); 509 510 // We expect to have data for two pointers 511 assertThat(motionEventDynamic).pointerId(0).isEqualTo(0); 512 assertThat(motionEventDynamic).pointerId(1).isEqualTo(1); 513 514 assertThat(motionEventDynamic).hasPointerCount(2); 515 assertThat(motionEventDynamic).hasFlags(0); 516 MotionEventEqualitySubject.assertThat(motionEventDynamic) 517 .pointerCoords(0) 518 .isEqualToWithinTolerance(coords0, TOLERANCE); 519 MotionEventEqualitySubject.assertThat(motionEventDynamic) 520 .pointerCoords(1) 521 .isEqualToWithinTolerance(coords1, TOLERANCE); 522 assertThat(motionEventDynamic).pointerProperties(0).isEqualTo(properties0); 523 assertThat(motionEventDynamic).pointerProperties(1).isEqualTo(properties1); 524 } 525 526 @Test testGetHistoricalDataWithTwoPointers()527 public void testGetHistoricalDataWithTwoPointers() { 528 // PHASE 1 - construct the initial data for the event 529 PointerCoords coordsInitial0 = 530 PointerCoordsBuilder.newBuilder() 531 .setCoords(10.0f, 20.0f) 532 .setPressure(1.2f) 533 .setSize(2.0f) 534 .setTool(1.2f, 1.4f) 535 .setTouch(0.7f, 0.6f) 536 .setOrientation(2.0f) 537 .build(); 538 PointerCoords coordsInitial1 = 539 PointerCoordsBuilder.newBuilder() 540 .setCoords(30.0f, 40.0f) 541 .setPressure(1.4f) 542 .setSize(3.0f) 543 .setTool(1.3f, 1.7f) 544 .setTouch(2.7f, 3.6f) 545 .setOrientation(1.0f) 546 .build(); 547 548 PointerProperties properties0 = 549 PointerPropertiesBuilder.newBuilder() 550 .setId(0) 551 .setToolType(MotionEvent.TOOL_TYPE_FINGER) 552 .build(); 553 PointerProperties properties1 = 554 PointerPropertiesBuilder.newBuilder() 555 .setId(1) 556 .setToolType(MotionEvent.TOOL_TYPE_FINGER) 557 .build(); 558 559 motionEventDynamic = 560 MotionEvent.obtain( 561 downTime, 562 eventTime, 563 MotionEvent.ACTION_MOVE, 564 2, 565 new PointerProperties[] {properties0, properties1}, 566 new PointerCoords[] {coordsInitial0, coordsInitial1}, 567 0, 568 0, 569 1.0f, 570 1.0f, 571 0, 572 0, 573 InputDevice.SOURCE_TOUCHSCREEN, 574 0); 575 576 // We expect to have data for two pointers 577 assertThat(motionEventDynamic).hasPointerCount(2); 578 assertThat(motionEventDynamic).pointerId(0).isEqualTo(0); 579 assertThat(motionEventDynamic).pointerId(1).isEqualTo(1); 580 assertThat(motionEventDynamic).hasFlags(0); 581 MotionEventEqualitySubject.assertThat(motionEventDynamic) 582 .pointerCoords(0) 583 .isEqualToWithinTolerance(coordsInitial0, TOLERANCE); 584 MotionEventEqualitySubject.assertThat(motionEventDynamic) 585 .pointerCoords(1) 586 .isEqualToWithinTolerance(coordsInitial1, TOLERANCE); 587 assertThat(motionEventDynamic).pointerProperties(0).isEqualTo(properties0); 588 assertThat(motionEventDynamic).pointerProperties(1).isEqualTo(properties1); 589 590 // PHASE 2 - add a new batch of data to our event 591 PointerCoords coordsNext0 = 592 PointerCoordsBuilder.newBuilder() 593 .setCoords(15.0f, 25.0f) 594 .setPressure(1.6f) 595 .setSize(2.2f) 596 .setTool(1.2f, 1.4f) 597 .setTouch(1.0f, 0.9f) 598 .setOrientation(2.2f) 599 .build(); 600 PointerCoords coordsNext1 = 601 PointerCoordsBuilder.newBuilder() 602 .setCoords(35.0f, 45.0f) 603 .setPressure(1.8f) 604 .setSize(3.2f) 605 .setTool(1.2f, 1.4f) 606 .setTouch(0.7f, 0.6f) 607 .setOrientation(2.9f) 608 .build(); 609 610 motionEventDynamic.addBatch(eventTime + 10, new PointerCoords[] {coordsNext0, coordsNext1}, 0); 611 // We still expect to have data for two pointers 612 assertThat(motionEventDynamic).hasPointerCount(2); 613 assertThat(motionEventDynamic).pointerId(0).isEqualTo(0); 614 assertThat(motionEventDynamic).pointerId(1).isEqualTo(1); 615 assertThat(motionEventDynamic).hasFlags(0); 616 617 // The newly added batch should be the "new" values of the event 618 MotionEventEqualitySubject.assertThat(motionEventDynamic) 619 .pointerCoords(0) 620 .isEqualToWithinTolerance(coordsNext0, TOLERANCE); 621 MotionEventEqualitySubject.assertThat(motionEventDynamic) 622 .pointerCoords(1) 623 .isEqualToWithinTolerance(coordsNext1, TOLERANCE); 624 assertThat(motionEventDynamic).pointerProperties(0).isEqualTo(properties0); 625 assertThat(motionEventDynamic).pointerProperties(1).isEqualTo(properties1); 626 assertThat(motionEventDynamic).hasEventTime(eventTime + 10); 627 628 // We should have history with 1 entry 629 assertThat(motionEventDynamic).hasHistorySize(1); 630 // And the previous / original data should be history at position 0 631 MotionEventEqualitySubject.assertThat(motionEventDynamic) 632 .historicalPointerCoords(0, 0) 633 .isEqualToWithinTolerance(coordsInitial0, TOLERANCE); 634 MotionEventEqualitySubject.assertThat(motionEventDynamic) 635 .historicalPointerCoords(1, 0) 636 .isEqualToWithinTolerance(coordsInitial1, TOLERANCE); 637 } 638 639 @Test testGetHistorySize()640 public void testGetHistorySize() { 641 long eventTime = SystemClock.uptimeMillis(); 642 float x = 10.0f; 643 float y = 20.0f; 644 float pressure = 1.0f; 645 float size = 1.0f; 646 647 motionEvent2.setAction(MotionEvent.ACTION_DOWN); 648 assertThat(motionEvent2).hasHistorySize(0); 649 650 motionEvent2.addBatch(eventTime, x, y, pressure, size, 0); 651 assertThat(motionEvent2).hasHistorySize(1); 652 } 653 654 @Test testRecycle()655 public void testRecycle() { 656 motionEvent2.recycle(); 657 658 try { 659 motionEvent2.recycle(); 660 fail("recycle() should throw an exception when the event has already been recycled."); 661 } catch (RuntimeException ex) { 662 // expected 663 } 664 motionEvent2 = null; // since it was recycled, don't try to recycle again in tear down 665 } 666 667 @Test testTransformShouldApplyMatrixToPointsAndPreserveRawPosition()668 public void testTransformShouldApplyMatrixToPointsAndPreserveRawPosition() { 669 // Generate some points on a circle. 670 // Each point 'i' is a point on a circle of radius ROTATION centered at (3,2) at an angle 671 // of ARC * i degrees clockwise relative to the Y axis. 672 // The geometrical representation is irrelevant to the test, it's just easy to generate 673 // and check rotation. We set the orientation to the same angle. 674 // Coordinate system: down is increasing Y, right is increasing X. 675 final float pi180 = (float) (Math.PI / 180); 676 final float radius = 10; 677 final float arc = 36; 678 final float rotation = arc * 2; 679 680 final int pointerCount = 11; 681 final int[] pointerIds = new int[pointerCount]; 682 final PointerCoords[] pointerCoords = new PointerCoords[pointerCount]; 683 for (int i = 0; i < pointerCount; i++) { 684 final PointerCoords c = new PointerCoords(); 685 final float angle = (float) (i * arc * pi180); 686 pointerIds[i] = i; 687 pointerCoords[i] = c; 688 c.x = (float) (Math.sin(angle) * radius + 3); 689 c.y = (float) (-Math.cos(angle) * radius + 2); 690 c.orientation = angle; 691 } 692 final MotionEvent event = 693 MotionEvent.obtain( 694 0, 695 0, 696 MotionEvent.ACTION_MOVE, 697 pointerCount, 698 pointerIds, 699 pointerCoords, 700 0, 701 0, 702 0, 703 0, 704 0, 705 InputDevice.SOURCE_TOUCHSCREEN, 706 0); 707 final float originalRawX = 0 + 3; 708 final float originalRawY = -radius + 2; 709 710 // Check original raw X and Y assumption. 711 assertThat(event).rawX().isWithin(TOLERANCE).of(originalRawX); 712 assertThat(event).rawY().isWithin(TOLERANCE).of(originalRawY); 713 714 // Now translate the motion event so the circle's origin is at (0,0). 715 event.offsetLocation(-3, -2); 716 717 // Offsetting the location should preserve the raw X and Y of the first point. 718 assertThat(event).rawX().isWithin(TOLERANCE).of(originalRawX); 719 assertThat(event).rawY().isWithin(TOLERANCE).of(originalRawY); 720 721 // Apply a rotation about the origin by ROTATION degrees clockwise. 722 Matrix matrix = new Matrix(); 723 matrix.setRotate(rotation); 724 event.transform(matrix); 725 726 // Check the points. 727 for (int i = 0; i < pointerCount; i++) { 728 final PointerCoords c = pointerCoords[i]; 729 event.getPointerCoords(i, c); 730 731 final float angle = (float) ((i * arc + rotation) * pi180); 732 assertThat(event) 733 .pointerCoords(i) 734 .x() 735 .isWithin(TOLERANCE) 736 .of((float) (Math.sin(angle) * radius)); 737 assertThat(event) 738 .pointerCoords(i) 739 .y() 740 .isWithin(TOLERANCE) 741 .of(-(float) Math.cos(angle) * radius); 742 743 assertThat(Math.tan(c.orientation)).isWithin(0.1f).of(Math.tan(angle)); 744 } 745 746 // Applying the transformation should preserve the raw X and Y of the first point. 747 assertThat(event).rawX().isWithin(TOLERANCE).of(originalRawX); 748 assertThat(event).rawY().isWithin(TOLERANCE).of(originalRawY); 749 } 750 751 @Test testPointerCoordsCopyConstructor()752 public void testPointerCoordsCopyConstructor() { 753 PointerCoords coords = new PointerCoords(); 754 coords.x = 1; 755 coords.y = 2; 756 coords.pressure = 3; 757 coords.size = 4; 758 coords.touchMajor = 5; 759 coords.touchMinor = 6; 760 coords.toolMajor = 7; 761 coords.toolMinor = 8; 762 coords.orientation = 9; 763 coords.setAxisValue(MotionEvent.AXIS_GENERIC_1, 10); 764 765 PointerCoords copy = new PointerCoords(coords); 766 assertThat(copy).x().isWithin(TOLERANCE).of(1f); 767 assertThat(copy).y().isWithin(TOLERANCE).of(2f); 768 assertThat(copy).pressure().isWithin(TOLERANCE).of(3f); 769 assertThat(copy).size().isWithin(TOLERANCE).of(4f); 770 assertThat(copy).touchMajor().isWithin(TOLERANCE).of(5f); 771 assertThat(copy).touchMinor().isWithin(TOLERANCE).of(6f); 772 assertThat(copy).toolMajor().isWithin(TOLERANCE).of(7f); 773 assertThat(copy).toolMinor().isWithin(TOLERANCE).of(8f); 774 assertThat(copy).orientation().isWithin(TOLERANCE).of(9f); 775 assertThat(copy).axisValue(MotionEvent.AXIS_GENERIC_1).isWithin(TOLERANCE).of(10f); 776 } 777 778 @Test testPointerCoordsCopyFrom()779 public void testPointerCoordsCopyFrom() { 780 PointerCoords coords = new PointerCoords(); 781 coords.x = 1; 782 coords.y = 2; 783 coords.pressure = 3; 784 coords.size = 4; 785 coords.touchMajor = 5; 786 coords.touchMinor = 6; 787 coords.toolMajor = 7; 788 coords.toolMinor = 8; 789 coords.orientation = 9; 790 coords.setAxisValue(MotionEvent.AXIS_GENERIC_1, 10); 791 792 PointerCoords copy = new PointerCoords(); 793 copy.copyFrom(coords); 794 assertThat(copy).x().isWithin(TOLERANCE).of(1f); 795 assertThat(copy).y().isWithin(TOLERANCE).of(2f); 796 assertThat(copy).pressure().isWithin(TOLERANCE).of(3f); 797 assertThat(copy).size().isWithin(TOLERANCE).of(4f); 798 assertThat(copy).touchMajor().isWithin(TOLERANCE).of(5f); 799 assertThat(copy).touchMinor().isWithin(TOLERANCE).of(6f); 800 assertThat(copy).toolMajor().isWithin(TOLERANCE).of(7f); 801 assertThat(copy).toolMinor().isWithin(TOLERANCE).of(8f); 802 assertThat(copy).orientation().isWithin(TOLERANCE).of(9f); 803 assertThat(copy).axisValue(MotionEvent.AXIS_GENERIC_1).isWithin(TOLERANCE).of(10f); 804 } 805 806 @Test testPointerPropertiesDefaultConstructor()807 public void testPointerPropertiesDefaultConstructor() { 808 PointerProperties properties = new PointerProperties(); 809 810 assertThat(properties).hasId(MotionEvent.INVALID_POINTER_ID); 811 assertThat(properties).hasToolType(MotionEvent.TOOL_TYPE_UNKNOWN); 812 } 813 814 @Test testPointerPropertiesCopyConstructor()815 public void testPointerPropertiesCopyConstructor() { 816 PointerProperties properties = new PointerProperties(); 817 properties.id = 1; 818 properties.toolType = MotionEvent.TOOL_TYPE_MOUSE; 819 820 PointerProperties copy = new PointerProperties(properties); 821 assertThat(copy).hasId(1); 822 assertThat(copy).hasToolType(MotionEvent.TOOL_TYPE_MOUSE); 823 } 824 825 @Test testPointerPropertiesCopyFrom()826 public void testPointerPropertiesCopyFrom() { 827 PointerProperties properties = new PointerProperties(); 828 properties.id = 1; 829 properties.toolType = MotionEvent.TOOL_TYPE_MOUSE; 830 831 PointerProperties copy = new PointerProperties(); 832 copy.copyFrom(properties); 833 assertThat(copy).hasId(1); 834 assertThat(properties).hasToolType(MotionEvent.TOOL_TYPE_MOUSE); 835 } 836 837 @Test testAxisFromToString()838 public void testAxisFromToString() { 839 assertThat(MotionEvent.axisToString(MotionEvent.AXIS_RTRIGGER)).isEqualTo("AXIS_RTRIGGER"); 840 assertThat(MotionEvent.axisFromString("AXIS_RTRIGGER")).isEqualTo(MotionEvent.AXIS_RTRIGGER); 841 } 842 843 private static class MotionEventEqualitySubject extends Subject { 844 private final MotionEvent actual; 845 MotionEventEqualitySubject(FailureMetadata metadata, MotionEvent actual)846 private MotionEventEqualitySubject(FailureMetadata metadata, MotionEvent actual) { 847 super(metadata, actual); 848 this.actual = actual; 849 } 850 assertThat(MotionEvent event)851 public static MotionEventEqualitySubject assertThat(MotionEvent event) { 852 return Truth.assertAbout(motionEvents()).that(event); 853 } 854 motionEvents()855 public static Subject.Factory<MotionEventEqualitySubject, MotionEvent> motionEvents() { 856 return MotionEventEqualitySubject::new; 857 } 858 pointerCoords(int pointerIndex)859 public PointerCoordsEqualitySubject pointerCoords(int pointerIndex) { 860 PointerCoords outPointerCoords = new PointerCoords(); 861 actual.getPointerCoords(pointerIndex, outPointerCoords); 862 return check("getPointerCoords(%s)", pointerIndex) 863 .about(PointerCoordsEqualitySubject.pointerCoords()) 864 .that(outPointerCoords); 865 } 866 historicalPointerCoords(int pointerIndex, int pos)867 public PointerCoordsEqualitySubject historicalPointerCoords(int pointerIndex, int pos) { 868 PointerCoords outPointerCoords = new PointerCoords(); 869 actual.getHistoricalPointerCoords(pointerIndex, pos, outPointerCoords); 870 return check("getHistoricalPointerCoords(%s, %s)", pointerIndex, pos) 871 .about(PointerCoordsEqualitySubject.pointerCoords()) 872 .that(outPointerCoords); 873 } 874 875 /** Asserts that the given MotionEvent matches the current subject. */ isEqualToWithinTolerance(MotionEvent other, float tolerance)876 public void isEqualToWithinTolerance(MotionEvent other, float tolerance) { 877 check("getDownTime()").that(actual.getDownTime()).isEqualTo(other.getDownTime()); 878 check("getEventTime()").that(actual.getEventTime()).isEqualTo(other.getEventTime()); 879 check("action()").that(actual.getAction()).isEqualTo(other.getAction()); 880 check("buttonState()").that(actual.getButtonState()).isEqualTo(other.getButtonState()); 881 check("deviceId()").that(actual.getDeviceId()).isEqualTo(other.getDeviceId()); 882 check("getFlags()").that(actual.getFlags()).isEqualTo(other.getFlags()); 883 check("getEdgeFlags()").that(actual.getEdgeFlags()).isEqualTo(other.getEdgeFlags()); 884 check("getXPrecision()").that(actual.getXPrecision()).isEqualTo(other.getXPrecision()); 885 check("getYPrecision()").that(actual.getYPrecision()).isEqualTo(other.getYPrecision()); 886 887 check("getX()").that(actual.getX()).isWithin(tolerance).of(other.getX()); 888 check("getY()").that(actual.getY()).isWithin(tolerance).of(other.getY()); 889 check("getPressure()").that(actual.getPressure()).isWithin(tolerance).of(other.getPressure()); 890 check("getSize()").that(actual.getSize()).isWithin(tolerance).of(other.getSize()); 891 check("getTouchMajor()") 892 .that(actual.getTouchMajor()) 893 .isWithin(tolerance) 894 .of(other.getTouchMajor()); 895 check("getTouchMinor()") 896 .that(actual.getTouchMinor()) 897 .isWithin(tolerance) 898 .of(other.getTouchMinor()); 899 check("getToolMajor()") 900 .that(actual.getToolMajor()) 901 .isWithin(tolerance) 902 .of(other.getToolMajor()); 903 check("getToolMinor()") 904 .that(actual.getToolMinor()) 905 .isWithin(tolerance) 906 .of(other.getToolMinor()); 907 check("getOrientation()") 908 .that(actual.getOrientation()) 909 .isWithin(tolerance) 910 .of(other.getOrientation()); 911 check("getPointerCount()").that(actual.getPointerCount()).isEqualTo(other.getPointerCount()); 912 913 for (int i = 1; i < actual.getPointerCount(); i++) { 914 check("getX(%s)", i).that(actual.getX(i)).isWithin(tolerance).of(other.getX(i)); 915 check("getY(%s)", i).that(actual.getY(i)).isWithin(tolerance).of(other.getY(i)); 916 check("getPressure(%s)", i) 917 .that(actual.getPressure(i)) 918 .isWithin(tolerance) 919 .of(other.getPressure(i)); 920 check("getSize(%s)", i).that(actual.getSize(i)).isWithin(tolerance).of(other.getSize(i)); 921 check("getTouchMajor(%s)", i) 922 .that(actual.getTouchMajor(i)) 923 .isWithin(tolerance) 924 .of(other.getTouchMajor(i)); 925 check("getTouchMinor(%s)", i) 926 .that(actual.getTouchMinor(i)) 927 .isWithin(tolerance) 928 .of(other.getTouchMinor(i)); 929 check("getToolMajor(%s)", i) 930 .that(actual.getToolMajor(i)) 931 .isWithin(tolerance) 932 .of(other.getToolMajor(i)); 933 check("getToolMinor(%s)", i) 934 .that(actual.getToolMinor(i)) 935 .isWithin(tolerance) 936 .of(other.getToolMinor(i)); 937 check("getOrientation(%s)", i) 938 .that(actual.getOrientation(i)) 939 .isWithin(tolerance) 940 .of(other.getOrientation(i)); 941 } 942 check("getHistorySize()").that(actual.getHistorySize()).isEqualTo(other.getHistorySize()); 943 944 for (int i = 0; i < actual.getHistorySize(); i++) { 945 check("getHistoricalX(%s)", i).that(actual.getX(i)).isWithin(tolerance).of(other.getX(i)); 946 check("getHistoricalY(%s)", i) 947 .that(actual.getHistoricalY(i)) 948 .isWithin(tolerance) 949 .of(other.getHistoricalY(i)); 950 check("getHistoricalPressure(%s)", i) 951 .that(actual.getHistoricalPressure(i)) 952 .isWithin(tolerance) 953 .of(other.getHistoricalPressure(i)); 954 check("getHistoricalSize(%s)", i) 955 .that(actual.getHistoricalSize(i)) 956 .isWithin(tolerance) 957 .of(other.getHistoricalSize(i)); 958 check("getHistoricalTouchMajor(%s)", i) 959 .that(actual.getHistoricalTouchMajor(i)) 960 .isWithin(tolerance) 961 .of(other.getHistoricalTouchMajor(i)); 962 check("getHistoricalTouchMinor(%s)", i) 963 .that(actual.getHistoricalTouchMinor(i)) 964 .isWithin(tolerance) 965 .of(other.getHistoricalTouchMinor(i)); 966 check("getHistoricalToolMajor(%s)", i) 967 .that(actual.getHistoricalToolMajor(i)) 968 .isWithin(tolerance) 969 .of(other.getHistoricalToolMajor(i)); 970 check("getHistoricalToolMinor(%s)", i) 971 .that(actual.getHistoricalToolMinor(i)) 972 .isWithin(tolerance) 973 .of(other.getHistoricalToolMinor(i)); 974 check("getHistoricalOrientation(%s)", i) 975 .that(actual.getHistoricalOrientation(i)) 976 .isWithin(tolerance) 977 .of(other.getHistoricalOrientation(i)); 978 } 979 } 980 } 981 982 private static class PointerCoordsEqualitySubject extends Subject { 983 private final PointerCoords actual; 984 PointerCoordsEqualitySubject(FailureMetadata metadata, PointerCoords actual)985 private PointerCoordsEqualitySubject(FailureMetadata metadata, PointerCoords actual) { 986 super(metadata, actual); 987 this.actual = actual; 988 } 989 assertThat(PointerCoords coords)990 public static PointerCoordsEqualitySubject assertThat(PointerCoords coords) { 991 return Truth.assertAbout(pointerCoords()).that(coords); 992 } 993 pointerCoords()994 public static Subject.Factory<PointerCoordsEqualitySubject, PointerCoords> pointerCoords() { 995 return PointerCoordsEqualitySubject::new; 996 } 997 isEqualToWithinTolerance(PointerCoords other, float tolerance)998 public void isEqualToWithinTolerance(PointerCoords other, float tolerance) { 999 check("orientation").that(actual.orientation).isWithin(tolerance).of(other.orientation); 1000 check("pressure").that(actual.pressure).isWithin(tolerance).of(other.pressure); 1001 check("size").that(actual.size).isWithin(tolerance).of(other.size); 1002 check("toolMajor").that(actual.toolMajor).isWithin(tolerance).of(other.toolMajor); 1003 check("toolMinor").that(actual.toolMinor).isWithin(tolerance).of(other.toolMinor); 1004 check("touchMajor").that(actual.touchMajor).isWithin(tolerance).of(other.touchMajor); 1005 check("touchMinor").that(actual.touchMinor).isWithin(tolerance).of(other.touchMinor); 1006 check("x").that(actual.x).isWithin(tolerance).of(other.x); 1007 check("y").that(actual.y).isWithin(tolerance).of(other.y); 1008 } 1009 } 1010 } 1011