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