1 package org.robolectric.shadows; 2 3 import static com.google.common.base.Preconditions.checkState; 4 import static org.robolectric.shadows.NativeAndroidInput.AINPUT_EVENT_TYPE_MOTION; 5 import static org.robolectric.shadows.NativeAndroidInput.AINPUT_SOURCE_CLASS_JOYSTICK; 6 import static org.robolectric.shadows.NativeAndroidInput.AINPUT_SOURCE_CLASS_POINTER; 7 import static org.robolectric.shadows.NativeAndroidInput.AINPUT_SOURCE_CLASS_POSITION; 8 import static org.robolectric.shadows.NativeAndroidInput.AINPUT_SOURCE_MOUSE_RELATIVE; 9 import static org.robolectric.shadows.NativeAndroidInput.AKEY_EVENT_FLAG_CANCELED; 10 import static org.robolectric.shadows.NativeAndroidInput.AMOTION_EVENT_ACTION_CANCEL; 11 import static org.robolectric.shadows.NativeAndroidInput.AMOTION_EVENT_ACTION_DOWN; 12 import static org.robolectric.shadows.NativeAndroidInput.AMOTION_EVENT_ACTION_MASK; 13 import static org.robolectric.shadows.NativeAndroidInput.AMOTION_EVENT_ACTION_MOVE; 14 import static org.robolectric.shadows.NativeAndroidInput.AMOTION_EVENT_ACTION_OUTSIDE; 15 import static org.robolectric.shadows.NativeAndroidInput.AMOTION_EVENT_ACTION_POINTER_DOWN; 16 import static org.robolectric.shadows.NativeAndroidInput.AMOTION_EVENT_ACTION_POINTER_INDEX_MASK; 17 import static org.robolectric.shadows.NativeAndroidInput.AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; 18 import static org.robolectric.shadows.NativeAndroidInput.AMOTION_EVENT_ACTION_POINTER_UP; 19 import static org.robolectric.shadows.NativeAndroidInput.AMOTION_EVENT_ACTION_UP; 20 import static org.robolectric.shadows.NativeAndroidInput.AMOTION_EVENT_AXIS_ORIENTATION; 21 import static org.robolectric.shadows.NativeAndroidInput.AMOTION_EVENT_AXIS_PRESSURE; 22 import static org.robolectric.shadows.NativeAndroidInput.AMOTION_EVENT_AXIS_SIZE; 23 import static org.robolectric.shadows.NativeAndroidInput.AMOTION_EVENT_AXIS_TOOL_MAJOR; 24 import static org.robolectric.shadows.NativeAndroidInput.AMOTION_EVENT_AXIS_TOOL_MINOR; 25 import static org.robolectric.shadows.NativeAndroidInput.AMOTION_EVENT_AXIS_TOUCH_MAJOR; 26 import static org.robolectric.shadows.NativeAndroidInput.AMOTION_EVENT_AXIS_TOUCH_MINOR; 27 import static org.robolectric.shadows.NativeAndroidInput.AMOTION_EVENT_AXIS_X; 28 import static org.robolectric.shadows.NativeAndroidInput.AMOTION_EVENT_AXIS_Y; 29 30 import android.os.Parcel; 31 import android.view.MotionEvent.PointerProperties; 32 import java.util.ArrayList; 33 import java.util.Arrays; 34 import java.util.List; 35 import java.util.Optional; 36 import org.robolectric.RuntimeEnvironment; 37 import org.robolectric.res.android.Ref; 38 import org.robolectric.versioning.AndroidVersions; 39 40 /** 41 * Java representation of framework native input Transliterated from oreo-mr1 (SDK 27) 42 * frameworks/native/include/input/Input.h and libs/input/Input.cpp 43 * 44 * @see <a 45 * href="https://android.googlesource.com/platform/frameworks/native/+/oreo-mr1-release/include/input/Input.h">include/input/Input.h</a> 46 * @see <a 47 * href="https://android.googlesource.com/platform/frameworks/native/+/oreo-mr1-release/libs/input/Input.cpp">libs/input/Input.cpp</a> 48 */ 49 public class NativeInput { 50 51 /* 52 * Maximum number of pointers supported per motion event. 53 * Smallest number of pointers is 1. 54 * (We want at least 10 but some touch controllers obstensibly configured for 10 pointers 55 * will occasionally emit 11. There is not much harm making this ant bigger.) 56 */ 57 private static final int MAX_POINTERS = 16; 58 /* 59 * Maximum number of samples supported per motion event. 60 */ 61 private static final int MAX_SAMPLES = 0xffff; /* UINT16_MAX */ 62 /* 63 * Maximum pointer id value supported in a motion event. 64 * Smallest pointer id is 0. 65 * (This is limited by our use of BitSet32 to track pointer assignments.) 66 */ 67 private static final int MAX_POINTER_ID = 31; 68 69 /* 70 * Declare a concrete type for the NDK's input event forward declaration. 71 */ 72 static class AInputEvent {} 73 74 /** 75 * Pointer coordinate data. 76 * 77 * <p>Deviates from original platform implementation to store axises in simple SparseArray as 78 * opposed to complicated bitset + array arrangement. 79 */ 80 static class PointerCoords { 81 82 private static final int MAX_AXES = 30; 83 84 // Bitfield of axes that are present in this structure. 85 private NativeBitSet64 bits = new NativeBitSet64(); 86 getBits()87 NativeBitSet64 getBits() { 88 return bits; 89 } 90 91 // Values of axes that are stored in this structure 92 private float[] values = new float[MAX_AXES]; 93 clear()94 public void clear() { 95 bits.clear(); 96 } 97 isEmpty()98 public boolean isEmpty() { 99 return bits.isEmpty(); 100 } 101 getAxisValue(int axis)102 public float getAxisValue(int axis) { 103 if (axis < 0 || axis > 63 || !bits.hasBit(axis)) { 104 return 0; 105 } 106 return values[bits.getIndexOfBit(axis)]; 107 } 108 setAxisValue(int axis, float value)109 public boolean setAxisValue(int axis, float value) { 110 checkState(axis >= 0 && axis <= 63, "axis out of range"); 111 int index = bits.getIndexOfBit(axis); 112 if (!bits.hasBit(axis)) { 113 if (value == 0) { 114 return true; // axes with value 0 do not need to be stored 115 } 116 117 int count = bits.count(); 118 if (count >= MAX_AXES) { 119 tooManyAxes(axis); 120 return false; 121 } 122 bits.markBit(axis); 123 for (int i = count; i > index; i--) { 124 values[i] = values[i - 1]; 125 } 126 } 127 values[index] = value; 128 return true; 129 } 130 scaleAxisValue(PointerCoords c, int axis, float scaleFactor)131 static void scaleAxisValue(PointerCoords c, int axis, float scaleFactor) { 132 float value = c.getAxisValue(axis); 133 if (value != 0) { 134 c.setAxisValue(axis, value * scaleFactor); 135 } 136 } 137 scale(float scaleFactor)138 public void scale(float scaleFactor) { 139 // No need to scale pressure or size since they are normalized. 140 // No need to scale orientation since it is meaningless to do so. 141 scaleAxisValue(this, AMOTION_EVENT_AXIS_X, scaleFactor); 142 scaleAxisValue(this, AMOTION_EVENT_AXIS_Y, scaleFactor); 143 scaleAxisValue(this, AMOTION_EVENT_AXIS_TOUCH_MAJOR, scaleFactor); 144 scaleAxisValue(this, AMOTION_EVENT_AXIS_TOUCH_MINOR, scaleFactor); 145 scaleAxisValue(this, AMOTION_EVENT_AXIS_TOOL_MAJOR, scaleFactor); 146 scaleAxisValue(this, AMOTION_EVENT_AXIS_TOOL_MINOR, scaleFactor); 147 } 148 applyOffset(float xOffset, float yOffset)149 public void applyOffset(float xOffset, float yOffset) { 150 setAxisValue(AMOTION_EVENT_AXIS_X, getX() + xOffset); 151 setAxisValue(AMOTION_EVENT_AXIS_Y, getY() + yOffset); 152 } 153 getX()154 public float getX() { 155 return getAxisValue(AMOTION_EVENT_AXIS_X); 156 } 157 getY()158 public float getY() { 159 return getAxisValue(AMOTION_EVENT_AXIS_Y); 160 } 161 readFromParcel(Parcel parcel)162 public boolean readFromParcel(Parcel parcel) { 163 bits.setValue(parcel.readLong()); 164 int count = bits.count(); 165 if (count > MAX_AXES) { 166 return false; 167 } 168 for (int i = 0; i < count; i++) { 169 values[i] = parcel.readFloat(); 170 } 171 return true; 172 } 173 writeToParcel(Parcel parcel)174 public boolean writeToParcel(Parcel parcel) { 175 parcel.writeLong(bits.getValue()); 176 int count = bits.count(); 177 for (int i = 0; i < count; i++) { 178 parcel.writeFloat(values[i]); 179 } 180 return true; 181 } 182 183 // bool operator==( PointerCoords& other) ; 184 // bool operator!=( PointerCoords& other) { 185 // return !(*this == other); 186 // } 187 copyFrom(PointerCoords other)188 public void copyFrom(PointerCoords other) { 189 bits = new NativeBitSet64(other.bits); 190 int count = bits.count(); 191 for (int i = 0; i < count; i++) { 192 values[i] = other.values[i]; 193 } 194 } 195 tooManyAxes(int axis)196 private static void tooManyAxes(int axis) { 197 // native code just logs this as warning. Be a bit more defensive for now and throw 198 throw new IllegalStateException( 199 String.format( 200 "Could not set value for axis %d because the PointerCoords structure is full and " 201 + "cannot contain more than %d axis values.", 202 axis, MAX_AXES)); 203 } 204 } 205 206 /* 207 * Input events. 208 */ 209 static class InputEvent extends AInputEvent { 210 211 protected int mDeviceId; 212 protected int mSource; 213 getType()214 public int getType() { 215 return 0; 216 } 217 getDeviceId()218 public int getDeviceId() { 219 return mDeviceId; 220 } 221 getSource()222 public int getSource() { 223 return mSource; 224 } 225 setSource(int source)226 public void setSource(int source) { 227 mSource = source; 228 } 229 initialize(int deviceId, int source)230 protected void initialize(int deviceId, int source) { 231 this.mDeviceId = deviceId; 232 this.mSource = source; 233 } 234 initialize(NativeInput.InputEvent from)235 protected void initialize(NativeInput.InputEvent from) { 236 initialize(from.getDeviceId(), from.getSource()); 237 } 238 } 239 240 /* 241 * Key events. 242 */ 243 static class KeyEvent extends InputEvent { 244 // public: 245 // virtual ~KeyEvent() { } 246 // virtual int getType() { return AINPUT_EVENT_TYPE_KEY; } 247 // int getAction() { return mAction; } 248 // int getFlags() { return mFlags; } 249 // void setFlags(int flags) { mFlags = flags; } 250 // int getKeyCode() { return mKeyCode; } 251 // int getScanCode() { return mScanCode; } 252 // int getMetaState() { return mMetaState; } 253 // int getRepeatCount() { return mRepeatCount; } 254 // nsecs_t getDownTime() { return mDownTime; } 255 // nsecs_t getEventTime() { return mEventTime; } 256 // static char* getLabel(int keyCode); 257 // static int getKeyCodeFromLabel( char* label); 258 // 259 // void initialize( 260 // int deviceId, 261 // int source, 262 // int action, 263 // int flags, 264 // int keyCode, 265 // int scanCode, 266 // int metaState, 267 // int repeatCount, 268 // nsecs_t downTime, 269 // nsecs_t eventTime); 270 // void initialize( KeyEvent& from); 271 // protected: 272 // int mAction; 273 // int mFlags; 274 // int mKeyCode; 275 // int mScanCode; 276 // int mMetaState; 277 // int mRepeatCount; 278 // nsecs_t mDownTime; 279 // nsecs_t mEventTime; 280 } 281 282 /** Motion events. */ 283 static class MotionEvent extends InputEvent { 284 285 // constants copied from android bionic/libc/include/math.h 286 @SuppressWarnings("FloatingPointLiteralPrecision") 287 private static final double M_PI = 3.14159265358979323846f; /* pi */ 288 289 @SuppressWarnings("FloatingPointLiteralPrecision") 290 private static final double M_PI_2 = 1.57079632679489661923f; /* pi/2 */ 291 292 public static final int ACTION_MASK = 0xff; 293 public static final int ACTION_DOWN = 0; 294 public static final int ACTION_UP = 1; 295 public static final int ACTION_MOVE = 2; 296 public static final int ACTION_CANCEL = 3; 297 public static final int ACTION_POINTER_DOWN = 5; 298 public static final int ACTION_POINTER_UP = 6; 299 private static final int HISTORY_CURRENT = -0x80000000; 300 public static final int FLAG_CANCELED = 0x20; 301 public static final int ACTION_POINTER_INDEX_MASK = 0xff00; 302 public static final int ACTION_POINTER_INDEX_SHIFT = 8; 303 304 private int mAction; 305 private int mActionButton; 306 private int mFlags; 307 private int mEdgeFlags; 308 private int mMetaState; 309 private int mButtonState; 310 private float mXOffset; 311 private float mYOffset; 312 private float mXPrecision; 313 private float mYPrecision; 314 private long mDownTime; 315 private List<PointerProperties> mPointerProperties = new ArrayList<>(); 316 private List<Long> mSampleEventTimes = new ArrayList<>(); 317 private List<NativeInput.PointerCoords> mSamplePointerCoords = new ArrayList<>(); 318 319 @Override getType()320 public int getType() { 321 return AINPUT_EVENT_TYPE_MOTION; 322 } 323 getAction()324 public int getAction() { 325 return mAction; 326 } 327 getActionMasked()328 public int getActionMasked() { 329 return mAction & AMOTION_EVENT_ACTION_MASK; 330 } 331 getActionIndex()332 public int getActionIndex() { 333 return (mAction & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) 334 >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; 335 } 336 setAction(int action)337 public void setAction(int action) { 338 mAction = action; 339 } 340 getFlags()341 public int getFlags() { 342 return mFlags; 343 } 344 setFlags(int flags)345 public void setFlags(int flags) { 346 mFlags = flags; 347 } 348 getEdgeFlags()349 public int getEdgeFlags() { 350 return mEdgeFlags; 351 } 352 setEdgeFlags(int edgeFlags)353 public void setEdgeFlags(int edgeFlags) { 354 mEdgeFlags = edgeFlags; 355 } 356 getMetaState()357 public int getMetaState() { 358 return mMetaState; 359 } 360 setMetaState(int metaState)361 public void setMetaState(int metaState) { 362 mMetaState = metaState; 363 } 364 getButtonState()365 public int getButtonState() { 366 return mButtonState; 367 } 368 setButtonState(int buttonState)369 public void setButtonState(int buttonState) { 370 mButtonState = buttonState; 371 } 372 getActionButton()373 public int getActionButton() { 374 return mActionButton; 375 } 376 setActionButton(int button)377 public void setActionButton(int button) { 378 mActionButton = button; 379 } 380 getXOffset()381 public float getXOffset() { 382 return mXOffset; 383 } 384 getYOffset()385 public float getYOffset() { 386 return mYOffset; 387 } 388 getXPrecision()389 public float getXPrecision() { 390 return mXPrecision; 391 } 392 getYPrecision()393 public float getYPrecision() { 394 return mYPrecision; 395 } 396 getDownTime()397 public long getDownTime() { 398 return mDownTime; 399 } 400 setDownTime(long downTime)401 public void setDownTime(long downTime) { 402 mDownTime = downTime; 403 } 404 getPointerCount()405 public int getPointerCount() { 406 return mPointerProperties.size(); 407 } 408 getPointerProperties(int pointerIndex)409 public PointerProperties getPointerProperties(int pointerIndex) { 410 return mPointerProperties.get(pointerIndex); 411 } 412 getPointerId(int pointerIndex)413 public int getPointerId(int pointerIndex) { 414 return mPointerProperties.get(pointerIndex).id; 415 } 416 getToolType(int pointerIndex)417 public int getToolType(int pointerIndex) { 418 return mPointerProperties.get(pointerIndex).toolType; 419 } 420 getEventTime()421 public long getEventTime() { 422 return mSampleEventTimes.get(getHistorySize()); 423 } 424 getRawPointerCoords(int pointerIndex)425 public PointerCoords getRawPointerCoords(int pointerIndex) { 426 427 return mSamplePointerCoords.get(getHistorySize() * getPointerCount() + pointerIndex); 428 } 429 getRawAxisValue(int axis, int pointerIndex)430 public float getRawAxisValue(int axis, int pointerIndex) { 431 return getRawPointerCoords(pointerIndex).getAxisValue(axis); 432 } 433 getRawX(int pointerIndex)434 public float getRawX(int pointerIndex) { 435 return getRawAxisValue(AMOTION_EVENT_AXIS_X, pointerIndex); 436 } 437 getRawY(int pointerIndex)438 public float getRawY(int pointerIndex) { 439 return getRawAxisValue(AMOTION_EVENT_AXIS_Y, pointerIndex); 440 } 441 getAxisValue(int axis, int pointerIndex)442 public float getAxisValue(int axis, int pointerIndex) { 443 float value = getRawPointerCoords(pointerIndex).getAxisValue(axis); 444 if (shouldDisregardTransformation(mSource)) { 445 return value; 446 } 447 switch (axis) { 448 case AMOTION_EVENT_AXIS_X: 449 return value + mXOffset; 450 case AMOTION_EVENT_AXIS_Y: 451 return value + mYOffset; 452 } 453 return value; 454 } 455 getX(int pointerIndex)456 public float getX(int pointerIndex) { 457 return getAxisValue(AMOTION_EVENT_AXIS_X, pointerIndex); 458 } 459 getY(int pointerIndex)460 public float getY(int pointerIndex) { 461 return getAxisValue(AMOTION_EVENT_AXIS_Y, pointerIndex); 462 } 463 getPressure(int pointerIndex)464 public float getPressure(int pointerIndex) { 465 return getAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pointerIndex); 466 } 467 getSize(int pointerIndex)468 public float getSize(int pointerIndex) { 469 return getAxisValue(AMOTION_EVENT_AXIS_SIZE, pointerIndex); 470 } 471 getTouchMajor(int pointerIndex)472 public float getTouchMajor(int pointerIndex) { 473 return getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, pointerIndex); 474 } 475 getTouchMinor(int pointerIndex)476 public float getTouchMinor(int pointerIndex) { 477 return getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, pointerIndex); 478 } 479 getToolMajor(int pointerIndex)480 public float getToolMajor(int pointerIndex) { 481 return getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, pointerIndex); 482 } 483 getToolMinor(int pointerIndex)484 public float getToolMinor(int pointerIndex) { 485 return getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, pointerIndex); 486 } 487 getOrientation(int pointerIndex)488 public float getOrientation(int pointerIndex) { 489 return getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, pointerIndex); 490 } 491 getHistorySize()492 public int getHistorySize() { 493 return mSampleEventTimes.size() - 1; 494 } 495 getHistoricalEventTime(int historicalIndex)496 public long getHistoricalEventTime(int historicalIndex) { 497 return mSampleEventTimes.get(historicalIndex); 498 } 499 getHistoricalRawPointerCoords(int pointerIndex, int historicalIndex)500 public PointerCoords getHistoricalRawPointerCoords(int pointerIndex, int historicalIndex) { 501 return mSamplePointerCoords.get(historicalIndex * getPointerCount() + pointerIndex); 502 } 503 getHistoricalRawAxisValue(int axis, int pointerIndex, int historicalIndex)504 public float getHistoricalRawAxisValue(int axis, int pointerIndex, int historicalIndex) { 505 return getHistoricalRawPointerCoords(pointerIndex, historicalIndex).getAxisValue(axis); 506 } 507 getHistoricalRawX(int pointerIndex, int historicalIndex)508 public float getHistoricalRawX(int pointerIndex, int historicalIndex) { 509 return getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_X, pointerIndex, historicalIndex); 510 } 511 getHistoricalRawY(int pointerIndex, int historicalIndex)512 public float getHistoricalRawY(int pointerIndex, int historicalIndex) { 513 return getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, pointerIndex, historicalIndex); 514 } 515 getHistoricalAxisValue(int axis, int pointerIndex, int historicalIndex)516 public float getHistoricalAxisValue(int axis, int pointerIndex, int historicalIndex) { 517 float value = getHistoricalRawPointerCoords(pointerIndex, historicalIndex).getAxisValue(axis); 518 if (shouldDisregardTransformation(mSource)) { 519 return value; 520 } 521 switch (axis) { 522 case AMOTION_EVENT_AXIS_X: 523 return value + mXOffset; 524 case AMOTION_EVENT_AXIS_Y: 525 return value + mYOffset; 526 } 527 return value; 528 } 529 getHistoricalX(int pointerIndex, int historicalIndex)530 public float getHistoricalX(int pointerIndex, int historicalIndex) { 531 return getHistoricalAxisValue(AMOTION_EVENT_AXIS_X, pointerIndex, historicalIndex); 532 } 533 getHistoricalY(int pointerIndex, int historicalIndex)534 public float getHistoricalY(int pointerIndex, int historicalIndex) { 535 return getHistoricalAxisValue(AMOTION_EVENT_AXIS_Y, pointerIndex, historicalIndex); 536 } 537 getHistoricalPressure(int pointerIndex, int historicalIndex)538 public float getHistoricalPressure(int pointerIndex, int historicalIndex) { 539 return getHistoricalAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pointerIndex, historicalIndex); 540 } 541 getHistoricalSize(int pointerIndex, int historicalIndex)542 public float getHistoricalSize(int pointerIndex, int historicalIndex) { 543 return getHistoricalAxisValue(AMOTION_EVENT_AXIS_SIZE, pointerIndex, historicalIndex); 544 } 545 getHistoricalTouchMajor(int pointerIndex, int historicalIndex)546 public float getHistoricalTouchMajor(int pointerIndex, int historicalIndex) { 547 return getHistoricalAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, pointerIndex, historicalIndex); 548 } 549 getHistoricalTouchMinor(int pointerIndex, int historicalIndex)550 public float getHistoricalTouchMinor(int pointerIndex, int historicalIndex) { 551 return getHistoricalAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, pointerIndex, historicalIndex); 552 } 553 getHistoricalToolMajor(int pointerIndex, int historicalIndex)554 public float getHistoricalToolMajor(int pointerIndex, int historicalIndex) { 555 return getHistoricalAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, pointerIndex, historicalIndex); 556 } 557 getHistoricalToolMinor(int pointerIndex, int historicalIndex)558 public float getHistoricalToolMinor(int pointerIndex, int historicalIndex) { 559 return getHistoricalAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, pointerIndex, historicalIndex); 560 } 561 getHistoricalOrientation(int pointerIndex, int historicalIndex)562 public float getHistoricalOrientation(int pointerIndex, int historicalIndex) { 563 return getHistoricalAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, pointerIndex, historicalIndex); 564 } 565 getNativePointerCoords()566 private android.view.MotionEvent.PointerCoords[] getNativePointerCoords() { 567 android.view.MotionEvent.PointerCoords[] nativePointerCoords = 568 new android.view.MotionEvent.PointerCoords[mSamplePointerCoords.size()]; 569 for (int i = 0; i < mSamplePointerCoords.size(); i++) { 570 android.view.MotionEvent.PointerCoords newPc = new android.view.MotionEvent.PointerCoords(); 571 PointerCoords pc = mSamplePointerCoords.get(i); 572 newPc.x = pc.getX(); 573 newPc.y = pc.getY(); 574 newPc.setAxisValue(AMOTION_EVENT_AXIS_X, pc.getX()); 575 newPc.setAxisValue(AMOTION_EVENT_AXIS_Y, pc.getY()); 576 nativePointerCoords[i] = newPc; 577 } 578 return nativePointerCoords; 579 } 580 resolveActionForSplitMotionEvent( int action, int flags, PointerProperties[] pointerProperties, PointerProperties[] splitPointerProperties)581 private int resolveActionForSplitMotionEvent( 582 int action, 583 int flags, 584 PointerProperties[] pointerProperties, 585 PointerProperties[] splitPointerProperties) { 586 int maskedAction = getActionMasked(); 587 if (maskedAction != AMOTION_EVENT_ACTION_POINTER_DOWN 588 && maskedAction != AMOTION_EVENT_ACTION_POINTER_UP) { 589 // The action is unaffected by splitting this motion event. 590 return action; 591 } 592 593 int actionIndex = getActionIndex(); 594 595 int affectedPointerId = pointerProperties[actionIndex].id; 596 Optional<Integer> splitActionIndex = Optional.empty(); 597 for (int i = 0; i < splitPointerProperties.length; i++) { 598 if (affectedPointerId == splitPointerProperties[i].id) { 599 splitActionIndex = Optional.of(i); 600 break; 601 } 602 } 603 if (!splitActionIndex.isPresent()) { 604 // The affected pointer is not part of the split motion event. 605 return AMOTION_EVENT_ACTION_MOVE; 606 } 607 608 if (splitPointerProperties.length > 1) { 609 return maskedAction | (splitActionIndex.get() << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); 610 } 611 612 if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) { 613 return ((flags & AKEY_EVENT_FLAG_CANCELED) != 0) 614 ? AMOTION_EVENT_ACTION_CANCEL 615 : AMOTION_EVENT_ACTION_UP; 616 } 617 return AMOTION_EVENT_ACTION_DOWN; 618 } 619 nativeSplit(int idBits)620 public android.view.MotionEvent nativeSplit(int idBits) { 621 final int pointerCount = getPointerCount(); 622 List<PointerProperties> pointerProperties = new ArrayList<>(mPointerProperties); 623 final PointerProperties[] pp = pointerProperties.toArray(new PointerProperties[pointerCount]); 624 final android.view.MotionEvent.PointerCoords[] pc = getNativePointerCoords(); 625 626 List<PointerProperties> splitPointerProperties = new ArrayList<>(); 627 List<android.view.MotionEvent.PointerCoords> splitPointerCoords = new ArrayList<>(); 628 629 // Split the matching ids out for the new MotionEvent. 630 for (int i = 0; i < pointerCount; i++) { 631 final int idBit = 1 << pp[i].id; 632 if ((idBit & idBits) != 0) { 633 splitPointerProperties.add(pp[i]); 634 } 635 } 636 for (int i = 0; i < pc.length; i++) { 637 final int idBit = 1 << pp[i % pointerCount].id; 638 if ((idBit & idBits) != 0) { 639 splitPointerCoords.add(pc[i]); 640 } 641 } 642 643 // Convert them to arrays 644 PointerProperties[] splitPointerPropertiesArray = 645 new PointerProperties[splitPointerProperties.size()]; 646 splitPointerProperties.toArray(splitPointerPropertiesArray); 647 648 android.view.MotionEvent.PointerCoords[] splitPointerCoordsArray = 649 new android.view.MotionEvent.PointerCoords[splitPointerCoords.size()]; 650 splitPointerCoords.toArray(splitPointerCoordsArray); 651 652 int splitAction = 653 resolveActionForSplitMotionEvent( 654 getAction(), getFlags(), pp, splitPointerPropertiesArray); 655 656 android.view.MotionEvent newEvent = 657 android.view.MotionEvent.obtain( 658 getDownTime(), 659 getEventTime(), 660 splitAction, 661 splitPointerProperties.size(), 662 splitPointerPropertiesArray, 663 splitPointerCoordsArray, 664 getMetaState(), 665 getButtonState(), 666 getXPrecision(), 667 getYPrecision(), 668 getDeviceId(), 669 getEdgeFlags(), 670 getSource(), 671 getFlags()); 672 return newEvent; 673 } 674 findPointerIndex(int pointerId)675 public int findPointerIndex(int pointerId) { 676 int pointerCount = mPointerProperties.size(); 677 for (int i = 0; i < pointerCount; i++) { 678 if (mPointerProperties.get(i).id == pointerId) { 679 return i; 680 } 681 } 682 return -1; 683 } 684 initialize( int deviceId, int source, int action, int actionButton, int flags, int edgeFlags, int metaState, int buttonState, float xOffset, float yOffset, float xPrecision, float yPrecision, long downTime, long eventTime, int pointerCount, PointerProperties[] pointerProperties, NativeInput.PointerCoords[] pointerCoords)685 public void initialize( 686 int deviceId, 687 int source, 688 int action, 689 int actionButton, 690 int flags, 691 int edgeFlags, 692 int metaState, 693 int buttonState, 694 float xOffset, 695 float yOffset, 696 float xPrecision, 697 float yPrecision, 698 long downTime, 699 long eventTime, 700 int pointerCount, 701 PointerProperties[] pointerProperties, 702 NativeInput.PointerCoords[] pointerCoords) { 703 super.initialize(deviceId, source); 704 mAction = action; 705 mActionButton = actionButton; 706 mFlags = flags; 707 mEdgeFlags = edgeFlags; 708 mMetaState = metaState; 709 mButtonState = buttonState; 710 mXOffset = xOffset; 711 mYOffset = yOffset; 712 mXPrecision = xPrecision; 713 mYPrecision = yPrecision; 714 mDownTime = downTime; 715 mPointerProperties.clear(); 716 for (int i = 0; i < pointerCount; i++) { 717 PointerProperties copy = new PointerProperties(pointerProperties[i]); 718 mPointerProperties.add(copy); 719 } 720 mSampleEventTimes.clear(); 721 mSamplePointerCoords.clear(); 722 addSample(eventTime, Arrays.asList(pointerCoords).subList(0, pointerCount)); 723 } 724 copyFrom(MotionEvent other, boolean keepHistory)725 public void copyFrom(MotionEvent other, boolean keepHistory) { 726 super.initialize(other.getDeviceId(), other.getSource()); 727 mAction = other.mAction; 728 mActionButton = other.mActionButton; 729 mFlags = other.mFlags; 730 mEdgeFlags = other.mEdgeFlags; 731 mMetaState = other.mMetaState; 732 mButtonState = other.mButtonState; 733 mXOffset = other.mXOffset; 734 mYOffset = other.mYOffset; 735 mXPrecision = other.mXPrecision; 736 mYPrecision = other.mYPrecision; 737 mDownTime = other.mDownTime; 738 mPointerProperties.clear(); 739 for (PointerProperties pointerProperties : other.mPointerProperties) { 740 mPointerProperties.add(new PointerProperties(pointerProperties)); 741 } 742 mSampleEventTimes.clear(); 743 mSamplePointerCoords.clear(); 744 if (keepHistory) { 745 mSampleEventTimes.addAll(other.mSampleEventTimes); 746 mSamplePointerCoords.addAll(other.mSamplePointerCoords); 747 } else { 748 mSampleEventTimes.add(other.getEventTime()); 749 int pointerCount = other.getPointerCount(); 750 int historySize = other.getHistorySize(); 751 // mSamplePointerCoords.appendArray(other->mSamplePointerCoords.array() 752 // + (historySize * pointerCount), pointerCount); 753 int currentStartIndex = historySize * pointerCount; 754 mSamplePointerCoords.addAll( 755 other.mSamplePointerCoords.subList( 756 currentStartIndex, currentStartIndex + pointerCount)); 757 } 758 } 759 addSample(long eventTime, PointerCoords[] pointerCoords)760 public void addSample(long eventTime, PointerCoords[] pointerCoords) { 761 addSample(eventTime, Arrays.asList(pointerCoords)); 762 } 763 addSample(long eventTime, List<PointerCoords> pointerCoords)764 public void addSample(long eventTime, List<PointerCoords> pointerCoords) { 765 mSampleEventTimes.add(eventTime); 766 mSamplePointerCoords.addAll(pointerCoords); 767 } 768 offsetLocation(float xOffset, float yOffset)769 public void offsetLocation(float xOffset, float yOffset) { 770 mXOffset += xOffset; 771 mYOffset += yOffset; 772 } 773 scale(float scaleFactor)774 public void scale(float scaleFactor) { 775 mXOffset *= scaleFactor; 776 mYOffset *= scaleFactor; 777 mXPrecision *= scaleFactor; 778 mYPrecision *= scaleFactor; 779 int numSamples = mSamplePointerCoords.size(); 780 for (int i = 0; i < numSamples; i++) { 781 mSamplePointerCoords.get(i).scale(scaleFactor); 782 } 783 } 784 785 // Apply 3x3 perspective matrix transformation. 786 // Matrix is in row-major form and compatible with SkMatrix. transform(float[] matrix)787 public void transform(float[] matrix) { 788 checkState(matrix.length == 9); 789 // The tricky part of this implementation is to preserve the value of 790 // rawX and rawY. So we apply the transformation to the first point 791 // then derive an appropriate new X/Y offset that will preserve rawX 792 // and rawY for that point. 793 float oldXOffset = mXOffset; 794 float oldYOffset = mYOffset; 795 final Ref<Float> newX = new Ref<>(0f); 796 final Ref<Float> newY = new Ref<>(0f); 797 float rawX = getRawX(0); 798 float rawY = getRawY(0); 799 transformPoint(matrix, rawX + oldXOffset, rawY + oldYOffset, newX, newY); 800 mXOffset = newX.get() - rawX; 801 mYOffset = newY.get() - rawY; 802 // Determine how the origin is transformed by the matrix so that we 803 // can transform orientation vectors. 804 final Ref<Float> originX = new Ref<>(0f); 805 final Ref<Float> originY = new Ref<>(0f); 806 transformPoint(matrix, 0, 0, originX, originY); 807 // Apply the transformation to all samples. 808 int numSamples = mSamplePointerCoords.size(); 809 for (int i = 0; i < numSamples; i++) { 810 PointerCoords c = mSamplePointerCoords.get(i); 811 final Ref<Float> x = new Ref<>(c.getAxisValue(AMOTION_EVENT_AXIS_X) + oldXOffset); 812 final Ref<Float> y = new Ref<>(c.getAxisValue(AMOTION_EVENT_AXIS_Y) + oldYOffset); 813 transformPoint(matrix, x.get(), y.get(), x, y); 814 c.setAxisValue(AMOTION_EVENT_AXIS_X, x.get() - mXOffset); 815 c.setAxisValue(AMOTION_EVENT_AXIS_Y, y.get() - mYOffset); 816 float orientation = c.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION); 817 c.setAxisValue( 818 AMOTION_EVENT_AXIS_ORIENTATION, 819 transformAngle(matrix, orientation, originX.get(), originY.get())); 820 } 821 } 822 transformPoint( float[] matrix, float x, float y, Ref<Float> outX, Ref<Float> outY)823 private static void transformPoint( 824 float[] matrix, float x, float y, Ref<Float> outX, Ref<Float> outY) { 825 checkState(matrix.length == 9); 826 // Apply perspective transform like Skia. 827 float newX = matrix[0] * x + matrix[1] * y + matrix[2]; 828 float newY = matrix[3] * x + matrix[4] * y + matrix[5]; 829 float newZ = matrix[6] * x + matrix[7] * y + matrix[8]; 830 if (newZ != 0) { 831 newZ = 1.0f / newZ; 832 } 833 outX.set(newX * newZ); 834 outY.set(newY * newZ); 835 } 836 transformAngle(float[] matrix, float angleRadians, float originX, float originY)837 static float transformAngle(float[] matrix, float angleRadians, float originX, float originY) { 838 checkState(matrix.length == 9); 839 // ruct and transform a vector oriented at the specified clockwise angle from vertical. 840 // Coordinate system: down is increasing Y, right is increasing X. 841 final Ref<Float> x = new Ref<>((float) Math.sin(angleRadians)); 842 final Ref<Float> y = new Ref<>(-(float) Math.cos(angleRadians)); 843 transformPoint(matrix, x.get(), y.get(), x, y); 844 x.set(x.get() - originX); 845 y.set(y.get() - originY); 846 // Derive the transformed vector's clockwise angle from vertical. 847 double result = Math.atan2(x.get(), -y.get()); 848 if (result < -M_PI_2) { 849 result += M_PI; 850 } else if (result > M_PI_2) { 851 result -= M_PI; 852 } 853 return (float) result; 854 } 855 readFromParcel(Parcel parcel)856 public boolean readFromParcel(Parcel parcel) { 857 int pointerCount = parcel.readInt(); 858 int sampleCount = parcel.readInt(); 859 if (pointerCount == 0 860 || pointerCount > MAX_POINTERS 861 || sampleCount == 0 862 || sampleCount > MAX_SAMPLES) { 863 return false; 864 } 865 mDeviceId = parcel.readInt(); 866 mSource = parcel.readInt(); 867 mAction = parcel.readInt(); 868 mActionButton = parcel.readInt(); 869 mFlags = parcel.readInt(); 870 mEdgeFlags = parcel.readInt(); 871 mMetaState = parcel.readInt(); 872 mButtonState = parcel.readInt(); 873 mXOffset = parcel.readFloat(); 874 mYOffset = parcel.readFloat(); 875 mXPrecision = parcel.readFloat(); 876 mYPrecision = parcel.readFloat(); 877 mDownTime = parcel.readLong(); 878 mPointerProperties = new ArrayList<>(pointerCount); 879 mSampleEventTimes = new ArrayList<>(sampleCount); 880 mSamplePointerCoords = new ArrayList<>(sampleCount * pointerCount); 881 for (int i = 0; i < pointerCount; i++) { 882 PointerProperties properties = new PointerProperties(); 883 mPointerProperties.add(properties); 884 properties.id = parcel.readInt(); 885 properties.toolType = parcel.readInt(); 886 } 887 while (sampleCount > 0) { 888 sampleCount--; 889 mSampleEventTimes.add(parcel.readLong()); 890 for (int i = 0; i < pointerCount; i++) { 891 NativeInput.PointerCoords pointerCoords = new NativeInput.PointerCoords(); 892 mSamplePointerCoords.add(pointerCoords); 893 if (!pointerCoords.readFromParcel(parcel)) { 894 return false; 895 } 896 } 897 } 898 return true; 899 } 900 writeToParcel(Parcel parcel)901 public boolean writeToParcel(Parcel parcel) { 902 int pointerCount = mPointerProperties.size(); 903 int sampleCount = mSampleEventTimes.size(); 904 parcel.writeInt(pointerCount); 905 parcel.writeInt(sampleCount); 906 parcel.writeInt(mDeviceId); 907 parcel.writeInt(mSource); 908 parcel.writeInt(mAction); 909 parcel.writeInt(mActionButton); 910 parcel.writeInt(mFlags); 911 parcel.writeInt(mEdgeFlags); 912 parcel.writeInt(mMetaState); 913 parcel.writeInt(mButtonState); 914 parcel.writeFloat(mXOffset); 915 parcel.writeFloat(mYOffset); 916 parcel.writeFloat(mXPrecision); 917 parcel.writeFloat(mYPrecision); 918 parcel.writeLong(mDownTime); 919 for (int i = 0; i < pointerCount; i++) { 920 PointerProperties properties = mPointerProperties.get(i); 921 parcel.writeInt(properties.id); 922 parcel.writeInt(properties.toolType); 923 } 924 for (int h = 0; h < sampleCount; h++) { 925 parcel.writeLong(mSampleEventTimes.get(h)); 926 for (int i = 0; i < pointerCount; i++) { 927 if (!mSamplePointerCoords.get(i).writeToParcel(parcel)) { 928 return false; 929 } 930 } 931 } 932 return true; 933 } 934 isTouchEvent(int source, int action)935 public static boolean isTouchEvent(int source, int action) { 936 if ((source & AINPUT_SOURCE_CLASS_POINTER) != 0) { 937 // Specifically excludes HOVER_MOVE and SCROLL. 938 switch (action & AMOTION_EVENT_ACTION_MASK) { 939 case AMOTION_EVENT_ACTION_DOWN: 940 case AMOTION_EVENT_ACTION_MOVE: 941 case AMOTION_EVENT_ACTION_UP: 942 case AMOTION_EVENT_ACTION_POINTER_DOWN: 943 case AMOTION_EVENT_ACTION_POINTER_UP: 944 case AMOTION_EVENT_ACTION_CANCEL: 945 case AMOTION_EVENT_ACTION_OUTSIDE: 946 return true; 947 } 948 } 949 return false; 950 } 951 isTouchEvent()952 public boolean isTouchEvent() { 953 return isTouchEvent(getSource(), mAction); 954 } 955 956 // Low-level accessors. getPointerProperties()957 public List<PointerProperties> getPointerProperties() { 958 return mPointerProperties; 959 } 960 getSampleEventTimes()961 List<Long> getSampleEventTimes() { 962 return mSampleEventTimes; 963 } 964 getSamplePointerCoords()965 List<NativeInput.PointerCoords> getSamplePointerCoords() { 966 return mSamplePointerCoords; 967 } 968 969 // frameworks/native/libs/input/Input.cpp#isFromSource isFromSource(int source, int test)970 private static boolean isFromSource(int source, int test) { 971 return (source & test) == test; 972 } 973 974 // frameworks/native/libs/input/Input.cpp#shouldDisregardTransformation shouldDisregardTransformation(int source)975 private static boolean shouldDisregardTransformation(int source) { 976 // From the ctesque test result, the offsetLocation is not supported by non pointer source 977 // sources from Android 12L. So this method expects itself can work from Android 12L. 978 if (RuntimeEnvironment.getApiLevel() >= AndroidVersions.Sv2.SDK_INT) { 979 // See 980 // https://cs.android.com/android/_/android/platform/frameworks/native/+/7e1ee565b3fe4738e6771bceb2e9679562232992. 981 // Do not apply any transformations to axes from joysticks, touchpads, or relative mice. 982 return isFromSource(source, AINPUT_SOURCE_CLASS_JOYSTICK) 983 || isFromSource(source, AINPUT_SOURCE_CLASS_POSITION) 984 || isFromSource(source, AINPUT_SOURCE_MOUSE_RELATIVE); 985 } else { 986 return false; 987 } 988 } 989 } 990 } 991