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