1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.view.cts;
18 
19 import static android.view.cts.MotionEventUtils.withCoords;
20 import static android.view.cts.MotionEventUtils.withProperties;
21 
22 import static com.android.cts.input.inputeventmatchers.InputEventMatchersKt.withDeviceId;
23 import static com.android.cts.input.inputeventmatchers.InputEventMatchersKt.withDownTime;
24 import static com.android.cts.input.inputeventmatchers.InputEventMatchersKt.withEdgeFlags;
25 import static com.android.cts.input.inputeventmatchers.InputEventMatchersKt.withEventTime;
26 import static com.android.cts.input.inputeventmatchers.InputEventMatchersKt.withMetaState;
27 import static com.android.cts.input.inputeventmatchers.InputEventMatchersKt.withMotionAction;
28 import static com.android.cts.input.inputeventmatchers.InputEventMatchersKt.withPressure;
29 import static com.android.cts.input.inputeventmatchers.InputEventMatchersKt.withRawCoords;
30 import static com.android.cts.input.inputeventmatchers.InputEventMatchersKt.withSize;
31 import static com.android.cts.input.inputeventmatchers.InputEventMatchersKt.withXPrecision;
32 import static com.android.cts.input.inputeventmatchers.InputEventMatchersKt.withYPrecision;
33 
34 import static org.hamcrest.CoreMatchers.allOf;
35 import static org.hamcrest.CoreMatchers.is;
36 import static org.hamcrest.CoreMatchers.notNullValue;
37 import static org.hamcrest.MatcherAssert.assertThat;
38 import static org.junit.Assert.assertEquals;
39 import static org.junit.Assert.assertFalse;
40 import static org.junit.Assert.assertNotNull;
41 import static org.junit.Assert.assertNotSame;
42 import static org.junit.Assert.assertTrue;
43 import static org.junit.Assert.fail;
44 
45 import android.graphics.Matrix;
46 import android.graphics.PointF;
47 import android.os.Parcel;
48 import android.os.Parcelable;
49 import android.os.SystemClock;
50 import android.platform.test.annotations.AppModeSdkSandbox;
51 import android.text.TextUtils;
52 import android.view.InputDevice;
53 import android.view.KeyEvent;
54 import android.view.MotionEvent;
55 import android.view.MotionEvent.PointerCoords;
56 import android.view.MotionEvent.PointerProperties;
57 import android.view.cts.MotionEventUtils.PointerCoordsBuilder;
58 import android.view.cts.MotionEventUtils.PointerPropertiesBuilder;
59 import android.view.cts.util.NativeHeapLeakDetector;
60 
61 import androidx.test.filters.SmallTest;
62 import androidx.test.runner.AndroidJUnit4;
63 
64 import com.android.cts.input.inputeventmatchers.InputEventMatchersKt;
65 
66 import org.junit.After;
67 import org.junit.Before;
68 import org.junit.Test;
69 import org.junit.runner.RunWith;
70 
71 import java.util.LinkedHashSet;
72 import java.util.Set;
73 
74 /**
75  * Test {@link MotionEvent}.
76  */
77 @SmallTest
78 @RunWith(AndroidJUnit4.class)
79 @AppModeSdkSandbox(reason = "Allow test in the SDK sandbox (does not prevent other modes).")
80 public class MotionEventTest {
81     private MotionEvent mMotionEvent1;
82     private MotionEvent mMotionEvent2;
83     private MotionEvent mMotionEventDynamic;
84     private long mDownTime;
85     private long mEventTime;
86     private long mEventTimeNano;
87     private static final int NS_PER_MS = 1_000_000;
88     private static final float X_3F                = 3.0f;
89     private static final float Y_4F                = 4.0f;
90     private static final int META_STATE            = KeyEvent.META_SHIFT_ON;
91     private static final float PRESSURE_1F         = 1.0f;
92     private static final float SIZE_1F             = 1.0f;
93     private static final float X_PRECISION_3F      = 3.0f;
94     private static final float Y_PRECISION_4F      = 4.0f;
95     private static final int DEVICE_ID_1           = 1;
96     private static final int EDGE_FLAGS            = MotionEvent.EDGE_TOP;
97     private static final float DELTA               = 0.01f;
98     private static final float RAW_COORD_TOLERANCE = 0.001f;
99 
100     // Underestimated from ~528 B to breach threshold when leaked
101     private static final int APPROX_MOTION_EVENT_SIZE_BYTES = 500;
102     private static final int NUM_MOTION_EVENT_ALLOCATIONS =
103             NativeHeapLeakDetector.MEMORY_LEAK_THRESHOLD_KB * 1024 / APPROX_MOTION_EVENT_SIZE_BYTES;
104 
nativeMotionEventTest(MotionEvent event)105     private static native void nativeMotionEventTest(MotionEvent event);
106 
obtainNativeMotionEventCopyFromJava(MotionEvent event)107     private static native void obtainNativeMotionEventCopyFromJava(MotionEvent event);
108 
obtainMotionEventCopyFromNative(MotionEvent event)109     private static native MotionEvent obtainMotionEventCopyFromNative(MotionEvent event);
110 
111     static {
112         System.loadLibrary("ctsview_jni");
113     }
114 
115     @Before
setup()116     public void setup() {
117         mDownTime = SystemClock.uptimeMillis();
118         mEventTime = SystemClock.uptimeMillis();
119         mEventTimeNano = mEventTime * NS_PER_MS;
120         mMotionEvent1 = MotionEvent.obtain(mDownTime, mEventTime,
121                 MotionEvent.ACTION_MOVE, X_3F, Y_4F, META_STATE);
122         mMotionEvent2 = MotionEvent.obtain(mDownTime, mEventTime,
123                 MotionEvent.ACTION_MOVE, X_3F, Y_4F, PRESSURE_1F, SIZE_1F, META_STATE,
124                 X_PRECISION_3F, Y_PRECISION_4F, DEVICE_ID_1, EDGE_FLAGS);
125     }
126 
127     @After
teardown()128     public void teardown() {
129         if (null != mMotionEvent1) {
130             mMotionEvent1.recycle();
131         }
132         if (null != mMotionEvent2) {
133             mMotionEvent2.recycle();
134         }
135         if (null != mMotionEventDynamic) {
136             mMotionEventDynamic.recycle();
137         }
138     }
139 
140     @Test
testObtainBasic()141     public void testObtainBasic() {
142         mMotionEvent1 = MotionEvent.obtain(mDownTime, mEventTime,
143                 MotionEvent.ACTION_DOWN, X_3F, Y_4F, META_STATE);
144         assertNotNull(mMotionEvent1);
145         assertEquals(mDownTime, mMotionEvent1.getDownTime());
146         assertEquals(mEventTime, mMotionEvent1.getEventTime());
147         assertEquals(mEventTimeNano, mMotionEvent1.getEventTimeNanos());
148         assertEquals(MotionEvent.ACTION_DOWN, mMotionEvent1.getAction());
149         assertEquals(X_3F, mMotionEvent1.getX(), DELTA);
150         assertEquals(Y_4F, mMotionEvent1.getY(), DELTA);
151         assertEquals(X_3F, mMotionEvent1.getRawX(), DELTA);
152         assertEquals(Y_4F, mMotionEvent1.getRawY(), DELTA);
153         assertEquals(META_STATE, mMotionEvent1.getMetaState());
154         assertEquals(0, mMotionEvent1.getDeviceId());
155         assertEquals(0, mMotionEvent1.getEdgeFlags());
156         assertEquals(PRESSURE_1F, mMotionEvent1.getPressure(), DELTA);
157         assertEquals(SIZE_1F, mMotionEvent1.getSize(), DELTA);
158         assertEquals(1.0f, mMotionEvent1.getXPrecision(), DELTA);
159         assertEquals(1.0f, mMotionEvent1.getYPrecision(), DELTA);
160     }
161 
162     @Test
testObtainFromMotionEvent()163     public void testObtainFromMotionEvent() {
164         mMotionEventDynamic = MotionEvent.obtain(mMotionEvent2);
165         assertNotNull(mMotionEventDynamic);
166         assertEquals(mMotionEvent2.getDownTime(), mMotionEventDynamic.getDownTime());
167         assertEquals(mMotionEvent2.getEventTime(), mMotionEventDynamic.getEventTime());
168         assertEquals(mMotionEvent2.getEventTimeNanos(), mMotionEventDynamic.getEventTimeNanos());
169         assertEquals(mMotionEvent2.getAction(), mMotionEventDynamic.getAction());
170         assertEquals(mMotionEvent2.getX(), mMotionEventDynamic.getX(), DELTA);
171         assertEquals(mMotionEvent2.getY(), mMotionEventDynamic.getY(), DELTA);
172         assertEquals(mMotionEvent2.getX(), mMotionEventDynamic.getRawX(), DELTA);
173         assertEquals(mMotionEvent2.getY(), mMotionEventDynamic.getRawY(), DELTA);
174         assertEquals(mMotionEvent2.getMetaState(), mMotionEventDynamic.getMetaState());
175         assertEquals(mMotionEvent2.getDeviceId(), mMotionEventDynamic.getDeviceId());
176         assertEquals(mMotionEvent2.getEdgeFlags(), mMotionEventDynamic.getEdgeFlags());
177         assertEquals(mMotionEvent2.getPressure(), mMotionEventDynamic.getPressure(), DELTA);
178         assertEquals(mMotionEvent2.getSize(), mMotionEventDynamic.getSize(), DELTA);
179         assertEquals(mMotionEvent2.getXPrecision(), mMotionEventDynamic.getXPrecision(), DELTA);
180         assertEquals(mMotionEvent2.getYPrecision(), mMotionEventDynamic.getYPrecision(), DELTA);
181     }
182 
183     @Test
testObtainAllFields()184     public void testObtainAllFields() {
185         mMotionEventDynamic = MotionEvent.obtain(mDownTime, mEventTime,
186                 MotionEvent.ACTION_DOWN, X_3F, Y_4F, PRESSURE_1F, SIZE_1F, META_STATE,
187                 X_PRECISION_3F, Y_PRECISION_4F, DEVICE_ID_1, EDGE_FLAGS);
188         assertNotNull(mMotionEventDynamic);
189         assertEquals(mDownTime, mMotionEventDynamic.getDownTime());
190         assertEquals(mEventTime, mMotionEventDynamic.getEventTime());
191         assertEquals(mEventTimeNano, mMotionEventDynamic.getEventTimeNanos());
192         assertEquals(MotionEvent.ACTION_DOWN, mMotionEventDynamic.getAction());
193         assertEquals(X_3F, mMotionEventDynamic.getX(), DELTA);
194         assertEquals(Y_4F, mMotionEventDynamic.getY(), DELTA);
195         assertEquals(X_3F, mMotionEventDynamic.getRawX(), DELTA);
196         assertEquals(Y_4F, mMotionEventDynamic.getRawY(), DELTA);
197         assertEquals(META_STATE, mMotionEventDynamic.getMetaState());
198         assertEquals(DEVICE_ID_1, mMotionEventDynamic.getDeviceId());
199         assertEquals(EDGE_FLAGS, mMotionEventDynamic.getEdgeFlags());
200         assertEquals(PRESSURE_1F, mMotionEventDynamic.getPressure(), DELTA);
201         assertEquals(SIZE_1F, mMotionEventDynamic.getSize(), DELTA);
202         assertEquals(X_PRECISION_3F, mMotionEventDynamic.getXPrecision(), DELTA);
203         assertEquals(Y_PRECISION_4F, mMotionEventDynamic.getYPrecision(), DELTA);
204     }
205 
206     @Test
testObtainFromPropertyArrays()207     public void testObtainFromPropertyArrays() {
208         PointerCoordsBuilder coordsBuilder0 =
209                 withCoords(X_3F, Y_4F).withPressure(PRESSURE_1F).withSize(SIZE_1F).
210                         withTool(1.2f, 1.4f).withGenericAxis1(2.6f);
211         PointerCoordsBuilder coordsBuilder1 =
212                 withCoords(X_3F + 1.0f, Y_4F - 2.0f).withPressure(PRESSURE_1F + 0.2f).
213                         withSize(SIZE_1F + 0.5f).withTouch(2.2f, 0.6f).withGenericAxis1(2.6f);
214 
215         PointerPropertiesBuilder propertiesBuilder0 =
216                 withProperties(0, MotionEvent.TOOL_TYPE_FINGER);
217         PointerPropertiesBuilder propertiesBuilder1 =
218                 withProperties(1, MotionEvent.TOOL_TYPE_FINGER);
219 
220         mMotionEventDynamic = MotionEvent.obtain(mDownTime, mEventTime,
221                 MotionEvent.ACTION_MOVE, 2,
222                 new PointerProperties[] { propertiesBuilder0.build(), propertiesBuilder1.build() },
223                 new PointerCoords[] { coordsBuilder0.build(), coordsBuilder1.build() },
224                 META_STATE, 0, X_PRECISION_3F, Y_PRECISION_4F, DEVICE_ID_1, EDGE_FLAGS,
225                 InputDevice.SOURCE_TOUCHSCREEN, 0);
226 
227         // We expect to have data for two pointers
228         assertEquals(2, mMotionEventDynamic.getPointerCount());
229         assertEquals(0, mMotionEventDynamic.getPointerId(0));
230         assertEquals(1, mMotionEventDynamic.getPointerId(1));
231         assertEquals(0, mMotionEventDynamic.getFlags());
232         verifyCurrentPointerData(mMotionEventDynamic,
233                 new PointerPropertiesBuilder[] { propertiesBuilder0, propertiesBuilder1 },
234                 new PointerCoordsBuilder[] { coordsBuilder0, coordsBuilder1 });
235     }
236 
237     @Test
testObtainWithClassification()238     public void testObtainWithClassification() {
239         PointerCoordsBuilder coordsBuilder =
240                 withCoords(X_3F, Y_4F).withPressure(PRESSURE_1F).withSize(SIZE_1F)
241                         .withTool(1.2f, 1.4f).withGenericAxis1(2.6f);
242         PointerPropertiesBuilder propertiesBuilder =
243                 withProperties(0, MotionEvent.TOOL_TYPE_FINGER);
244 
245         mMotionEventDynamic = MotionEvent.obtain(mDownTime, mEventTime,
246                 MotionEvent.ACTION_MOVE, 1,
247                 new PointerProperties[] { propertiesBuilder.build() },
248                 new PointerCoords[] { coordsBuilder.build() },
249                 META_STATE, 0, X_PRECISION_3F, Y_PRECISION_4F, DEVICE_ID_1, EDGE_FLAGS,
250                 InputDevice.SOURCE_TOUCHSCREEN, 0, 0, MotionEvent.CLASSIFICATION_DEEP_PRESS);
251         assertEquals(MotionEvent.CLASSIFICATION_DEEP_PRESS,
252                 mMotionEventDynamic.getClassification());
253     }
254 
255     @Test
testObtainNoHistory()256     public void testObtainNoHistory() {
257         // Add two batch to one of our events
258         mMotionEvent2.addBatch(mEventTime + 10, X_3F + 5.0f, Y_4F + 5.0f, 0.5f, 0.5f, 0);
259         mMotionEvent2.addBatch(mEventTime + 20, X_3F + 10.0f, Y_4F + 15.0f, 2.0f, 3.0f, 0);
260         // The newly added batch should be the "new" values of the event
261         withCoords(X_3F + 10.0f, Y_4F + 15.0f).withPressure(2.0f).withSize(3.0f).
262                 verifyMatches(mMotionEvent2);
263         assertEquals(mEventTime + 20, mMotionEvent2.getEventTime());
264         assertEquals((mEventTime + 20) * NS_PER_MS, mMotionEvent2.getEventTimeNanos());
265         // We should have history with 2 entries
266         assertEquals(2, mMotionEvent2.getHistorySize());
267         // The previous data should be history at index 1
268         withCoords(X_3F + 5.0f, Y_4F + 5.0f).withPressure(0.5f).withSize(0.5f).
269                 verifyMatchesHistorical(mMotionEvent2, 1);
270         assertEquals(mEventTime + 10, mMotionEvent2.getHistoricalEventTime(1));
271         assertEquals((mEventTime + 10) * NS_PER_MS, mMotionEvent2.getHistoricalEventTimeNanos(1));
272         // And the original data should be history at index 0
273         withCoords(X_3F, Y_4F).withPressure(1.0f).withSize(1.0f).
274                 verifyMatchesHistorical(mMotionEvent2, 0);
275         assertEquals(mEventTime, mMotionEvent2.getHistoricalEventTime(0));
276         assertEquals(mEventTimeNano, mMotionEvent2.getHistoricalEventTimeNanos(0));
277 
278         assertEquals(2, mMotionEvent2.getHistorySize());
279 
280         mMotionEventDynamic = MotionEvent.obtainNoHistory(mMotionEvent2);
281         // The newly obtained event should have the matching current content
282         withCoords(X_3F + 10.0f, Y_4F + 15.0f).withPressure(2.0f).withSize(3.0f).
283                 verifyMatches(mMotionEvent2);
284         // And no history
285         assertEquals(0, mMotionEventDynamic.getHistorySize());
286     }
287 
288     @Test
testFlagCanceled()289     public void testFlagCanceled() {
290         final long eventTime = SystemClock.uptimeMillis();
291         MotionEvent event = MotionEvent.obtain(eventTime, eventTime, MotionEvent.ACTION_CANCEL,
292                         /*x=*/ 0, /*y=*/ 0, 0);
293         assertEquals(MotionEvent.FLAG_CANCELED, event.getFlags() & MotionEvent.FLAG_CANCELED);
294     }
295 
296     @Test
testAccessAction()297     public void testAccessAction() {
298         assertEquals(MotionEvent.ACTION_MOVE, mMotionEvent1.getAction());
299 
300         mMotionEvent1.setAction(MotionEvent.ACTION_UP);
301         assertEquals(MotionEvent.ACTION_UP, mMotionEvent1.getAction());
302 
303         mMotionEvent1.setAction(MotionEvent.ACTION_MOVE);
304         assertEquals(MotionEvent.ACTION_MOVE, mMotionEvent1.getAction());
305 
306         mMotionEvent1.setAction(MotionEvent.ACTION_CANCEL);
307         assertEquals(MotionEvent.ACTION_CANCEL, mMotionEvent1.getAction());
308         assertEquals(MotionEvent.FLAG_CANCELED,
309                         mMotionEvent1.getFlags() & MotionEvent.FLAG_CANCELED);
310 
311         mMotionEvent1.setAction(MotionEvent.ACTION_DOWN);
312         assertEquals(MotionEvent.ACTION_DOWN, mMotionEvent1.getAction());
313         assertEquals(0, mMotionEvent1.getFlags() & MotionEvent.FLAG_CANCELED);
314     }
315 
316     @Test
testDescribeContents()317     public void testDescribeContents() {
318         // make sure this method never throw any exception.
319         mMotionEvent2.describeContents();
320     }
321 
322     @Test
testAccessEdgeFlags()323     public void testAccessEdgeFlags() {
324         assertEquals(EDGE_FLAGS, mMotionEvent2.getEdgeFlags());
325 
326         int edgeFlags = 10;
327         mMotionEvent2.setEdgeFlags(edgeFlags);
328         assertEquals(edgeFlags, mMotionEvent2.getEdgeFlags());
329     }
330 
331     @Test
testWriteToParcel()332     public void testWriteToParcel() {
333         Parcel parcel = Parcel.obtain();
334         mMotionEvent2.writeToParcel(parcel, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
335         parcel.setDataPosition(0);
336 
337         MotionEvent motionEvent = MotionEvent.CREATOR.createFromParcel(parcel);
338         assertEquals(mMotionEvent2.getRawY(), motionEvent.getRawY(), DELTA);
339         assertEquals(mMotionEvent2.getRawX(), motionEvent.getRawX(), DELTA);
340         assertEquals(mMotionEvent2.getY(), motionEvent.getY(), DELTA);
341         assertEquals(mMotionEvent2.getX(), motionEvent.getX(), DELTA);
342         assertEquals(mMotionEvent2.getAction(), motionEvent.getAction());
343         assertEquals(mMotionEvent2.getDownTime(), motionEvent.getDownTime());
344         assertEquals(mMotionEvent2.getEventTime(), motionEvent.getEventTime());
345         assertEquals(mMotionEvent2.getEventTimeNanos(), motionEvent.getEventTimeNanos());
346         assertEquals(mMotionEvent2.getEdgeFlags(), motionEvent.getEdgeFlags());
347         assertEquals(mMotionEvent2.getDeviceId(), motionEvent.getDeviceId());
348     }
349 
350     @Test
testReadFromParcelWithInvalidPointerCountSize()351     public void testReadFromParcelWithInvalidPointerCountSize() {
352         Parcel parcel = Parcel.obtain();
353         mMotionEvent2.writeToParcel(parcel, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
354 
355         // Move to pointer id count.
356         parcel.setDataPosition(4);
357         // Use a very large pointer count, which should make this parcel invalid.
358         parcel.writeInt(117);
359 
360         parcel.setDataPosition(0);
361         try {
362             MotionEvent.CREATOR.createFromParcel(parcel);
363             fail("deserialized invalid parcel");
364         } catch (RuntimeException e) {
365             // Expected.
366         }
367     }
368 
369     @Test
testReadFromParcelWithInvalidSampleSize()370     public void testReadFromParcelWithInvalidSampleSize() {
371         Parcel parcel = Parcel.obtain();
372         mMotionEvent2.writeToParcel(parcel, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
373 
374         // Move to sample count.
375         parcel.setDataPosition(2 * 4);
376         parcel.writeInt(0x000f0000);
377 
378         parcel.setDataPosition(0);
379         try {
380             MotionEvent.CREATOR.createFromParcel(parcel);
381             fail("deserialized invalid parcel");
382         } catch (RuntimeException e) {
383             // Expected.
384         }
385     }
386 
387     @SuppressWarnings("ReturnValueIgnored")
388     @Test
testToString()389     public void testToString() {
390         // make sure this method never throw exception.
391         mMotionEvent2.toString();
392     }
393 
394     @Test
testOffsetLocationForPointerSource()395     public void testOffsetLocationForPointerSource() {
396         assertEquals(X_3F, mMotionEvent2.getX(), DELTA);
397         assertEquals(Y_4F, mMotionEvent2.getY(), DELTA);
398         mMotionEvent2.setSource(InputDevice.SOURCE_TOUCHSCREEN);
399 
400         float offsetX = 1.0f;
401         float offsetY = 1.0f;
402         mMotionEvent2.offsetLocation(offsetX, offsetY);
403         withCoords(X_3F + offsetX, Y_4F + offsetY)
404                 .withPressure(PRESSURE_1F)
405                 .withSize(SIZE_1F)
406                 .verifyMatches(mMotionEvent2);
407     }
408 
409     @Test
testNoLocationOffsetForNonPointerSource()410     public void testNoLocationOffsetForNonPointerSource() {
411         assertEquals(X_3F, mMotionEvent2.getX(), DELTA);
412         assertEquals(Y_4F, mMotionEvent2.getY(), DELTA);
413         mMotionEvent2.setSource(InputDevice.SOURCE_TOUCHPAD);
414 
415         float offsetX = 1.0f;
416         float offsetY = 1.0f;
417         mMotionEvent2.offsetLocation(offsetX, offsetY);
418         withCoords(X_3F, Y_4F)
419                 .withPressure(PRESSURE_1F)
420                 .withSize(SIZE_1F)
421                 .verifyMatches(mMotionEvent2);
422     }
423 
424     @Test
testSetLocation()425     public void testSetLocation() {
426         assertEquals(X_3F, mMotionEvent2.getX(), DELTA);
427         assertEquals(Y_4F, mMotionEvent2.getY(), DELTA);
428         mMotionEvent2.setSource(InputDevice.SOURCE_TOUCHSCREEN);
429 
430         mMotionEvent2.setLocation(0.0f, 0.0f);
431         withCoords(0.0f, 0.0f).withPressure(PRESSURE_1F).withSize(SIZE_1F).
432                 verifyMatches(mMotionEvent2);
433 
434         mMotionEvent2.setLocation(2.0f, 2.0f);
435         withCoords(2.0f, 2.0f).withPressure(PRESSURE_1F).withSize(SIZE_1F).
436                 verifyMatches(mMotionEvent2);
437     }
438 
439     @Test
testGetHistoricalData()440     public void testGetHistoricalData() {
441         assertEquals(0, mMotionEvent2.getHistorySize());
442 
443         mMotionEvent2.addBatch(mEventTime + 10, X_3F + 5.0f, Y_4F + 5.0f, 0.5f, 0.5f, 0);
444         // The newly added batch should be the "new" values of the event
445         withCoords(X_3F + 5.0f, Y_4F + 5.0f).withPressure(0.5f).withSize(0.5f).
446                 verifyMatches(mMotionEvent2);
447         assertEquals(mEventTime + 10, mMotionEvent2.getEventTime());
448         assertEquals((mEventTime + 10) * NS_PER_MS, mMotionEvent2.getEventTimeNanos());
449         // We should have history with 1 entry
450         assertEquals(1, mMotionEvent2.getHistorySize());
451         // And the previous / original data should be history at index 0
452         assertEquals(1, mMotionEvent2.getHistorySize());
453         withCoords(X_3F, Y_4F).withPressure(1.0f).withSize(1.0f).
454                 verifyMatchesHistorical(mMotionEvent2, 0);
455         assertEquals(mEventTime, mMotionEvent2.getHistoricalEventTime(0));
456         assertEquals(mEventTimeNano, mMotionEvent2.getHistoricalEventTimeNanos(0));
457 
458         // Add another update batch to our event
459         mMotionEvent2.addBatch(mEventTime + 20, X_3F + 10.0f, Y_4F + 15.0f, 2.0f, 3.0f, 0);
460         // The newly added batch should be the "new" values of the event
461         withCoords(X_3F + 10.0f, Y_4F + 15.0f).withPressure(2.0f).withSize(3.0f).
462                 verifyMatches(mMotionEvent2);
463         assertEquals(mEventTime + 20, mMotionEvent2.getEventTime());
464         assertEquals((mEventTime + 20) * NS_PER_MS, mMotionEvent2.getEventTimeNanos());
465         // We should have history with 2 entries
466         assertEquals(2, mMotionEvent2.getHistorySize());
467         // The previous data should be history at index 1
468         withCoords(X_3F + 5.0f, Y_4F + 5.0f).withPressure(0.5f).withSize(0.5f).
469                 verifyMatchesHistorical(mMotionEvent2, 1);
470         assertEquals(mEventTime + 10, mMotionEvent2.getHistoricalEventTime(1));
471         assertEquals((mEventTime + 10) * NS_PER_MS, mMotionEvent2.getHistoricalEventTimeNanos(1));
472         // And the original data should be history at index 0
473         withCoords(X_3F, Y_4F).withPressure(1.0f).withSize(1.0f).
474                 verifyMatchesHistorical(mMotionEvent2, 0);
475         assertEquals(mEventTime, mMotionEvent2.getHistoricalEventTime(0));
476         assertEquals(mEventTimeNano, mMotionEvent2.getHistoricalEventTimeNanos(0));
477     }
478 
verifyCurrentPointerData(MotionEvent motionEvent, PointerPropertiesBuilder[] pointerPropertiesBuilders, PointerCoordsBuilder[] pointerCoordsBuilders)479     private static void verifyCurrentPointerData(MotionEvent motionEvent,
480             PointerPropertiesBuilder[] pointerPropertiesBuilders,
481             PointerCoordsBuilder[] pointerCoordsBuilders) {
482         assertNotNull(motionEvent);
483         assertNotNull(pointerPropertiesBuilders);
484         assertNotNull(pointerCoordsBuilders);
485         final int pointerCount = motionEvent.getPointerCount();
486         assertEquals(pointerCount, pointerPropertiesBuilders.length);
487         assertEquals(pointerCount, pointerCoordsBuilders.length);
488 
489         // Test that we have the expected data fetched via MotionEvent.getPointerCoords API
490         for (int i = 0; i < pointerCount; i++) {
491             pointerCoordsBuilders[i].verifyMatchesPointerCoords(motionEvent, i);
492         }
493 
494         // Test that we have the expected data fetched via per-field MotionEvent getter APIs
495         for (int i = 0; i < pointerCount; i++) {
496             pointerCoordsBuilders[i].verifyMatches(motionEvent, i);
497         }
498 
499         // Test that we have the expected data fetched via MotionEvent.getPointerProperties API
500         for (int i = 0; i < pointerCount; i++) {
501             pointerPropertiesBuilders[i].verifyMatchesPointerProperties(motionEvent, i);
502         }
503 
504         // Test that we have the expected data fetched via per-field MotionEvent getter APIs
505         for (int i = 0; i < pointerCount; i++) {
506             pointerPropertiesBuilders[i].verifyMatches(motionEvent, i);
507         }
508     }
509 
verifyHistoricalPointerData(MotionEvent motionEvent, PointerCoordsBuilder[] pointerCoordsBuilders, int pos)510     private static void verifyHistoricalPointerData(MotionEvent motionEvent,
511             PointerCoordsBuilder[] pointerCoordsBuilders, int pos) {
512         assertNotNull(motionEvent);
513         assertNotNull(pointerCoordsBuilders);
514         final int pointerCount = motionEvent.getPointerCount();
515         assertEquals(pointerCount, pointerCoordsBuilders.length);
516 
517         // Test that we have the expected data fetched via MotionEvent.getHistoricalPointerCoords
518         // API
519         for (int i = 0; i < pointerCount; i++) {
520             pointerCoordsBuilders[i].verifyMatchesHistoricalPointerCoords(motionEvent, i, pos);
521         }
522 
523         // Test that we have the expected data fetched via per-field MotionEvent getter APIs
524         for (int i = 0; i < pointerCount; i++) {
525             pointerCoordsBuilders[i].verifyMatchesHistorical(motionEvent, i, pos);
526         }
527     }
528 
529     @Test
testGetCurrentDataWithTwoPointers()530     public void testGetCurrentDataWithTwoPointers() {
531         PointerCoordsBuilder coordsBuilder0 =
532                 withCoords(10.0f, 20.0f).withPressure(1.2f).withSize(2.0f).withTool(1.2f,
533                         1.4f).withGenericAxis1(4.4f);
534         PointerCoordsBuilder coordsBuilder1 =
535                 withCoords(30.0f, 40.0f).withPressure(1.4f).withSize(3.0f).withTouch(2.2f,
536                         0.6f).withGenericAxis1(6.6f);
537 
538         PointerPropertiesBuilder propertiesBuilder0 =
539                 withProperties(0, MotionEvent.TOOL_TYPE_FINGER);
540         PointerPropertiesBuilder propertiesBuilder1 =
541                 withProperties(1, MotionEvent.TOOL_TYPE_FINGER);
542 
543         mMotionEventDynamic = MotionEvent.obtain(mDownTime, mEventTime,
544                 MotionEvent.ACTION_MOVE, 2,
545                 new PointerProperties[] { propertiesBuilder0.build(), propertiesBuilder1.build() },
546                 new PointerCoords[] { coordsBuilder0.build(), coordsBuilder1.build() },
547                 0, 0, 1.0f, 1.0f, 0, 0, InputDevice.SOURCE_TOUCHSCREEN, 0);
548 
549         // We expect to have data for two pointers
550         assertEquals(2, mMotionEventDynamic.getPointerCount());
551         assertEquals(0, mMotionEventDynamic.getPointerId(0));
552         assertEquals(1, mMotionEventDynamic.getPointerId(1));
553         assertEquals(0, mMotionEventDynamic.getFlags());
554         verifyCurrentPointerData(mMotionEventDynamic,
555                 new PointerPropertiesBuilder[] { propertiesBuilder0, propertiesBuilder1 },
556                 new PointerCoordsBuilder[] { coordsBuilder0, coordsBuilder1 });
557     }
558 
559     /**
560      * Verify we can get raw coordinates for specific pointers using MotionEvent#getRawX(int) and
561      * MotionEvent#getRawY(int). Also verity MotionEvent#getRawX() and MotionEvent#getRawY()
562      * returns the raw coordinates of pointer with pointer index 0.
563      */
564     @Test
testGetRawCoordsWithTwoPointers()565     public void testGetRawCoordsWithTwoPointers() {
566         PointerCoordsBuilder coordsBuilder0 =
567                 withCoords(10.0f, 20.0f).withPressure(1.2f).withSize(2.0f).withTool(1.2f,
568                         1.4f).withGenericAxis1(4.4f);
569         PointerCoordsBuilder coordsBuilder1 =
570                 withCoords(30.0f, 40.0f).withPressure(1.4f).withSize(3.0f).withTouch(2.2f,
571                         0.6f).withGenericAxis1(6.6f);
572 
573         PointerPropertiesBuilder propertiesBuilder0 =
574                 withProperties(0, MotionEvent.TOOL_TYPE_FINGER);
575         PointerPropertiesBuilder propertiesBuilder1 =
576                 withProperties(1, MotionEvent.TOOL_TYPE_FINGER);
577 
578         mMotionEventDynamic = MotionEvent.obtain(mDownTime, mEventTime,
579                 MotionEvent.ACTION_MOVE, 2,
580                 new PointerProperties[] { propertiesBuilder0.build(), propertiesBuilder1.build() },
581                 new PointerCoords[] { coordsBuilder0.build(), coordsBuilder1.build() },
582                 0, 0, 1.0f, 1.0f, 0, 0, InputDevice.SOURCE_TOUCHSCREEN, 0);
583 
584         assertEquals(10.0f, mMotionEventDynamic.getRawX(), RAW_COORD_TOLERANCE);
585         assertEquals(20.0f, mMotionEventDynamic.getRawY(), RAW_COORD_TOLERANCE);
586 
587         // Assert that getRawX returns the results for the first pointer index.
588         assertEquals(mMotionEventDynamic.getRawX(), mMotionEventDynamic.getRawX(0),
589                 RAW_COORD_TOLERANCE);
590         assertEquals(mMotionEventDynamic.getRawY(), mMotionEventDynamic.getRawY(0),
591                 RAW_COORD_TOLERANCE);
592 
593         assertEquals(30.0f, mMotionEventDynamic.getRawX(1), RAW_COORD_TOLERANCE);
594         assertEquals(40.0f, mMotionEventDynamic.getRawY(1), RAW_COORD_TOLERANCE);
595     }
596 
597 
598     @Test
testGetHistoricalDataWithTwoPointers()599     public void testGetHistoricalDataWithTwoPointers() {
600         // PHASE 1 - construct the initial data for the event
601         PointerCoordsBuilder coordsBuilderInitial0 =
602                 withCoords(10.0f, 20.0f).withPressure(1.2f).withSize(2.0f).withTool(1.2f, 1.4f).
603                         withTouch(0.7f, 0.6f).withOrientation(2.0f).withGenericAxis1(4.4f);
604         PointerCoordsBuilder coordsBuilderInitial1 =
605                 withCoords(30.0f, 40.0f).withPressure(1.4f).withSize(3.0f).withTool(1.3f, 1.7f).
606                         withTouch(2.7f, 3.6f).withOrientation(1.0f).withGenericAxis1(5.4f);
607 
608         PointerPropertiesBuilder propertiesBuilder0 =
609                 withProperties(0, MotionEvent.TOOL_TYPE_FINGER);
610         PointerPropertiesBuilder propertiesBuilder1 =
611                 withProperties(1, MotionEvent.TOOL_TYPE_FINGER);
612 
613         mMotionEventDynamic = MotionEvent.obtain(mDownTime, mEventTime,
614                 MotionEvent.ACTION_MOVE, 2,
615                 new PointerProperties[] { propertiesBuilder0.build(), propertiesBuilder1.build() },
616                 new PointerCoords[] {
617                         coordsBuilderInitial0.build(), coordsBuilderInitial1.build() },
618                 0, 0, 1.0f, 1.0f, 0, 0, InputDevice.SOURCE_TOUCHSCREEN, 0);
619 
620         // We expect to have data for two pointers
621         assertEquals(2, mMotionEventDynamic.getPointerCount());
622         assertEquals(0, mMotionEventDynamic.getPointerId(0));
623         assertEquals(1, mMotionEventDynamic.getPointerId(1));
624         assertEquals(0, mMotionEventDynamic.getFlags());
625         verifyCurrentPointerData(mMotionEventDynamic,
626                 new PointerPropertiesBuilder[] { propertiesBuilder0, propertiesBuilder1 },
627                 new PointerCoordsBuilder[] { coordsBuilderInitial0, coordsBuilderInitial1 });
628 
629         // PHASE 2 - add a new batch of data to our event
630         PointerCoordsBuilder coordsBuilderNext0 =
631                 withCoords(15.0f, 25.0f).withPressure(1.6f).withSize(2.2f).withTool(1.2f, 1.4f).
632                         withTouch(1.0f, 0.9f).withOrientation(2.2f).withGenericAxis1(7.4f);
633         PointerCoordsBuilder coordsBuilderNext1 =
634                 withCoords(35.0f, 45.0f).withPressure(1.8f).withSize(3.2f).withTool(1.2f, 1.4f).
635                         withTouch(0.7f, 0.6f).withOrientation(2.9f).withGenericAxis1(8.4f);
636 
637         mMotionEventDynamic.addBatch(mEventTime + 10,
638                 new PointerCoords[] { coordsBuilderNext0.build(), coordsBuilderNext1.build() }, 0);
639         // We still expect to have data for two pointers
640         assertEquals(2, mMotionEventDynamic.getPointerCount());
641         assertEquals(0, mMotionEventDynamic.getPointerId(0));
642         assertEquals(1, mMotionEventDynamic.getPointerId(1));
643         assertEquals(0, mMotionEventDynamic.getFlags());
644 
645         // The newly added batch should be the "new" values of the event
646         verifyCurrentPointerData(mMotionEventDynamic,
647                 new PointerPropertiesBuilder[] { propertiesBuilder0, propertiesBuilder1 },
648                 new PointerCoordsBuilder[] { coordsBuilderNext0, coordsBuilderNext1 });
649         assertEquals(mEventTime + 10, mMotionEventDynamic.getEventTime());
650         assertEquals((mEventTime + 10) * NS_PER_MS, mMotionEventDynamic.getEventTimeNanos());
651         // We should have history with 1 entry
652         assertEquals(1, mMotionEventDynamic.getHistorySize());
653         // And the previous / original data should be history at index 0
654         assertEquals(1, mMotionEventDynamic.getHistorySize());
655         verifyHistoricalPointerData(mMotionEventDynamic,
656                 new PointerCoordsBuilder[] { coordsBuilderInitial0, coordsBuilderInitial1 },
657                 0);
658 
659         // PHASE 3 - add one more new batch of data to our event
660         PointerCoordsBuilder coordsBuilderLast0 =
661                 withCoords(18.0f, 28.0f).withPressure(1.1f).withSize(2.9f).withTool(1.5f, 1.9f).
662                         withTouch(1.2f, 5.0f).withOrientation(3.1f).withGenericAxis1(1.4f);
663         PointerCoordsBuilder coordsBuilderLast1 =
664                 withCoords(38.0f, 48.0f).withPressure(1.2f).withSize(2.5f).withTool(0.2f, 0.4f).
665                         withTouch(2.7f, 4.6f).withOrientation(0.2f).withGenericAxis1(5.4f);
666 
667         mMotionEventDynamic.addBatch(mEventTime + 20,
668                 new PointerCoords[] { coordsBuilderLast0.build(), coordsBuilderLast1.build() }, 0);
669         // We still expect to have data for two pointers
670         assertEquals(2, mMotionEventDynamic.getPointerCount());
671         assertEquals(0, mMotionEventDynamic.getPointerId(0));
672         assertEquals(1, mMotionEventDynamic.getPointerId(1));
673         assertEquals(0, mMotionEventDynamic.getFlags());
674 
675         // The newly added batch should be the "new" values of the event
676         verifyCurrentPointerData(mMotionEventDynamic,
677                 new PointerPropertiesBuilder[] { propertiesBuilder0, propertiesBuilder1 },
678                 new PointerCoordsBuilder[] { coordsBuilderLast0, coordsBuilderLast1 });
679         assertEquals(mEventTime + 20, mMotionEventDynamic.getEventTime());
680         assertEquals((mEventTime + 20) * NS_PER_MS, mMotionEventDynamic.getEventTimeNanos());
681         // We should have history with 2 entries
682         assertEquals(2, mMotionEventDynamic.getHistorySize());
683         // The previous data should be history at index 1
684         verifyHistoricalPointerData(mMotionEventDynamic,
685                 new PointerCoordsBuilder[] { coordsBuilderNext0, coordsBuilderNext1 },
686                 1);
687         assertEquals(mEventTime + 10, mMotionEventDynamic.getHistoricalEventTime(1));
688         assertEquals((mEventTime + 10) * NS_PER_MS,
689                 mMotionEventDynamic.getHistoricalEventTimeNanos(1));
690         // And the original data should be history at index 0
691         verifyHistoricalPointerData(mMotionEventDynamic,
692                 new PointerCoordsBuilder[] { coordsBuilderInitial0, coordsBuilderInitial1 },
693                 0);
694         assertEquals(mEventTime, mMotionEventDynamic.getHistoricalEventTime(0));
695         assertEquals(mEventTimeNano, mMotionEventDynamic.getHistoricalEventTimeNanos(0));
696     }
697 
698     @Test
testGetHistorySize()699     public void testGetHistorySize() {
700         long eventTime = SystemClock.uptimeMillis();
701         float x = 10.0f;
702         float y = 20.0f;
703         float pressure = 1.0f;
704         float size = 1.0f;
705 
706         mMotionEvent2.setAction(MotionEvent.ACTION_DOWN);
707         assertEquals(0, mMotionEvent2.getHistorySize());
708 
709         mMotionEvent2.setAction(MotionEvent.ACTION_MOVE);
710         mMotionEvent2.addBatch(eventTime, x, y, pressure, size, 0);
711         assertEquals(1, mMotionEvent2.getHistorySize());
712     }
713 
714     @Test
testRecycle()715     public void testRecycle() {
716         mMotionEvent2.setAction(MotionEvent.ACTION_MOVE);
717         assertEquals(0, mMotionEvent2.getHistorySize());
718         mMotionEvent2.addBatch(mEventTime, 10.0f, 5.0f, 1.0f, 0.0f, 0);
719         assertEquals(1, mMotionEvent2.getHistorySize());
720 
721         mMotionEvent2.recycle();
722 
723         try {
724             mMotionEvent2.recycle();
725             fail("recycle() should throw an exception when the event has already been recycled.");
726         } catch (RuntimeException ex) {
727         }
728 
729         mMotionEvent2 = null; // since it was recycled, don't try to recycle again in tear down
730     }
731 
732     @Test(expected=IllegalArgumentException.class)
testTransformShouldThrowWhenMatrixIsNull()733     public void testTransformShouldThrowWhenMatrixIsNull() {
734         // transform() should throw an exception when matrix is null
735         mMotionEvent1.transform(null);
736     }
737 
738     @Test
testTransformShouldApplyMatrixToPointsAndPreserveRawPosition()739     public void testTransformShouldApplyMatrixToPointsAndPreserveRawPosition() {
740         // Generate some points on a circle, then assign each point to a pointer.
741         // The location of pointer 'i' is a point on a circle of radius ROTATION centered at (3,2)
742         // at an angle of ARC * i degrees clockwise relative to the Y axis.
743         // The geometrical representation is irrelevant to the test, it's just easy to generate
744         // and check rotation.  We set the orientation to the same angle.
745         // Coordinate system: down is increasing Y, right is increasing X.
746         final float PI_180 = (float) (Math.PI / 180);
747         final float RADIUS = 10;
748         final float ARC = 36;
749         final float ROTATION = ARC * 2;
750 
751         final int pointerCount = 11;
752         final int[] pointerIds = new int[pointerCount];
753         final PointerCoords[] pointerCoords = new PointerCoords[pointerCount];
754         final PointerCoords[] originalRawCoords = new PointerCoords[pointerCount];
755         for (int i = 0; i < pointerCount; i++) {
756             final PointerCoords c = new PointerCoords();
757             final float angle = (float) (i * ARC * PI_180);
758             pointerIds[i] = i;
759             pointerCoords[i] = c;
760             c.x = (float) (Math.sin(angle) * RADIUS + 3);
761             c.y = (float) (- Math.cos(angle) * RADIUS + 2);
762             c.orientation = angle;
763             originalRawCoords[i] = new PointerCoords(c);
764         }
765         final MotionEvent event = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE,
766                 pointerCount, pointerIds, pointerCoords, 0, 0, 0, 0, 0,
767                 InputDevice.SOURCE_TOUCHSCREEN, 0);
768         dump("Original points.", event);
769 
770         // Check original raw X and Y assumption.
771         for (int i = 0; i < pointerCount; i++) {
772             assertEquals(originalRawCoords[i].x, event.getRawX(i), RAW_COORD_TOLERANCE);
773             assertEquals(originalRawCoords[i].y, event.getRawY(i), RAW_COORD_TOLERANCE);
774         }
775 
776         // Now translate the motion event so the circle's origin is at (0,0).
777         event.offsetLocation(-3, -2);
778         dump("Translated points.", event);
779 
780         // Offsetting the location should preserve the raw X and Y of all pointers.
781         for (int i = 0; i < pointerCount; i++) {
782             assertEquals(originalRawCoords[i].x, event.getRawX(i), RAW_COORD_TOLERANCE);
783             assertEquals(originalRawCoords[i].y, event.getRawY(i), RAW_COORD_TOLERANCE);
784         }
785 
786         // Apply a rotation about the origin by ROTATION degrees clockwise.
787         Matrix matrix = new Matrix();
788         matrix.setRotate(ROTATION);
789         event.transform(matrix);
790         dump("Rotated points.", event);
791 
792         // Check the points.
793         for (int i = 0; i < pointerCount; i++) {
794             final PointerCoords c = pointerCoords[i];
795             event.getPointerCoords(i, c);
796 
797             final float angle = (float) ((i * ARC + ROTATION) * PI_180);
798             assertEquals(Math.sin(angle) * RADIUS, c.x, RAW_COORD_TOLERANCE);
799             assertEquals(-Math.cos(angle) * RADIUS, c.y, RAW_COORD_TOLERANCE);
800             assertEquals(Math.tan(angle), Math.tan(c.orientation), 0.1);
801 
802             // Applying the transformation should preserve the raw X and Y of all pointers.
803             assertEquals(originalRawCoords[i].x, event.getRawX(i), RAW_COORD_TOLERANCE);
804             assertEquals(originalRawCoords[i].y, event.getRawY(i), RAW_COORD_TOLERANCE);
805         }
806     }
807 
dump(String label, MotionEvent ev)808     private void dump(String label, MotionEvent ev) {
809         if (false) {
810             StringBuilder msg = new StringBuilder();
811             msg.append(label).append("\n");
812 
813             msg.append("  Raw: (").append(ev.getRawX()).append(",").append(ev.getRawY()).append(")\n");
814             int pointerCount = ev.getPointerCount();
815             for (int i = 0; i < pointerCount; i++) {
816                 msg.append("  Pointer[").append(i).append("]: (")
817                         .append(ev.getX(i)).append(",").append(ev.getY(i)).append("), orientation=")
818                         .append(ev.getOrientation(i) * 180 / Math.PI).append(" deg\n");
819             }
820 
821             android.util.Log.i("TEST", msg.toString());
822         }
823     }
824 
825     @Test
testPointerCoordsDefaultConstructor()826     public void testPointerCoordsDefaultConstructor() {
827         PointerCoords coords = new PointerCoords();
828 
829         assertEquals(0f, coords.x, 0.0f);
830         assertEquals(0f, coords.y, 0.0f);
831         assertEquals(0f, coords.pressure, 0.0f);
832         assertEquals(0f, coords.size, 0.0f);
833         assertEquals(0f, coords.touchMajor, 0.0f);
834         assertEquals(0f, coords.touchMinor, 0.0f);
835         assertEquals(0f, coords.toolMajor, 0.0f);
836         assertEquals(0f, coords.toolMinor, 0.0f);
837         assertEquals(0f, coords.orientation, 0.0f);
838     }
839 
840     @Test
testPointerCoordsCopyConstructor()841     public void testPointerCoordsCopyConstructor() {
842         PointerCoords coords = new PointerCoords();
843         coords.x = 1;
844         coords.y = 2;
845         coords.pressure = 3;
846         coords.size = 4;
847         coords.touchMajor = 5;
848         coords.touchMinor = 6;
849         coords.toolMajor = 7;
850         coords.toolMinor = 8;
851         coords.orientation = 9;
852         coords.setAxisValue(MotionEvent.AXIS_GENERIC_1, 10);
853 
854         PointerCoords copy = new PointerCoords(coords);
855         assertEquals(1f, copy.x, 0.0f);
856         assertEquals(2f, copy.y, 0.0f);
857         assertEquals(3f, copy.pressure, 0.0f);
858         assertEquals(4f, copy.size, 0.0f);
859         assertEquals(5f, copy.touchMajor, 0.0f);
860         assertEquals(6f, copy.touchMinor, 0.0f);
861         assertEquals(7f, copy.toolMajor, 0.0f);
862         assertEquals(8f, copy.toolMinor, 0.0f);
863         assertEquals(9f, copy.orientation, 0.0f);
864         assertEquals(10f, coords.getAxisValue(MotionEvent.AXIS_GENERIC_1), 0.0f);
865     }
866 
867     @Test
testPointerCoordsCopyFrom()868     public void testPointerCoordsCopyFrom() {
869         PointerCoords coords = new PointerCoords();
870         coords.x = 1;
871         coords.y = 2;
872         coords.pressure = 3;
873         coords.size = 4;
874         coords.touchMajor = 5;
875         coords.touchMinor = 6;
876         coords.toolMajor = 7;
877         coords.toolMinor = 8;
878         coords.orientation = 9;
879         coords.setAxisValue(MotionEvent.AXIS_GENERIC_1, 10);
880 
881         PointerCoords copy = new PointerCoords();
882         copy.copyFrom(coords);
883         assertEquals(1f, copy.x, 0.0f);
884         assertEquals(2f, copy.y, 0.0f);
885         assertEquals(3f, copy.pressure, 0.0f);
886         assertEquals(4f, copy.size, 0.0f);
887         assertEquals(5f, copy.touchMajor, 0.0f);
888         assertEquals(6f, copy.touchMinor, 0.0f);
889         assertEquals(7f, copy.toolMajor, 0.0f);
890         assertEquals(8f, copy.toolMinor, 0.0f);
891         assertEquals(9f, copy.orientation, 0.0f);
892         assertEquals(10f, coords.getAxisValue(MotionEvent.AXIS_GENERIC_1), 0.0f);
893     }
894 
895     @Test
testPointerPropertiesDefaultConstructor()896     public void testPointerPropertiesDefaultConstructor() {
897         PointerProperties properties = new PointerProperties();
898 
899         assertEquals(MotionEvent.INVALID_POINTER_ID, properties.id);
900         assertEquals(MotionEvent.TOOL_TYPE_UNKNOWN, properties.toolType);
901     }
902 
903     @Test
testPointerPropertiesCopyConstructor()904     public void testPointerPropertiesCopyConstructor() {
905         PointerProperties properties = new PointerProperties();
906         properties.id = 1;
907         properties.toolType = MotionEvent.TOOL_TYPE_MOUSE;
908 
909         PointerProperties copy = new PointerProperties(properties);
910         assertEquals(1, copy.id);
911         assertEquals(MotionEvent.TOOL_TYPE_MOUSE, copy.toolType);
912     }
913 
914     @Test
testPointerPropertiesCopyFrom()915     public void testPointerPropertiesCopyFrom() {
916         PointerProperties properties = new PointerProperties();
917         properties.id = 1;
918         properties.toolType = MotionEvent.TOOL_TYPE_MOUSE;
919 
920         PointerProperties copy = new PointerProperties();
921         copy.copyFrom(properties);
922         assertEquals(1, copy.id);
923         assertEquals(MotionEvent.TOOL_TYPE_MOUSE, copy.toolType);
924     }
925 
926     @Test
testActionToString()927     public void testActionToString() {
928         final int[] actions = {
929                 MotionEvent.ACTION_DOWN,
930                 MotionEvent.ACTION_UP,
931                 MotionEvent.ACTION_MOVE,
932                 MotionEvent.ACTION_CANCEL,
933                 MotionEvent.ACTION_OUTSIDE,
934                 MotionEvent.ACTION_HOVER_MOVE,
935                 MotionEvent.ACTION_SCROLL,
936                 MotionEvent.ACTION_HOVER_ENTER,
937                 MotionEvent.ACTION_HOVER_EXIT,
938                 MotionEvent.ACTION_BUTTON_PRESS,
939                 MotionEvent.ACTION_BUTTON_RELEASE
940         };
941 
942         // There is no hard guarantee on the actual return result on any specific action
943         // from MotionEvent.actionToString. Verify that we are not crashing on those calls
944         // and that the return result on each is not empty
945         for (int i = 0; i < actions.length; i++) {
946             assertFalse(TextUtils.isEmpty(MotionEvent.actionToString(actions[i])));
947         }
948 
949         final int[] pointerActions = {
950                 MotionEvent.ACTION_POINTER_UP,
951                 MotionEvent.ACTION_POINTER_DOWN
952         };
953 
954         for (int i = 0; i < pointerActions.length; i++) {
955             for (int pointer = 0; pointer < 5; pointer++) {
956                 int pointerAction =
957                         pointerActions[i] | pointer << MotionEvent.ACTION_POINTER_INDEX_SHIFT;
958                 assertFalse(TextUtils.isEmpty(MotionEvent.actionToString(pointerAction)));
959             }
960         }
961     }
962 
963     @Test
testAxisFromToString()964     public void testAxisFromToString() {
965         final int[] axes = {
966                 MotionEvent.AXIS_X,
967                 MotionEvent.AXIS_Y,
968                 MotionEvent.AXIS_PRESSURE,
969                 MotionEvent.AXIS_SIZE,
970                 MotionEvent.AXIS_TOUCH_MAJOR,
971                 MotionEvent.AXIS_TOUCH_MINOR,
972                 MotionEvent.AXIS_TOOL_MAJOR,
973                 MotionEvent.AXIS_TOOL_MINOR,
974                 MotionEvent.AXIS_ORIENTATION,
975                 MotionEvent.AXIS_VSCROLL,
976                 MotionEvent.AXIS_HSCROLL,
977                 MotionEvent.AXIS_Z,
978                 MotionEvent.AXIS_RX,
979                 MotionEvent.AXIS_RY,
980                 MotionEvent.AXIS_RZ,
981                 MotionEvent.AXIS_HAT_X,
982                 MotionEvent.AXIS_HAT_Y,
983                 MotionEvent.AXIS_LTRIGGER,
984                 MotionEvent.AXIS_RTRIGGER,
985                 MotionEvent.AXIS_THROTTLE,
986                 MotionEvent.AXIS_RUDDER,
987                 MotionEvent.AXIS_WHEEL,
988                 MotionEvent.AXIS_GAS,
989                 MotionEvent.AXIS_BRAKE,
990                 MotionEvent.AXIS_DISTANCE,
991                 MotionEvent.AXIS_TILT,
992                 MotionEvent.AXIS_SCROLL,
993                 MotionEvent.AXIS_RELATIVE_X,
994                 MotionEvent.AXIS_RELATIVE_Y,
995                 MotionEvent.AXIS_GENERIC_1,
996                 MotionEvent.AXIS_GENERIC_2,
997                 MotionEvent.AXIS_GENERIC_3,
998                 MotionEvent.AXIS_GENERIC_4,
999                 MotionEvent.AXIS_GENERIC_5,
1000                 MotionEvent.AXIS_GENERIC_6,
1001                 MotionEvent.AXIS_GENERIC_7,
1002                 MotionEvent.AXIS_GENERIC_8,
1003                 MotionEvent.AXIS_GENERIC_9,
1004                 MotionEvent.AXIS_GENERIC_10,
1005                 MotionEvent.AXIS_GENERIC_11,
1006                 MotionEvent.AXIS_GENERIC_12,
1007                 MotionEvent.AXIS_GENERIC_13,
1008                 MotionEvent.AXIS_GENERIC_14,
1009                 MotionEvent.AXIS_GENERIC_15,
1010                 MotionEvent.AXIS_GENERIC_16,
1011                 MotionEvent.AXIS_GESTURE_X_OFFSET,
1012                 MotionEvent.AXIS_GESTURE_Y_OFFSET,
1013                 MotionEvent.AXIS_GESTURE_SCROLL_X_DISTANCE,
1014                 MotionEvent.AXIS_GESTURE_SCROLL_Y_DISTANCE,
1015                 MotionEvent.AXIS_GESTURE_PINCH_SCALE_FACTOR,
1016         };
1017 
1018         // There is no hard guarantee on the actual return result on any specific axis
1019         // from MotionEvent.axisToString. Verify that we are not crashing on those calls
1020         // and that the return result on each is not empty. However, we do expect the two-way
1021         // call chain of to/from to get us back to the original integer value.
1022         for (int i = 0; i < axes.length; i++) {
1023             String axisToString = MotionEvent.axisToString(axes[i]);
1024             assertFalse(TextUtils.isEmpty(axisToString));
1025             assertEquals(axes[i], MotionEvent.axisFromString(axisToString));
1026         }
1027     }
1028 
1029     @Test
testGetActionButton()1030     public void testGetActionButton() {
1031         mMotionEventDynamic = MotionEvent.obtain(mDownTime, mEventTime,
1032                 MotionEvent.ACTION_BUTTON_PRESS, X_3F, Y_4F, 0);
1033         mMotionEventDynamic.setActionButton(MotionEvent.BUTTON_STYLUS_PRIMARY);
1034         assertEquals(MotionEvent.BUTTON_STYLUS_PRIMARY, mMotionEventDynamic.getActionButton());
1035         mMotionEventDynamic.recycle();
1036 
1037         mMotionEventDynamic = MotionEvent.obtain(mDownTime, mEventTime,
1038                 MotionEvent.ACTION_BUTTON_PRESS, X_3F, Y_4F, 0);
1039         mMotionEventDynamic.setActionButton(MotionEvent.BUTTON_SECONDARY);
1040         assertEquals(MotionEvent.BUTTON_SECONDARY, mMotionEventDynamic.getActionButton());
1041     }
1042 
1043     @Test
testIsButtonPressed()1044     public void testIsButtonPressed() {
1045         mMotionEventDynamic = MotionEvent.obtain(mDownTime, mEventTime,
1046                 MotionEvent.ACTION_DOWN, X_3F, Y_4F, 0);
1047         mMotionEventDynamic.setSource(InputDevice.SOURCE_MOUSE);
1048 
1049         mMotionEventDynamic.setButtonState(
1050                 MotionEvent.BUTTON_PRIMARY | MotionEvent.BUTTON_STYLUS_PRIMARY);
1051         assertTrue(mMotionEventDynamic.isButtonPressed(MotionEvent.BUTTON_PRIMARY));
1052         assertFalse(mMotionEventDynamic.isButtonPressed(MotionEvent.BUTTON_SECONDARY));
1053         assertFalse(mMotionEventDynamic.isButtonPressed(MotionEvent.BUTTON_TERTIARY));
1054         assertTrue(mMotionEventDynamic.isButtonPressed(MotionEvent.BUTTON_STYLUS_PRIMARY));
1055         assertFalse(mMotionEventDynamic.isButtonPressed(MotionEvent.BUTTON_STYLUS_SECONDARY));
1056         assertFalse(mMotionEventDynamic.isButtonPressed(MotionEvent.BUTTON_BACK));
1057         assertFalse(mMotionEventDynamic.isButtonPressed(MotionEvent.BUTTON_FORWARD));
1058 
1059         mMotionEventDynamic.setButtonState(MotionEvent.BUTTON_PRIMARY);
1060         assertTrue(mMotionEventDynamic.isButtonPressed(MotionEvent.BUTTON_PRIMARY));
1061         assertFalse(mMotionEventDynamic.isButtonPressed(MotionEvent.BUTTON_SECONDARY));
1062         assertFalse(mMotionEventDynamic.isButtonPressed(MotionEvent.BUTTON_TERTIARY));
1063         assertFalse(mMotionEventDynamic.isButtonPressed(MotionEvent.BUTTON_STYLUS_PRIMARY));
1064         assertFalse(mMotionEventDynamic.isButtonPressed(MotionEvent.BUTTON_STYLUS_SECONDARY));
1065         assertFalse(mMotionEventDynamic.isButtonPressed(MotionEvent.BUTTON_BACK));
1066         assertFalse(mMotionEventDynamic.isButtonPressed(MotionEvent.BUTTON_FORWARD));
1067 
1068         mMotionEventDynamic.setButtonState(
1069                 MotionEvent.BUTTON_FORWARD | MotionEvent.BUTTON_TERTIARY);
1070         assertFalse(mMotionEventDynamic.isButtonPressed(MotionEvent.BUTTON_PRIMARY));
1071         assertFalse(mMotionEventDynamic.isButtonPressed(MotionEvent.BUTTON_SECONDARY));
1072         assertTrue(mMotionEventDynamic.isButtonPressed(MotionEvent.BUTTON_TERTIARY));
1073         assertFalse(mMotionEventDynamic.isButtonPressed(MotionEvent.BUTTON_STYLUS_PRIMARY));
1074         assertFalse(mMotionEventDynamic.isButtonPressed(MotionEvent.BUTTON_STYLUS_SECONDARY));
1075         assertFalse(mMotionEventDynamic.isButtonPressed(MotionEvent.BUTTON_BACK));
1076         assertTrue(mMotionEventDynamic.isButtonPressed(MotionEvent.BUTTON_FORWARD));
1077     }
1078 
1079     @Test
testClassificationConstantsAreUnique()1080     public void testClassificationConstantsAreUnique() {
1081         Set<Integer> values = new LinkedHashSet<>();
1082         values.add(MotionEvent.CLASSIFICATION_NONE);
1083         values.add(MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE);
1084         values.add(MotionEvent.CLASSIFICATION_DEEP_PRESS);
1085         assertEquals(3, values.size());
1086     }
1087 
1088     /**
1089      * The motion events 1 and 2 were created using one of the obtain methods.
1090      * As a result, they should not have any classification.
1091      * Only events generated by the framework are allowed to have classification other than NONE.
1092      */
1093     @Test
testGetClassification()1094     public void testGetClassification() {
1095         assertEquals(MotionEvent.CLASSIFICATION_NONE, mMotionEvent1.getClassification());
1096         assertEquals(MotionEvent.CLASSIFICATION_NONE, mMotionEvent2.getClassification());
1097     }
1098 
1099     @Test
testNativeConverter()1100     public void testNativeConverter() {
1101         final MotionEvent event = MotionEvent.obtain(mDownTime, mEventTime,
1102                 MotionEvent.ACTION_BUTTON_PRESS, X_3F, Y_4F, META_STATE);
1103         event.setActionButton(MotionEvent.BUTTON_PRIMARY);
1104         nativeMotionEventTest(event);
1105     }
1106 
1107     @Test
testNativeToJavaConverter()1108     public void testNativeToJavaConverter() {
1109         final MotionEvent javaMotionEvent = MotionEvent.obtain(mDownTime, mEventTime,
1110                 MotionEvent.ACTION_DOWN, X_3F, Y_4F, PRESSURE_1F, SIZE_1F, META_STATE,
1111                 X_PRECISION_3F, Y_PRECISION_4F, DEVICE_ID_1, EDGE_FLAGS);
1112         final MotionEvent motionEventFromNative = obtainMotionEventCopyFromNative(javaMotionEvent);
1113         assertNotSame(javaMotionEvent, motionEventFromNative);
1114         assertThat(motionEventFromNative, allOf(
1115                 is(notNullValue()),
1116                 withEventTime(javaMotionEvent.getEventTime()),
1117                 withDownTime(javaMotionEvent.getDownTime()),
1118                 withMotionAction(javaMotionEvent.getAction()),
1119                 InputEventMatchersKt.withCoords(
1120                         new PointF(javaMotionEvent.getX(), javaMotionEvent.getY())
1121                 ),
1122                 withRawCoords(new PointF(javaMotionEvent.getRawX(), javaMotionEvent.getRawY())),
1123                 withDeviceId(javaMotionEvent.getDeviceId()),
1124                 withEdgeFlags(javaMotionEvent.getEdgeFlags()),
1125                 withMetaState(javaMotionEvent.getMetaState()),
1126                 withPressure(javaMotionEvent.getPressure()),
1127                 withSize(javaMotionEvent.getSize()),
1128                 withXPrecision(javaMotionEvent.getXPrecision()),
1129                 withYPrecision(javaMotionEvent.getYPrecision())
1130         ));
1131         assertEquals(
1132                 javaMotionEvent.getEventTimeNanos(),
1133                 motionEventFromNative.getEventTimeNanos()
1134         );
1135     }
1136 
1137     @Test
testNativeToJavaConverterMemoryLeak()1138     public void testNativeToJavaConverterMemoryLeak() {
1139         final MotionEvent javaMotionEvent =
1140                 MotionEvent.obtain(
1141                         mDownTime, mEventTime, MotionEvent.ACTION_DOWN, X_3F, Y_4F, META_STATE);
1142 
1143         try (NativeHeapLeakDetector d = new NativeHeapLeakDetector()) {
1144             for (int iteration = 0; iteration < NUM_MOTION_EVENT_ALLOCATIONS; ++iteration) {
1145                 obtainMotionEventCopyFromNative(javaMotionEvent);
1146             }
1147         }
1148     }
1149 
1150     @Test
testNativeToJavaConverterMemoryLeakRecylingObjects()1151     public void testNativeToJavaConverterMemoryLeakRecylingObjects() {
1152         final MotionEvent javaMotionEvent =
1153                 MotionEvent.obtain(
1154                         mDownTime, mEventTime, MotionEvent.ACTION_DOWN, X_3F, Y_4F, META_STATE);
1155 
1156         try (NativeHeapLeakDetector d = new NativeHeapLeakDetector()) {
1157             for (int iteration = 0; iteration < NUM_MOTION_EVENT_ALLOCATIONS; ++iteration) {
1158                 obtainMotionEventCopyFromNative(javaMotionEvent).recycle();
1159             }
1160         }
1161     }
1162 
1163     @Test
testJavaToNativeConverterMemoryLeak()1164     public void testJavaToNativeConverterMemoryLeak() {
1165         final MotionEvent event =
1166                 MotionEvent.obtain(
1167                         mDownTime,
1168                         mEventTime,
1169                         MotionEvent.ACTION_BUTTON_PRESS,
1170                         X_3F,
1171                         Y_4F,
1172                         META_STATE);
1173 
1174         try (NativeHeapLeakDetector d = new NativeHeapLeakDetector()) {
1175             for (int iteration = 0; iteration < NUM_MOTION_EVENT_ALLOCATIONS; ++iteration) {
1176                 obtainNativeMotionEventCopyFromJava(event);
1177             }
1178         }
1179     }
1180 
1181     @Test
testAddBatchWithTransform()1182     public void testAddBatchWithTransform() {
1183         PointerCoordsBuilder coordsBuilder0 =
1184                 withCoords(10.0f, 20.0f).withPressure(1.2f).withSize(2.0f).withTool(1.2f,
1185                         1.4f).withGenericAxis1(4.4f).withOrientation(1.0f);
1186         PointerCoordsBuilder coordsBuilder1 =
1187                 withCoords(30.0f, 40.0f).withPressure(1.4f).withSize(3.0f).withTouch(2.2f,
1188                         0.6f).withGenericAxis1(6.6f).withOrientation(-1.0f);
1189 
1190         PointerPropertiesBuilder propertiesBuilder0 =
1191                 withProperties(0, MotionEvent.TOOL_TYPE_FINGER);
1192         PointerPropertiesBuilder propertiesBuilder1 =
1193                 withProperties(1, MotionEvent.TOOL_TYPE_FINGER);
1194 
1195         mMotionEventDynamic = MotionEvent.obtain(mDownTime, mEventTime,
1196                 MotionEvent.ACTION_MOVE, 2,
1197                 new PointerProperties[]{propertiesBuilder0.build(), propertiesBuilder1.build()},
1198                 new PointerCoords[]{coordsBuilder0.build(), coordsBuilder1.build()},
1199                 0, 0, 1.0f, 1.0f, 0, 0, InputDevice.SOURCE_TOUCHSCREEN, 0);
1200 
1201         // Apply an arbitrary transform to the event.
1202         Matrix matrix = new Matrix();
1203         matrix.setRotate(90);
1204         mMotionEventDynamic.transform(matrix);
1205 
1206         // Add a new batch to our event.
1207         PointerCoordsBuilder coordsBuilderNext0 = withCoords(15.0f, 25.0f).withPressure(1.6f)
1208                 .withSize(2.2f).withTool(1.2f, 1.4f).withTouch(1.0f, 0.9f)
1209                 .withOrientation(2.2f).withGenericAxis1(7.4f);
1210         PointerCoordsBuilder coordsBuilderNext1 = withCoords(35.0f, 45.0f).withPressure(1.8f)
1211                 .withSize(3.2f).withTool(1.2f, 1.4f).withTouch(0.7f, 0.6f)
1212                 .withOrientation(2.9f).withGenericAxis1(8.4f);
1213 
1214         mMotionEventDynamic.addBatch(mEventTime + 20,
1215                 new PointerCoords[]{coordsBuilderNext0.build(), coordsBuilderNext1.build()}, 0);
1216 
1217         assertEquals(1, mMotionEventDynamic.getHistorySize());
1218         assertEquals(2, mMotionEventDynamic.getPointerCount());
1219 
1220         // The added batch should be our "new" values for our event, because the batch was added
1221         // after the event was transformed.
1222         verifyCurrentPointerData(mMotionEventDynamic,
1223                 new PointerPropertiesBuilder[]{propertiesBuilder0, propertiesBuilder1},
1224                 new PointerCoordsBuilder[]{coordsBuilderNext0, coordsBuilderNext1});
1225 
1226         // Undo the transformation.
1227         Matrix inverseMatrix = new Matrix();
1228         assertTrue(matrix.invert(inverseMatrix));
1229         mMotionEventDynamic.transform(inverseMatrix);
1230 
1231         // Now, the "old" values should match what they were initially set to.
1232         verifyHistoricalPointerData(mMotionEventDynamic,
1233                 new PointerCoordsBuilder[]{coordsBuilder0, coordsBuilder1}, 0);
1234     }
1235 }
1236