1 /*
2  * Copyright (C) 2016 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 com.android.compatibility.common.util;
18 
19 import android.app.Instrumentation;
20 import android.app.UiAutomation;
21 import android.graphics.Point;
22 import android.os.SystemClock;
23 import android.util.SparseArray;
24 import android.view.InputDevice;
25 import android.view.MotionEvent;
26 import android.view.View;
27 import android.view.ViewConfiguration;
28 import android.view.ViewGroup;
29 
30 /**
31  * Test utilities for touch emulation.
32  */
33 public final class CtsTouchUtils {
34 
CtsTouchUtils()35     private CtsTouchUtils() {}
36 
37     /**
38      * Emulates a tap in the center of the passed {@link View}.
39      *
40      * @param instrumentation the instrumentation used to run the test
41      * @param view the view to "tap"
42      */
emulateTapOnViewCenter(Instrumentation instrumentation, View view)43     public static void emulateTapOnViewCenter(Instrumentation instrumentation, View view) {
44         emulateTapOnView(instrumentation, view, view.getWidth() / 2, view.getHeight() / 2);
45     }
46 
47     /**
48      * Emulates a tap on a point relative to the top-left corner of the passed {@link View}. Offset
49      * parameters are used to compute the final screen coordinates of the tap point.
50      *
51      * @param instrumentation the instrumentation used to run the test
52      * @param anchorView the anchor view to determine the tap location on the screen
53      * @param offsetX extra X offset for the tap
54      * @param offsetY extra Y offset for the tap
55      */
emulateTapOnView(Instrumentation instrumentation, View anchorView, int offsetX, int offsetY)56     public static void emulateTapOnView(Instrumentation instrumentation, View anchorView,
57             int offsetX, int offsetY) {
58         final int touchSlop = ViewConfiguration.get(anchorView.getContext()).getScaledTouchSlop();
59         // Get anchor coordinates on the screen
60         final int[] viewOnScreenXY = new int[2];
61         anchorView.getLocationOnScreen(viewOnScreenXY);
62         int xOnScreen = viewOnScreenXY[0] + offsetX;
63         int yOnScreen = viewOnScreenXY[1] + offsetY;
64         final UiAutomation uiAutomation = instrumentation.getUiAutomation();
65         final long downTime = SystemClock.uptimeMillis();
66 
67         injectDownEvent(uiAutomation, downTime, xOnScreen, yOnScreen);
68         injectMoveEventForTap(uiAutomation, downTime, touchSlop, xOnScreen, yOnScreen);
69         injectUpEvent(uiAutomation, downTime, false, xOnScreen, yOnScreen);
70 
71         // Wait for the system to process all events in the queue
72         instrumentation.waitForIdleSync();
73     }
74 
75     /**
76      * Emulates a double tap in the center of the passed {@link View}.
77      *
78      * @param instrumentation the instrumentation used to run the test
79      * @param view the view to "double tap"
80      */
emulateDoubleTapOnViewCenter(Instrumentation instrumentation, View view)81     public static void emulateDoubleTapOnViewCenter(Instrumentation instrumentation, View view) {
82         emulateDoubleTapOnView(instrumentation, view, view.getWidth() / 2, view.getHeight() / 2);
83     }
84 
85     /**
86      * Emulates a double tap on a point relative to the top-left corner of the passed {@link View}.
87      * Offset parameters are used to compute the final screen coordinates of the tap points.
88      *
89      * @param instrumentation the instrumentation used to run the test
90      * @param anchorView the anchor view to determine the tap location on the screen
91      * @param offsetX extra X offset for the taps
92      * @param offsetY extra Y offset for the taps
93      */
emulateDoubleTapOnView(Instrumentation instrumentation, View anchorView, int offsetX, int offsetY)94     public static void emulateDoubleTapOnView(Instrumentation instrumentation, View anchorView,
95             int offsetX, int offsetY) {
96         final int touchSlop = ViewConfiguration.get(anchorView.getContext()).getScaledTouchSlop();
97         // Get anchor coordinates on the screen
98         final int[] viewOnScreenXY = new int[2];
99         anchorView.getLocationOnScreen(viewOnScreenXY);
100         int xOnScreen = viewOnScreenXY[0] + offsetX;
101         int yOnScreen = viewOnScreenXY[1] + offsetY;
102         final UiAutomation uiAutomation = instrumentation.getUiAutomation();
103         final long downTime = SystemClock.uptimeMillis();
104 
105         injectDownEvent(uiAutomation, downTime, xOnScreen, yOnScreen);
106         injectMoveEventForTap(uiAutomation, downTime, touchSlop, xOnScreen, yOnScreen);
107         injectUpEvent(uiAutomation, downTime, false, xOnScreen, yOnScreen);
108         injectDownEvent(uiAutomation, downTime, xOnScreen, yOnScreen);
109         injectMoveEventForTap(uiAutomation, downTime, touchSlop, xOnScreen, yOnScreen);
110         injectUpEvent(uiAutomation, downTime, false, xOnScreen, yOnScreen);
111 
112         // Wait for the system to process all events in the queue
113         instrumentation.waitForIdleSync();
114     }
115 
116     /**
117      * Emulates a linear drag gesture between 2 points across the screen.
118      *
119      * @param instrumentation the instrumentation used to run the test
120      * @param dragStartX Start X of the emulated drag gesture
121      * @param dragStartY Start Y of the emulated drag gesture
122      * @param dragAmountX X amount of the emulated drag gesture
123      * @param dragAmountY Y amount of the emulated drag gesture
124      */
emulateDragGesture(Instrumentation instrumentation, int dragStartX, int dragStartY, int dragAmountX, int dragAmountY)125     public static void emulateDragGesture(Instrumentation instrumentation,
126             int dragStartX, int dragStartY, int dragAmountX, int dragAmountY) {
127         emulateDragGesture(instrumentation, dragStartX, dragStartY, dragAmountX, dragAmountY,
128                 2000, 20);
129     }
130 
emulateDragGesture(Instrumentation instrumentation, int dragStartX, int dragStartY, int dragAmountX, int dragAmountY, int dragDurationMs, int moveEventCount)131     private static void emulateDragGesture(Instrumentation instrumentation,
132             int dragStartX, int dragStartY, int dragAmountX, int dragAmountY,
133             int dragDurationMs, int moveEventCount) {
134         // We are using the UiAutomation object to inject events so that drag works
135         // across view / window boundaries (such as for the emulated drag and drop
136         // sequences)
137         final UiAutomation uiAutomation = instrumentation.getUiAutomation();
138         final long downTime = SystemClock.uptimeMillis();
139 
140         injectDownEvent(uiAutomation, downTime, dragStartX, dragStartY);
141 
142         // Inject a sequence of MOVE events that emulate the "move" part of the gesture
143         injectMoveEventsForDrag(uiAutomation, downTime, true, dragStartX, dragStartY,
144                 dragStartX + dragAmountX, dragStartY + dragAmountY, moveEventCount, dragDurationMs);
145 
146         injectUpEvent(uiAutomation, downTime, true, dragStartX + dragAmountX,
147                 dragStartY + dragAmountY);
148 
149         // Wait for the system to process all events in the queue
150         instrumentation.waitForIdleSync();
151     }
152 
153     /**
154      * Emulates a series of linear drag gestures across the screen between multiple points without
155      * lifting the finger. Note that this function does not support curve movements between the
156      * points.
157      *
158      * @param instrumentation the instrumentation used to run the test
159      * @param coordinates the ordered list of points for the drag gesture
160      */
emulateDragGesture(Instrumentation instrumentation, SparseArray<Point> coordinates)161     public static void emulateDragGesture(Instrumentation instrumentation,
162             SparseArray<Point> coordinates) {
163         emulateDragGesture(instrumentation, coordinates, 2000, 20);
164     }
165 
emulateDragGesture(Instrumentation instrumentation, SparseArray<Point> coordinates, int dragDurationMs, int moveEventCount)166     private static void emulateDragGesture(Instrumentation instrumentation,
167             SparseArray<Point> coordinates, int dragDurationMs, int moveEventCount) {
168         final int coordinatesSize = coordinates.size();
169         if (coordinatesSize < 2) {
170             throw new IllegalArgumentException("Need at least 2 points for emulating drag");
171         }
172         // We are using the UiAutomation object to inject events so that drag works
173         // across view / window boundaries (such as for the emulated drag and drop
174         // sequences)
175         final UiAutomation uiAutomation = instrumentation.getUiAutomation();
176         final long downTime = SystemClock.uptimeMillis();
177 
178         injectDownEvent(uiAutomation, downTime, coordinates.get(0).x, coordinates.get(0).y);
179 
180         // Move to each coordinate.
181         for (int i = 0; i < coordinatesSize - 1; i++) {
182             // Inject a sequence of MOVE events that emulate the "move" part of the gesture.
183             injectMoveEventsForDrag(uiAutomation,
184                     downTime,
185                     true,
186                     coordinates.get(i).x,
187                     coordinates.get(i).y,
188                     coordinates.get(i + 1).x,
189                     coordinates.get(i + 1).y,
190                     moveEventCount,
191                     dragDurationMs);
192         }
193 
194         injectUpEvent(uiAutomation,
195                 downTime,
196                 true,
197                 coordinates.get(coordinatesSize - 1).x,
198                 coordinates.get(coordinatesSize - 1).y);
199 
200         // Wait for the system to process all events in the queue
201         instrumentation.waitForIdleSync();
202     }
203 
injectDownEvent(UiAutomation uiAutomation, long downTime, int xOnScreen, int yOnScreen)204     private static long injectDownEvent(UiAutomation uiAutomation, long downTime, int xOnScreen,
205             int yOnScreen) {
206         MotionEvent eventDown = MotionEvent.obtain(
207                 downTime, downTime, MotionEvent.ACTION_DOWN, xOnScreen, yOnScreen, 1);
208         eventDown.setSource(InputDevice.SOURCE_TOUCHSCREEN);
209         uiAutomation.injectInputEvent(eventDown, true);
210         eventDown.recycle();
211         return downTime;
212     }
213 
injectMoveEventForTap(UiAutomation uiAutomation, long downTime, int touchSlop, int xOnScreen, int yOnScreen)214     private static void injectMoveEventForTap(UiAutomation uiAutomation, long downTime,
215             int touchSlop, int xOnScreen, int yOnScreen) {
216         MotionEvent eventMove = MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_MOVE,
217                 xOnScreen + (touchSlop / 2.0f), yOnScreen + (touchSlop / 2.0f), 1);
218         eventMove.setSource(InputDevice.SOURCE_TOUCHSCREEN);
219         uiAutomation.injectInputEvent(eventMove, true);
220         eventMove.recycle();
221     }
222 
injectMoveEventsForDrag(UiAutomation uiAutomation, long downTime, boolean useCurrentEventTime, int dragStartX, int dragStartY, int dragEndX, int dragEndY, int moveEventCount, int dragDurationMs)223     private static void injectMoveEventsForDrag(UiAutomation uiAutomation, long downTime,
224             boolean useCurrentEventTime, int dragStartX, int dragStartY, int dragEndX, int dragEndY,
225             int moveEventCount, int dragDurationMs) {
226         final int dragAmountX = dragEndX - dragStartX;
227         final int dragAmountY = dragEndY - dragStartY;
228         final int sleepTime = dragDurationMs / moveEventCount;
229 
230         // sleep for a bit to emulate the overall drag gesture.
231         long prevEventTime = downTime;
232         SystemClock.sleep(sleepTime);
233         for (int i = 0; i < moveEventCount; i++) {
234             // Note that the first MOVE event is generated "away" from the coordinates
235             // of the start / DOWN event, and the last MOVE event is generated
236             // at the same coordinates as the subsequent UP event.
237             final int moveX = dragStartX + dragAmountX * (i  + 1) / moveEventCount;
238             final int moveY = dragStartY + dragAmountY * (i  + 1) / moveEventCount;
239             long eventTime = useCurrentEventTime ? SystemClock.uptimeMillis() : downTime;
240 
241             // If necessary, generate history for our next MOVE event. The history is generated
242             // to be spaced at 10 millisecond intervals, interpolating the coordinates from the
243             // last generated MOVE event to our current one.
244             int historyEventCount = (int) ((eventTime - prevEventTime) / 10);
245             MotionEvent eventMove = null;
246             if (historyEventCount == 0) {
247                 eventMove = MotionEvent.obtain(
248                         downTime, eventTime, MotionEvent.ACTION_MOVE, moveX, moveY, 1);
249             } else {
250                 final int prevMoveX = dragStartX + dragAmountX * i / moveEventCount;
251                 final int prevMoveY = dragStartY + dragAmountY * i / moveEventCount;
252                 final int deltaMoveX = moveX - prevMoveX;
253                 final int deltaMoveY = moveY - prevMoveY;
254                 final long deltaTime = (eventTime - prevEventTime);
255                 for (int historyIndex = 0; historyIndex < historyEventCount; historyIndex++) {
256                     int stepMoveX = prevMoveX + deltaMoveX * (historyIndex + 1) / historyEventCount;
257                     int stepMoveY = prevMoveY + deltaMoveY * (historyIndex + 1) / historyEventCount;
258                     long stepEventTime = useCurrentEventTime
259                             ? prevEventTime + deltaTime * (historyIndex + 1) / historyEventCount
260                             : downTime;
261                     if (historyIndex == 0) {
262                         // Generate the first event in our sequence
263                         eventMove = MotionEvent.obtain(downTime, stepEventTime,
264                                 MotionEvent.ACTION_MOVE, stepMoveX, stepMoveY, 1);
265                     } else {
266                         // and then add to it
267                         eventMove.addBatch(stepEventTime, stepMoveX, stepMoveY, 1.0f, 1.0f, 1);
268                     }
269                 }
270             }
271 
272             eventMove.setSource(InputDevice.SOURCE_TOUCHSCREEN);
273             uiAutomation.injectInputEvent(eventMove, true);
274             eventMove.recycle();
275             prevEventTime = eventTime;
276 
277             // sleep for a bit to emulate the overall drag gesture.
278             SystemClock.sleep(sleepTime);
279         }
280     }
281 
injectUpEvent(UiAutomation uiAutomation, long downTime, boolean useCurrentEventTime, int xOnScreen, int yOnScreen)282     private static void injectUpEvent(UiAutomation uiAutomation, long downTime,
283             boolean useCurrentEventTime, int xOnScreen, int yOnScreen) {
284         long eventTime = useCurrentEventTime ? SystemClock.uptimeMillis() : downTime;
285         MotionEvent eventUp = MotionEvent.obtain(
286                 downTime, eventTime, MotionEvent.ACTION_UP, xOnScreen, yOnScreen, 1);
287         eventUp.setSource(InputDevice.SOURCE_TOUCHSCREEN);
288         uiAutomation.injectInputEvent(eventUp, true);
289         eventUp.recycle();
290     }
291 
292     /**
293      * Emulates a fling gesture across the horizontal center of the passed view.
294      *
295      * @param instrumentation the instrumentation used to run the test
296      * @param view the view to fling
297      * @param isDownwardsFlingGesture if <code>true</code>, the emulated fling will
298      *      be a downwards gesture
299      * @return The vertical amount of emulated fling in pixels
300      */
emulateFlingGesture(Instrumentation instrumentation, View view, boolean isDownwardsFlingGesture)301     public static int emulateFlingGesture(Instrumentation instrumentation,
302             View view, boolean isDownwardsFlingGesture) {
303         final ViewConfiguration configuration = ViewConfiguration.get(view.getContext());
304         final int flingVelocity = (configuration.getScaledMinimumFlingVelocity() +
305                 configuration.getScaledMaximumFlingVelocity()) / 2;
306         // Get view coordinates on the screen
307         final int[] viewOnScreenXY = new int[2];
308         view.getLocationOnScreen(viewOnScreenXY);
309 
310         // Our fling gesture will be from 25% height of the view to 75% height of the view
311         // for downwards fling gesture, and the other way around for upwards fling gesture
312         final int viewHeight = view.getHeight();
313         final int x = viewOnScreenXY[0] + view.getWidth() / 2;
314         final int startY = isDownwardsFlingGesture ? viewOnScreenXY[1] + viewHeight / 4
315                 : viewOnScreenXY[1] + 3 * viewHeight / 4;
316         final int amountY = isDownwardsFlingGesture ? viewHeight / 2 : -viewHeight / 2;
317 
318         // Compute fling gesture duration based on the distance (50% height of the view) and
319         // fling velocity
320         final int durationMs = (1000 * viewHeight) / (2 * flingVelocity);
321 
322         // And do the same event injection sequence as our generic drag gesture
323         emulateDragGesture(instrumentation, x, startY, 0, amountY, durationMs, durationMs / 16);
324 
325         return amountY;
326     }
327 
328     private static class ViewStateSnapshot {
329         final View mFirst;
330         final View mLast;
331         final int mFirstTop;
332         final int mLastBottom;
333         final int mChildCount;
ViewStateSnapshot(ViewGroup viewGroup)334         private ViewStateSnapshot(ViewGroup viewGroup) {
335             mChildCount = viewGroup.getChildCount();
336             if (mChildCount == 0) {
337                 mFirst = mLast = null;
338                 mFirstTop = mLastBottom = Integer.MIN_VALUE;
339             } else {
340                 mFirst = viewGroup.getChildAt(0);
341                 mLast = viewGroup.getChildAt(mChildCount - 1);
342                 mFirstTop = mFirst.getTop();
343                 mLastBottom = mLast.getBottom();
344             }
345         }
346 
347         @Override
equals(Object o)348         public boolean equals(Object o) {
349             if (this == o) {
350                 return true;
351             }
352             if (o == null || getClass() != o.getClass()) {
353                 return false;
354             }
355 
356             final ViewStateSnapshot that = (ViewStateSnapshot) o;
357             return mFirstTop == that.mFirstTop &&
358                     mLastBottom == that.mLastBottom &&
359                     mFirst == that.mFirst &&
360                     mLast == that.mLast &&
361                     mChildCount == that.mChildCount;
362         }
363 
364         @Override
hashCode()365         public int hashCode() {
366             int result = mFirst != null ? mFirst.hashCode() : 0;
367             result = 31 * result + (mLast != null ? mLast.hashCode() : 0);
368             result = 31 * result + mFirstTop;
369             result = 31 * result + mLastBottom;
370             result = 31 * result + mChildCount;
371             return result;
372         }
373     }
374 
375     /**
376      * Emulates a scroll to the bottom of the specified {@link ViewGroup}.
377      *
378      * @param instrumentation the instrumentation used to run the test
379      * @param viewGroup View group
380      */
emulateScrollToBottom(Instrumentation instrumentation, ViewGroup viewGroup)381     public static void emulateScrollToBottom(Instrumentation instrumentation, ViewGroup viewGroup) {
382         final int[] viewGroupOnScreenXY = new int[2];
383         viewGroup.getLocationOnScreen(viewGroupOnScreenXY);
384 
385         final int emulatedX = viewGroupOnScreenXY[0] + viewGroup.getWidth() / 2;
386         final int emulatedStartY = viewGroupOnScreenXY[1] + 3 * viewGroup.getHeight() / 4;
387         final int swipeAmount = viewGroup.getHeight() / 2;
388 
389         ViewStateSnapshot prev;
390         ViewStateSnapshot next = new ViewStateSnapshot(viewGroup);
391         do {
392             prev = next;
393             emulateDragGesture(instrumentation, emulatedX, emulatedStartY, 0, -swipeAmount,
394                     300, 10);
395             next = new ViewStateSnapshot(viewGroup);
396         } while (!prev.equals(next));
397     }
398 
399     /**
400      * Emulates a long press in the center of the passed {@link View}.
401      *
402      * @param instrumentation the instrumentation used to run the test
403      * @param view the view to "long press"
404      */
emulateLongPressOnViewCenter(Instrumentation instrumentation, View view)405     public static void emulateLongPressOnViewCenter(Instrumentation instrumentation, View view) {
406         emulateLongPressOnViewCenter(instrumentation, view, 0);
407     }
408 
409     /**
410      * Emulates a long press in the center of the passed {@link View}.
411      *
412      * @param instrumentation the instrumentation used to run the test
413      * @param view the view to "long press"
414      * @param extraWaitMs the duration of emulated "long press" in milliseconds starting
415      *      after system-level long press timeout.
416      */
emulateLongPressOnViewCenter(Instrumentation instrumentation, View view, long extraWaitMs)417     public static void emulateLongPressOnViewCenter(Instrumentation instrumentation, View view,
418             long extraWaitMs) {
419         final int touchSlop = ViewConfiguration.get(view.getContext()).getScaledTouchSlop();
420         // Use instrumentation to emulate a tap on the spinner to bring down its popup
421         final int[] viewOnScreenXY = new int[2];
422         view.getLocationOnScreen(viewOnScreenXY);
423         int xOnScreen = viewOnScreenXY[0] + view.getWidth() / 2;
424         int yOnScreen = viewOnScreenXY[1] + view.getHeight() / 2;
425 
426         emulateLongPressOnScreen(
427                 instrumentation, xOnScreen, yOnScreen, touchSlop, extraWaitMs, true);
428     }
429 
430     /**
431      * Emulates a long press confirmed on a point relative to the top-left corner of the passed
432      * {@link View}. Offset parameters are used to compute the final screen coordinates of the
433      * press point.
434      *
435      * @param instrumentation the instrumentation used to run the test
436      * @param view the view to "long press"
437      * @param offsetX extra X offset for the tap
438      * @param offsetY extra Y offset for the tap
439      */
emulateLongPressOnView(Instrumentation instrumentation, View view, int offsetX, int offsetY)440     public static void emulateLongPressOnView(Instrumentation instrumentation, View view,
441             int offsetX, int offsetY) {
442         final int touchSlop = ViewConfiguration.get(view.getContext()).getScaledTouchSlop();
443         final int[] viewOnScreenXY = new int[2];
444         view.getLocationOnScreen(viewOnScreenXY);
445         int xOnScreen = viewOnScreenXY[0] + offsetX;
446         int yOnScreen = viewOnScreenXY[1] + offsetY;
447 
448         emulateLongPressOnScreen(instrumentation, xOnScreen, yOnScreen, touchSlop, 0, true);
449     }
450 
451     /**
452      * Emulates a long press then a linear drag gesture between 2 points across the screen.
453      * This is used for drag selection.
454      *
455      * @param instrumentation the instrumentation used to run the test
456      * @param dragStartX Start X of the emulated drag gesture
457      * @param dragStartY Start Y of the emulated drag gesture
458      * @param dragAmountX X amount of the emulated drag gesture
459      * @param dragAmountY Y amount of the emulated drag gesture
460      */
emulateLongPressAndDragGesture(Instrumentation instrumentation, int dragStartX, int dragStartY, int dragAmountX, int dragAmountY)461     public static void emulateLongPressAndDragGesture(Instrumentation instrumentation,
462             int dragStartX, int dragStartY, int dragAmountX, int dragAmountY) {
463         emulateLongPressOnScreen(instrumentation, dragStartX, dragStartY,
464                 0 /* touchSlop */, 0 /* extraWaitMs */, false /* upGesture */);
465         emulateDragGesture(instrumentation, dragStartX, dragStartY, dragAmountX, dragAmountY);
466     }
467 
468     /**
469      * Emulates a long press on the screen.
470      *
471      * @param instrumentation the instrumentation used to run the test
472      * @param xOnScreen X position on screen for the "long press"
473      * @param yOnScreen Y position on screen for the "long press"
474      * @param extraWaitMs extra duration of emulated long press in milliseconds added
475      *        after the system-level "long press" timeout.
476      * @param upGesture whether to include an up event.
477      */
emulateLongPressOnScreen(Instrumentation instrumentation, int xOnScreen, int yOnScreen, int touchSlop, long extraWaitMs, boolean upGesture)478     private static void emulateLongPressOnScreen(Instrumentation instrumentation,
479             int xOnScreen, int yOnScreen, int touchSlop, long extraWaitMs, boolean upGesture) {
480         final UiAutomation uiAutomation = instrumentation.getUiAutomation();
481         final long downTime = SystemClock.uptimeMillis();
482 
483         injectDownEvent(uiAutomation, downTime, xOnScreen, yOnScreen);
484         injectMoveEventForTap(uiAutomation, downTime, touchSlop, xOnScreen, yOnScreen);
485         SystemClock.sleep((long) (ViewConfiguration.getLongPressTimeout() * 1.5f) + extraWaitMs);
486         if (upGesture) {
487             injectUpEvent(uiAutomation, downTime, false, xOnScreen, yOnScreen);
488         }
489 
490         // Wait for the system to process all events in the queue
491         instrumentation.waitForIdleSync();
492     }
493 }
494