• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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