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