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