1 package org.robolectric.shadows; 2 3 import static android.os.Build.VERSION_CODES.M; 4 import static android.os.Build.VERSION_CODES.P; 5 import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE; 6 import static com.google.common.base.Preconditions.checkNotNull; 7 import static com.google.common.base.Preconditions.checkState; 8 import static org.robolectric.shadows.NativeAndroidInput.AMOTION_EVENT_AXIS_ORIENTATION; 9 import static org.robolectric.shadows.NativeAndroidInput.AMOTION_EVENT_AXIS_PRESSURE; 10 import static org.robolectric.shadows.NativeAndroidInput.AMOTION_EVENT_AXIS_SIZE; 11 import static org.robolectric.shadows.NativeAndroidInput.AMOTION_EVENT_AXIS_TOOL_MAJOR; 12 import static org.robolectric.shadows.NativeAndroidInput.AMOTION_EVENT_AXIS_TOOL_MINOR; 13 import static org.robolectric.shadows.NativeAndroidInput.AMOTION_EVENT_AXIS_TOUCH_MAJOR; 14 import static org.robolectric.shadows.NativeAndroidInput.AMOTION_EVENT_AXIS_TOUCH_MINOR; 15 import static org.robolectric.shadows.NativeAndroidInput.AMOTION_EVENT_AXIS_X; 16 import static org.robolectric.shadows.NativeAndroidInput.AMOTION_EVENT_AXIS_Y; 17 18 import android.graphics.Matrix; 19 import android.os.Parcel; 20 import android.view.MotionEvent; 21 import android.view.MotionEvent.PointerCoords; 22 import android.view.MotionEvent.PointerProperties; 23 import java.lang.reflect.Field; 24 import java.lang.reflect.Modifier; 25 import java.util.List; 26 import org.robolectric.annotation.HiddenApi; 27 import org.robolectric.annotation.Implementation; 28 import org.robolectric.annotation.Implements; 29 import org.robolectric.annotation.InDevelopment; 30 import org.robolectric.annotation.RealObject; 31 import org.robolectric.annotation.Resetter; 32 import org.robolectric.res.android.NativeObjRegistry; 33 import org.robolectric.util.ReflectionHelpers; 34 import org.robolectric.versioning.AndroidVersions.V; 35 36 /** 37 * Shadow of MotionEvent. 38 * 39 * <p>The Android framework stores motion events in a pool of native objects. All motion event data 40 * is stored natively, and accessed via a series of static native methods following the pattern 41 * nativeGetXXXX(mNativePtr, ...) 42 * 43 * <p>This shadow mirrors this design, but has java equivalents of each native object. Most of the 44 * contents of this class were transliterated from oreo-mr1 (SDK 27) 45 * frameworks/base/core/jni/android_view_MotionEvent.cpp 46 * 47 * @see <a 48 * href="https://android.googlesource.com/platform/frameworks/base/+/oreo-mr1-release/core/jni/android_view_MotionEvent.cpp">core/jni/android_view_MotionEvent.cpp</a> 49 * <p>Tests should not reference this class directly. MotionEvents should be created via one of 50 * the MotionEvent.obtain methods or via MotionEventBuilder. 51 */ 52 @SuppressWarnings({"UnusedDeclaration"}) 53 @Implements(value = MotionEvent.class) 54 public class ShadowMotionEvent extends ShadowInputEvent { 55 56 private static NativeObjRegistry<NativeInput.MotionEvent> nativeMotionEventRegistry = 57 new NativeObjRegistry<>(NativeInput.MotionEvent.class); 58 59 private static final int HISTORY_CURRENT = -0x80000000; 60 61 @RealObject private MotionEvent realMotionEvent; 62 63 @Resetter reset()64 public static void reset() { 65 // rely on MotionEvent finalizer to clear native object instead of calling 66 // nativeMotionEventRegistry.clear(); 67 ReflectionHelpers.setStaticField(MotionEvent.class, "gRecyclerTop", null); 68 ReflectionHelpers.setStaticField(MotionEvent.class, "gSharedTempPointerCoords", null); 69 ReflectionHelpers.setStaticField(MotionEvent.class, "gSharedTempPointerProperties", null); 70 ReflectionHelpers.setStaticField(MotionEvent.class, "gRecyclerUsed", 0); 71 ReflectionHelpers.setStaticField(MotionEvent.class, "gSharedTempPointerIndexMap", null); 72 } 73 validatePointerCount(int pointerCount)74 private static void validatePointerCount(int pointerCount) { 75 checkState(pointerCount >= 1, "pointerCount must be at least 1"); 76 } 77 validatePointerPropertiesArray( PointerProperties[] pointerPropertiesObjArray, int pointerCount)78 private static void validatePointerPropertiesArray( 79 PointerProperties[] pointerPropertiesObjArray, int pointerCount) { 80 checkNotNull(pointerPropertiesObjArray, "pointerProperties array must not be null"); 81 checkState( 82 pointerPropertiesObjArray.length >= pointerCount, 83 "pointerProperties array must be large enough to hold all pointers"); 84 } 85 validatePointerCoordsObjArray( PointerCoords[] pointerCoordsObjArray, int pointerCount)86 private static void validatePointerCoordsObjArray( 87 PointerCoords[] pointerCoordsObjArray, int pointerCount) { 88 checkNotNull(pointerCoordsObjArray, "pointerCoords array must not be null"); 89 checkState( 90 pointerCoordsObjArray.length >= pointerCount, 91 "pointerCoords array must be large enough to hold all pointers"); 92 } 93 validatePointerIndex(int pointerIndex, int pointerCount)94 private static void validatePointerIndex(int pointerIndex, int pointerCount) { 95 checkState(pointerIndex >= 0 && pointerIndex < pointerCount, "pointerIndex out of range"); 96 } 97 validateHistoryPos(int historyPos, int historySize)98 private static void validateHistoryPos(int historyPos, int historySize) { 99 checkState(historyPos >= 0 && historyPos < historySize, "historyPos out of range"); 100 } 101 validatePointerCoords(PointerCoords pointerCoordsObj)102 private static void validatePointerCoords(PointerCoords pointerCoordsObj) { 103 checkNotNull(pointerCoordsObj, "pointerCoords must not be null"); 104 } 105 validatePointerProperties(PointerProperties pointerPropertiesObj)106 private static void validatePointerProperties(PointerProperties pointerPropertiesObj) { 107 checkNotNull(pointerPropertiesObj, "pointerProperties must not be null"); 108 } 109 pointerCoordsToNative( PointerCoords pointerCoordsObj, float xOffset, float yOffset)110 private static NativeInput.PointerCoords pointerCoordsToNative( 111 PointerCoords pointerCoordsObj, float xOffset, float yOffset) { 112 NativeInput.PointerCoords outRawPointerCoords = new NativeInput.PointerCoords(); 113 outRawPointerCoords.clear(); 114 outRawPointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, pointerCoordsObj.x - xOffset); 115 outRawPointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, pointerCoordsObj.y - yOffset); 116 outRawPointerCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pointerCoordsObj.pressure); 117 outRawPointerCoords.setAxisValue(AMOTION_EVENT_AXIS_SIZE, pointerCoordsObj.size); 118 outRawPointerCoords.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, pointerCoordsObj.touchMajor); 119 outRawPointerCoords.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, pointerCoordsObj.touchMinor); 120 outRawPointerCoords.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, pointerCoordsObj.toolMajor); 121 outRawPointerCoords.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, pointerCoordsObj.toolMinor); 122 outRawPointerCoords.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, pointerCoordsObj.orientation); 123 long packedAxisBits = ReflectionHelpers.getField(pointerCoordsObj, "mPackedAxisBits"); 124 NativeBitSet64 bits = new NativeBitSet64(packedAxisBits); 125 if (!bits.isEmpty()) { 126 float[] valuesArray = ReflectionHelpers.getField(pointerCoordsObj, "mPackedAxisValues"); 127 if (valuesArray != null) { 128 int index = 0; 129 do { 130 int axis = bits.clearFirstMarkedBit(); 131 outRawPointerCoords.setAxisValue(axis, valuesArray[index++]); 132 } while (!bits.isEmpty()); 133 } 134 } 135 return outRawPointerCoords; 136 } 137 obtainPackedAxisValuesArray( int minSize, PointerCoords outPointerCoordsObj)138 private static float[] obtainPackedAxisValuesArray( 139 int minSize, PointerCoords outPointerCoordsObj) { 140 float[] outValuesArray = ReflectionHelpers.getField(outPointerCoordsObj, "mPackedAxisValues"); 141 if (outValuesArray != null) { 142 int size = outValuesArray.length; 143 if (minSize <= size) { 144 return outValuesArray; 145 } 146 } 147 int size = 8; 148 while (size < minSize) { 149 size *= 2; 150 } 151 outValuesArray = new float[size]; 152 ReflectionHelpers.setField(outPointerCoordsObj, "mPackedAxisValues", outValuesArray); 153 return outValuesArray; 154 } 155 pointerCoordsFromNative( NativeInput.PointerCoords rawPointerCoords, float xOffset, float yOffset, PointerCoords outPointerCoordsObj)156 private static void pointerCoordsFromNative( 157 NativeInput.PointerCoords rawPointerCoords, 158 float xOffset, 159 float yOffset, 160 PointerCoords outPointerCoordsObj) { 161 outPointerCoordsObj.x = rawPointerCoords.getAxisValue(AMOTION_EVENT_AXIS_X) + xOffset; 162 outPointerCoordsObj.y = rawPointerCoords.getAxisValue(AMOTION_EVENT_AXIS_Y) + yOffset; 163 outPointerCoordsObj.pressure = rawPointerCoords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE); 164 outPointerCoordsObj.size = rawPointerCoords.getAxisValue(AMOTION_EVENT_AXIS_SIZE); 165 outPointerCoordsObj.touchMajor = rawPointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR); 166 outPointerCoordsObj.touchMinor = rawPointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR); 167 outPointerCoordsObj.toolMajor = rawPointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR); 168 outPointerCoordsObj.toolMinor = rawPointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR); 169 outPointerCoordsObj.orientation = rawPointerCoords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION); 170 long outBits = 0; 171 NativeBitSet64 bits = new NativeBitSet64(rawPointerCoords.getBits()); 172 bits.clearBit(AMOTION_EVENT_AXIS_X); 173 bits.clearBit(AMOTION_EVENT_AXIS_Y); 174 bits.clearBit(AMOTION_EVENT_AXIS_PRESSURE); 175 bits.clearBit(AMOTION_EVENT_AXIS_SIZE); 176 bits.clearBit(AMOTION_EVENT_AXIS_TOUCH_MAJOR); 177 bits.clearBit(AMOTION_EVENT_AXIS_TOUCH_MINOR); 178 bits.clearBit(AMOTION_EVENT_AXIS_TOOL_MAJOR); 179 bits.clearBit(AMOTION_EVENT_AXIS_TOOL_MINOR); 180 bits.clearBit(AMOTION_EVENT_AXIS_ORIENTATION); 181 if (!bits.isEmpty()) { 182 int packedAxesCount = bits.count(); 183 float[] outValuesArray = obtainPackedAxisValuesArray(packedAxesCount, outPointerCoordsObj); 184 float[] outValues = outValuesArray; 185 int index = 0; 186 do { 187 int axis = bits.clearFirstMarkedBit(); 188 outBits |= NativeBitSet64.valueForBit(axis); 189 outValues[index++] = rawPointerCoords.getAxisValue(axis); 190 } while (!bits.isEmpty()); 191 } 192 ReflectionHelpers.setField(outPointerCoordsObj, "mPackedAxisBits", outBits); 193 } 194 195 @Implementation(maxSdk = P) 196 @HiddenApi nativeInitialize( long nativePtr, int deviceId, int source, int action, int flags, int edgeFlags, int metaState, int buttonState, float xOffset, float yOffset, float xPrecision, float yPrecision, long downTimeNanos, long eventTimeNanos, int pointerCount, PointerProperties[] pointerPropertiesObjArray, PointerCoords[] pointerCoordsObjArray)197 protected static long nativeInitialize( 198 long nativePtr, 199 int deviceId, 200 int source, 201 int action, 202 int flags, 203 int edgeFlags, 204 int metaState, 205 int buttonState, 206 float xOffset, 207 float yOffset, 208 float xPrecision, 209 float yPrecision, 210 long downTimeNanos, 211 long eventTimeNanos, 212 int pointerCount, 213 PointerProperties[] pointerPropertiesObjArray, 214 PointerCoords[] pointerCoordsObjArray) { 215 216 validatePointerCount(pointerCount); 217 validatePointerPropertiesArray(pointerPropertiesObjArray, pointerCount); 218 validatePointerCoordsObjArray(pointerCoordsObjArray, pointerCount); 219 220 NativeInput.MotionEvent event; 221 if (nativePtr > 0) { 222 event = nativeMotionEventRegistry.getNativeObject(nativePtr); 223 } else { 224 event = new NativeInput.MotionEvent(); 225 nativePtr = nativeMotionEventRegistry.register(event); 226 } 227 228 NativeInput.PointerCoords[] rawPointerCoords = new NativeInput.PointerCoords[pointerCount]; 229 for (int i = 0; i < pointerCount; i++) { 230 PointerCoords pointerCoordsObj = pointerCoordsObjArray[i]; 231 checkNotNull(pointerCoordsObj); 232 rawPointerCoords[i] = pointerCoordsToNative(pointerCoordsObj, xOffset, yOffset); 233 } 234 235 event.initialize( 236 deviceId, 237 source, 238 action, 239 0, 240 flags, 241 edgeFlags, 242 metaState, 243 buttonState, 244 xOffset, 245 yOffset, 246 xPrecision, 247 yPrecision, 248 downTimeNanos, 249 eventTimeNanos, 250 pointerCount, 251 pointerPropertiesObjArray, 252 rawPointerCoords); 253 return nativePtr; 254 } 255 256 // TODO(brettchabot): properly handle displayId 257 @Implementation(minSdk = android.os.Build.VERSION_CODES.Q) 258 @HiddenApi nativeInitialize( long nativePtr, int deviceId, int source, int displayId, int action, int flags, int edgeFlags, int metaState, int buttonState, int classification, float xOffset, float yOffset, float xPrecision, float yPrecision, long downTimeNanos, long eventTimeNanos, int pointerCount, PointerProperties[] pointerIds, PointerCoords[] pointerCoords)259 protected static long nativeInitialize( 260 long nativePtr, 261 int deviceId, 262 int source, 263 int displayId, 264 int action, 265 int flags, 266 int edgeFlags, 267 int metaState, 268 int buttonState, 269 int classification, 270 float xOffset, 271 float yOffset, 272 float xPrecision, 273 float yPrecision, 274 long downTimeNanos, 275 long eventTimeNanos, 276 int pointerCount, 277 PointerProperties[] pointerIds, 278 PointerCoords[] pointerCoords) { 279 return nativeInitialize( 280 nativePtr, 281 deviceId, 282 source, 283 action, 284 flags, 285 edgeFlags, 286 metaState, 287 buttonState, 288 xOffset, 289 yOffset, 290 xPrecision, 291 yPrecision, 292 downTimeNanos, 293 eventTimeNanos, 294 pointerCount, 295 pointerIds, 296 pointerCoords); 297 } 298 299 @Implementation 300 @HiddenApi nativeDispose(long nativePtr)301 protected static void nativeDispose(long nativePtr) { 302 nativeMotionEventRegistry.unregister(nativePtr); 303 } 304 305 @Implementation 306 @HiddenApi nativeAddBatch( long nativePtr, long eventTimeNanos, PointerCoords[] pointerCoordsObjArray, int metaState)307 protected static void nativeAddBatch( 308 long nativePtr, long eventTimeNanos, PointerCoords[] pointerCoordsObjArray, int metaState) { 309 NativeInput.MotionEvent event = getNativeMotionEvent(nativePtr); 310 int pointerCount = event.getPointerCount(); 311 validatePointerCoordsObjArray(pointerCoordsObjArray, pointerCount); 312 NativeInput.PointerCoords[] rawPointerCoords = new NativeInput.PointerCoords[pointerCount]; 313 for (int i = 0; i < pointerCount; i++) { 314 PointerCoords pointerCoordsObj = pointerCoordsObjArray[i]; 315 checkNotNull(pointerCoordsObj); 316 rawPointerCoords[i] = 317 pointerCoordsToNative(pointerCoordsObj, event.getXOffset(), event.getYOffset()); 318 } 319 event.addSample(eventTimeNanos, rawPointerCoords); 320 event.setMetaState(event.getMetaState() | metaState); 321 } 322 323 @Implementation 324 @HiddenApi nativeGetPointerCoords( long nativePtr, int pointerIndex, int historyPos, PointerCoords outPointerCoordsObj)325 protected static void nativeGetPointerCoords( 326 long nativePtr, int pointerIndex, int historyPos, PointerCoords outPointerCoordsObj) { 327 NativeInput.MotionEvent event = getNativeMotionEvent(nativePtr); 328 int pointerCount = event.getPointerCount(); 329 validatePointerIndex(pointerIndex, pointerCount); 330 validatePointerCoords(outPointerCoordsObj); 331 332 NativeInput.PointerCoords rawPointerCoords; 333 if (historyPos == HISTORY_CURRENT) { 334 rawPointerCoords = event.getRawPointerCoords(pointerIndex); 335 } else { 336 int historySize = event.getHistorySize(); 337 validateHistoryPos(historyPos, historySize); 338 rawPointerCoords = event.getHistoricalRawPointerCoords(pointerIndex, historyPos); 339 } 340 pointerCoordsFromNative( 341 rawPointerCoords, event.getXOffset(), event.getYOffset(), outPointerCoordsObj); 342 } 343 344 @Implementation 345 @HiddenApi nativeGetPointerProperties( long nativePtr, int pointerIndex, PointerProperties outPointerPropertiesObj)346 protected static void nativeGetPointerProperties( 347 long nativePtr, int pointerIndex, PointerProperties outPointerPropertiesObj) { 348 NativeInput.MotionEvent event = getNativeMotionEvent(nativePtr); 349 int pointerCount = event.getPointerCount(); 350 validatePointerIndex(pointerIndex, pointerCount); 351 validatePointerProperties(outPointerPropertiesObj); 352 353 PointerProperties pointerProperties = event.getPointerProperties(pointerIndex); 354 // pointerPropertiesFromNative(env, pointerProperties, outPointerPropertiesObj); 355 outPointerPropertiesObj.copyFrom(pointerProperties); 356 } 357 358 @Implementation 359 @HiddenApi nativeReadFromParcel(long nativePtr, Parcel parcelObj)360 protected static long nativeReadFromParcel(long nativePtr, Parcel parcelObj) { 361 NativeInput.MotionEvent event; 362 if (nativePtr == 0) { 363 event = new NativeInput.MotionEvent(); 364 nativePtr = nativeMotionEventRegistry.register(event); 365 } else { 366 event = nativeMotionEventRegistry.getNativeObject(nativePtr); 367 } 368 boolean status = event.readFromParcel(parcelObj); 369 if (!status) { 370 if (nativePtr > 0) { 371 nativeMotionEventRegistry.unregister(nativePtr); 372 } 373 throw new RuntimeException("Failed to read MotionEvent parcel."); 374 } 375 return nativePtr; 376 } 377 378 @Implementation 379 @HiddenApi nativeWriteToParcel(long nativePtr, Parcel parcel)380 protected static void nativeWriteToParcel(long nativePtr, Parcel parcel) { 381 NativeInput.MotionEvent event = getNativeMotionEvent(nativePtr); 382 if (!event.writeToParcel(parcel)) { 383 throw new RuntimeException("Failed to write MotionEvent parcel."); 384 } 385 } 386 387 @Implementation 388 @HiddenApi nativeAxisToString(int axis)389 protected static String nativeAxisToString(int axis) { 390 // The native code just mirrors the AXIS_* constants defined in MotionEvent.java. 391 // Look up the field value by reflection to future proof this method 392 for (Field field : MotionEvent.class.getDeclaredFields()) { 393 int modifiers = field.getModifiers(); 394 try { 395 if (Modifier.isStatic(modifiers) 396 && Modifier.isPublic(modifiers) 397 && field.getName().startsWith("AXIS_") 398 && field.getInt(null) == axis) { 399 // return the field name stripping off the "AXIS_" prefix 400 return field.getName().substring(5); 401 } 402 } catch (IllegalAccessException e) { 403 // ignore 404 } 405 } 406 return null; 407 } 408 409 @Implementation 410 @HiddenApi nativeAxisFromString(String label)411 protected static int nativeAxisFromString(String label) { 412 // The native code just mirrors the AXIS_* constants defined in MotionEvent.java. Look up 413 // the field value by reflection 414 try { 415 Field constantField = MotionEvent.class.getDeclaredField("AXIS_" + label); 416 return constantField.getInt(null); 417 } catch (NoSuchFieldException | IllegalAccessException e) { 418 return 0; 419 } 420 } 421 422 @Implementation 423 @HiddenApi nativeGetPointerId(long nativePtr, int pointerIndex)424 protected static int nativeGetPointerId(long nativePtr, int pointerIndex) { 425 NativeInput.MotionEvent event = getNativeMotionEvent(nativePtr); 426 int pointerCount = event.getPointerCount(); 427 validatePointerIndex(pointerIndex, pointerCount); 428 return event.getPointerId(pointerIndex); 429 } 430 431 @Implementation 432 @HiddenApi nativeGetToolType(long nativePtr, int pointerIndex)433 protected static int nativeGetToolType(long nativePtr, int pointerIndex) { 434 NativeInput.MotionEvent event = getNativeMotionEvent(nativePtr); 435 int pointerCount = event.getPointerCount(); 436 validatePointerIndex(pointerIndex, pointerCount); 437 return event.getToolType(pointerIndex); 438 } 439 440 @Implementation 441 @HiddenApi nativeGetEventTimeNanos(long nativePtr, int historyPos)442 protected static long nativeGetEventTimeNanos(long nativePtr, int historyPos) { 443 NativeInput.MotionEvent event = getNativeMotionEvent(nativePtr); 444 if (historyPos == HISTORY_CURRENT) { 445 return event.getEventTime(); 446 } else { 447 int historySize = event.getHistorySize(); 448 validateHistoryPos(historyPos, historySize); 449 return event.getHistoricalEventTime(historyPos); 450 } 451 } 452 453 @Implementation 454 @HiddenApi nativeGetRawAxisValue( long nativePtr, int axis, int pointerIndex, int historyPos)455 protected static float nativeGetRawAxisValue( 456 long nativePtr, int axis, int pointerIndex, int historyPos) { 457 NativeInput.MotionEvent event = getNativeMotionEvent(nativePtr); 458 int pointerCount = event.getPointerCount(); 459 validatePointerIndex(pointerIndex, pointerCount); 460 461 if (historyPos == HISTORY_CURRENT) { 462 return event.getRawAxisValue(axis, pointerIndex); 463 } else { 464 int historySize = event.getHistorySize(); 465 validateHistoryPos(historyPos, historySize); 466 return event.getHistoricalRawAxisValue(axis, pointerIndex, historyPos); 467 } 468 } 469 470 @Implementation 471 @HiddenApi nativeGetAxisValue( long nativePtr, int axis, int pointerIndex, int historyPos)472 protected static float nativeGetAxisValue( 473 long nativePtr, int axis, int pointerIndex, int historyPos) { 474 NativeInput.MotionEvent event = getNativeMotionEvent(nativePtr); 475 int pointerCount = event.getPointerCount(); 476 validatePointerIndex(pointerIndex, pointerCount); 477 478 if (historyPos == HISTORY_CURRENT) { 479 return event.getAxisValue(axis, pointerIndex); 480 } else { 481 int historySize = event.getHistorySize(); 482 validateHistoryPos(historyPos, historySize); 483 return event.getHistoricalAxisValue(axis, pointerIndex, historyPos); 484 } 485 } 486 487 @Implementation 488 @HiddenApi nativeCopy(long destNativePtr, long sourceNativePtr, boolean keepHistory)489 protected static long nativeCopy(long destNativePtr, long sourceNativePtr, boolean keepHistory) { 490 NativeInput.MotionEvent destEvent = nativeMotionEventRegistry.peekNativeObject(destNativePtr); 491 if (destEvent == null) { 492 destEvent = new NativeInput.MotionEvent(); 493 destNativePtr = nativeMotionEventRegistry.register(destEvent); 494 } 495 NativeInput.MotionEvent sourceEvent = getNativeMotionEvent(sourceNativePtr); 496 destEvent.copyFrom(sourceEvent, keepHistory); 497 return destNativePtr; 498 } 499 500 @Implementation 501 @HiddenApi nativeGetDeviceId(long nativePtr)502 protected static int nativeGetDeviceId(long nativePtr) { 503 NativeInput.MotionEvent event = getNativeMotionEvent(nativePtr); 504 return event.getDeviceId(); 505 } 506 507 @Implementation 508 @HiddenApi nativeGetSource(long nativePtr)509 protected static int nativeGetSource(long nativePtr) { 510 NativeInput.MotionEvent event = getNativeMotionEvent(nativePtr); 511 return event.getSource(); 512 } 513 514 @Implementation 515 @HiddenApi 516 @SuppressWarnings("robolectric.ShadowReturnTypeMismatch") nativeSetSource(long nativePtr, int source)517 protected static void nativeSetSource(long nativePtr, int source) { 518 NativeInput.MotionEvent event = getNativeMotionEvent(nativePtr); 519 event.setSource(source); 520 } 521 522 @Implementation 523 @HiddenApi nativeGetAction(long nativePtr)524 protected static int nativeGetAction(long nativePtr) { 525 NativeInput.MotionEvent event = getNativeMotionEvent(nativePtr); 526 return event.getAction(); 527 } 528 529 @Implementation 530 @HiddenApi nativeSetAction(long nativePtr, int action)531 protected static void nativeSetAction(long nativePtr, int action) { 532 NativeInput.MotionEvent event = getNativeMotionEvent(nativePtr); 533 event.setAction(action); 534 } 535 536 @Implementation(minSdk = M) 537 @HiddenApi nativeGetActionButton(long nativePtr)538 protected static int nativeGetActionButton(long nativePtr) { 539 NativeInput.MotionEvent event = getNativeMotionEvent(nativePtr); 540 return event.getActionButton(); 541 } 542 543 @Implementation(minSdk = M) 544 @HiddenApi nativeSetActionButton(long nativePtr, int button)545 protected static void nativeSetActionButton(long nativePtr, int button) { 546 NativeInput.MotionEvent event = getNativeMotionEvent(nativePtr); 547 event.setActionButton(button); 548 } 549 550 @Implementation 551 @HiddenApi nativeIsTouchEvent(long nativePtr)552 protected static boolean nativeIsTouchEvent(long nativePtr) { 553 NativeInput.MotionEvent event = getNativeMotionEvent(nativePtr); 554 return event.isTouchEvent(); 555 } 556 557 @Implementation 558 @HiddenApi nativeGetFlags(long nativePtr)559 protected static int nativeGetFlags(long nativePtr) { 560 NativeInput.MotionEvent event = getNativeMotionEvent(nativePtr); 561 return event.getFlags(); 562 } 563 564 @Implementation 565 @HiddenApi nativeSetFlags(long nativePtr, int flags)566 protected static void nativeSetFlags(long nativePtr, int flags) { 567 NativeInput.MotionEvent event = getNativeMotionEvent(nativePtr); 568 event.setFlags(flags); 569 } 570 571 @Implementation 572 @HiddenApi nativeGetEdgeFlags(long nativePtr)573 protected static int nativeGetEdgeFlags(long nativePtr) { 574 NativeInput.MotionEvent event = getNativeMotionEvent(nativePtr); 575 return event.getEdgeFlags(); 576 } 577 578 @Implementation 579 @HiddenApi nativeSetEdgeFlags(long nativePtr, int edgeFlags)580 protected static void nativeSetEdgeFlags(long nativePtr, int edgeFlags) { 581 NativeInput.MotionEvent event = getNativeMotionEvent(nativePtr); 582 event.setEdgeFlags(edgeFlags); 583 } 584 585 @Implementation 586 @HiddenApi nativeGetMetaState(long nativePtr)587 protected static int nativeGetMetaState(long nativePtr) { 588 NativeInput.MotionEvent event = getNativeMotionEvent(nativePtr); 589 return event.getMetaState(); 590 } 591 592 @Implementation 593 @HiddenApi nativeGetButtonState(long nativePtr)594 protected static int nativeGetButtonState(long nativePtr) { 595 NativeInput.MotionEvent event = getNativeMotionEvent(nativePtr); 596 return event.getButtonState(); 597 } 598 599 @Implementation(minSdk = M) 600 @HiddenApi nativeSetButtonState(long nativePtr, int buttonState)601 protected static void nativeSetButtonState(long nativePtr, int buttonState) { 602 NativeInput.MotionEvent event = getNativeMotionEvent(nativePtr); 603 event.setButtonState(buttonState); 604 } 605 606 @Implementation 607 @HiddenApi nativeOffsetLocation(long nativePtr, float deltaX, float deltaY)608 protected static void nativeOffsetLocation(long nativePtr, float deltaX, float deltaY) { 609 NativeInput.MotionEvent event = getNativeMotionEvent(nativePtr); 610 event.offsetLocation(deltaX, deltaY); 611 } 612 613 @Implementation(maxSdk = UPSIDE_DOWN_CAKE) 614 @HiddenApi 615 @InDevelopment nativeGetXOffset(long nativePtr)616 protected static float nativeGetXOffset(long nativePtr) { 617 NativeInput.MotionEvent event = getNativeMotionEvent(nativePtr); 618 return event.getXOffset(); 619 } 620 621 @Implementation(maxSdk = UPSIDE_DOWN_CAKE) 622 @HiddenApi 623 @InDevelopment nativeGetYOffset(long nativePtr)624 protected static float nativeGetYOffset(long nativePtr) { 625 NativeInput.MotionEvent event = getNativeMotionEvent(nativePtr); 626 return event.getYOffset(); 627 } 628 629 @Implementation(minSdk = V.SDK_INT) split(int idBits)630 protected final MotionEvent split(int idBits) { 631 NativeInput.MotionEvent event = getNativeMotionEvent(); 632 return event.nativeSplit(idBits); 633 } 634 635 @Implementation(minSdk = V.SDK_INT) 636 @HiddenApi 637 @InDevelopment nativeGetRawXOffset(long nativePtr)638 protected static float nativeGetRawXOffset(long nativePtr) { 639 return getNativeMotionEvent(nativePtr).getXOffset(); 640 } 641 642 @Implementation(minSdk = V.SDK_INT) 643 @HiddenApi 644 @InDevelopment nativeGetRawYOffset(long nativePtr)645 protected static float nativeGetRawYOffset(long nativePtr) { 646 return getNativeMotionEvent(nativePtr).getYOffset(); 647 } 648 649 @Implementation 650 @HiddenApi nativeGetXPrecision(long nativePtr)651 protected static float nativeGetXPrecision(long nativePtr) { 652 NativeInput.MotionEvent event = getNativeMotionEvent(nativePtr); 653 return event.getXPrecision(); 654 } 655 656 @Implementation 657 @HiddenApi nativeGetYPrecision(long nativePtr)658 protected static float nativeGetYPrecision(long nativePtr) { 659 NativeInput.MotionEvent event = getNativeMotionEvent(nativePtr); 660 return event.getYPrecision(); 661 } 662 663 @Implementation 664 @HiddenApi nativeGetDownTimeNanos(long nativePtr)665 protected static long nativeGetDownTimeNanos(long nativePtr) { 666 NativeInput.MotionEvent event = getNativeMotionEvent(nativePtr); 667 return event.getDownTime(); 668 } 669 670 @Implementation 671 @HiddenApi nativeSetDownTimeNanos(long nativePtr, long downTimeNanos)672 protected static void nativeSetDownTimeNanos(long nativePtr, long downTimeNanos) { 673 NativeInput.MotionEvent event = getNativeMotionEvent(nativePtr); 674 event.setDownTime(downTimeNanos); 675 } 676 677 @Implementation 678 @HiddenApi nativeGetPointerCount(long nativePtr)679 protected static int nativeGetPointerCount(long nativePtr) { 680 NativeInput.MotionEvent event = getNativeMotionEvent(nativePtr); 681 return event.getPointerCount(); 682 } 683 684 @Implementation 685 @HiddenApi nativeFindPointerIndex(long nativePtr, int pointerId)686 protected static int nativeFindPointerIndex(long nativePtr, int pointerId) { 687 NativeInput.MotionEvent event = getNativeMotionEvent(nativePtr); 688 return event.findPointerIndex(pointerId); 689 } 690 691 @Implementation 692 @HiddenApi nativeGetHistorySize(long nativePtr)693 protected static int nativeGetHistorySize(long nativePtr) { 694 NativeInput.MotionEvent event = getNativeMotionEvent(nativePtr); 695 return event.getHistorySize(); 696 } 697 698 @Implementation 699 @HiddenApi nativeScale(long nativePtr, float scale)700 protected static void nativeScale(long nativePtr, float scale) { 701 NativeInput.MotionEvent event = getNativeMotionEvent(nativePtr); 702 event.scale(scale); 703 } 704 getNativeMotionEvent(long nativePtr)705 protected static NativeInput.MotionEvent getNativeMotionEvent(long nativePtr) { 706 // check that MotionEvent was initialized properly. This can occur if MotionEvent was mocked 707 checkState( 708 nativePtr > 0, 709 "MotionEvent has not been initialized. " 710 + "Ensure MotionEvent.obtain was used to create it, instead of creating it directly " 711 + "or via a Mocking framework"); 712 713 return nativeMotionEventRegistry.getNativeObject(nativePtr); 714 } 715 716 @Implementation transform(Matrix matrix)717 protected void transform(Matrix matrix) { 718 checkNotNull(matrix); 719 NativeInput.MotionEvent event = getNativeMotionEvent(); 720 721 float[] m = new float[9]; 722 matrix.getValues(m); 723 event.transform(m); 724 } 725 getNativeMotionEvent()726 protected NativeInput.MotionEvent getNativeMotionEvent() { 727 728 long nativePtr = ReflectionHelpers.getField(realMotionEvent, "mNativePtr"); 729 730 return nativeMotionEventRegistry.getNativeObject(nativePtr); 731 } 732 733 // Testing API methods 734 735 /** 736 * @deprecated use {@link MotionEvent#obtain} or {@link 737 * androidx.test.core.view.MotionEventBuilder} to create a MotionEvent with desired data. 738 */ 739 @Deprecated setPointer2(float pointer1X, float pointer1Y)740 public MotionEvent setPointer2(float pointer1X, float pointer1Y) { 741 NativeInput.MotionEvent event = getNativeMotionEvent(); 742 List<NativeInput.PointerCoords> pointerCoords = event.getSamplePointerCoords(); 743 List<PointerProperties> pointerProperties = event.getPointerProperties(); 744 ensureTwoPointers(pointerCoords, pointerProperties); 745 746 pointerCoords.get(1).setAxisValue(AMOTION_EVENT_AXIS_X, pointer1X); 747 pointerCoords.get(1).setAxisValue(AMOTION_EVENT_AXIS_Y, pointer1Y); 748 return realMotionEvent; 749 } 750 ensureTwoPointers( List<NativeInput.PointerCoords> pointerCoords, List<PointerProperties> pointerProperties)751 private static void ensureTwoPointers( 752 List<NativeInput.PointerCoords> pointerCoords, List<PointerProperties> pointerProperties) { 753 if (pointerCoords.size() < 2) { 754 pointerCoords.add(new NativeInput.PointerCoords()); 755 } 756 if (pointerProperties.size() < 2) { 757 pointerProperties.add(new PointerProperties()); 758 } 759 } 760 761 /** 762 * @deprecated use {@link MotionEvent#obtain} or {@link 763 * androidx.test.core.view.MotionEventBuilder#setPointerAction(int, int)} to create a 764 * MotionEvent with desired data. 765 */ 766 @Deprecated setPointerIndex(int pointerIndex)767 public void setPointerIndex(int pointerIndex) { 768 NativeInput.MotionEvent event = getNativeMotionEvent(); 769 // pointer index is stored in upper two bytes of action 770 event.setAction( 771 event.getAction() | ((pointerIndex & 0xff) << MotionEvent.ACTION_POINTER_INDEX_SHIFT)); 772 } 773 774 /** 775 * @deprecated use {@link MotionEvent#obtain} or {@link MotionEventBuilder} to create a 776 * MotionEvent with desired data 777 */ 778 @Deprecated setPointerIds(int index0PointerId, int index1PointerId)779 public void setPointerIds(int index0PointerId, int index1PointerId) { 780 NativeInput.MotionEvent event = getNativeMotionEvent(); 781 List<NativeInput.PointerCoords> pointerCoords = event.getSamplePointerCoords(); 782 List<PointerProperties> pointerProperties = event.getPointerProperties(); 783 ensureTwoPointers(pointerCoords, pointerProperties); 784 785 pointerProperties.get(0).id = index0PointerId; 786 pointerProperties.get(1).id = index1PointerId; 787 } 788 } 789