1 /*
2  * Copyright (C) 2007 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.test;
18 
19 import static android.view.WindowInsets.Type.displayCutout;
20 import static android.view.WindowInsets.Type.navigationBars;
21 
22 import android.app.Activity;
23 import android.app.Instrumentation;
24 import android.graphics.Insets;
25 import android.graphics.Rect;
26 import android.os.SystemClock;
27 import android.util.Size;
28 import android.view.Gravity;
29 import android.view.MotionEvent;
30 import android.view.View;
31 import android.view.ViewConfiguration;
32 import android.view.ViewGroup;
33 import android.view.WindowInsets;
34 import android.view.WindowManager;
35 import android.view.WindowMetrics;
36 
37 /**
38  * Reusable methods for generating touch events. These methods can be used with
39  * InstrumentationTestCase or ActivityInstrumentationTestCase2 to simulate user interaction with
40  * the application through a touch screen.
41  *
42  * @deprecated Use
43  * <a href="{@docRoot}training/testing/ui-testing/espresso-testing.html">Espresso UI testing
44  * framework</a> instead. New tests should be written using the
45  * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
46  */
47 @Deprecated
48 public class TouchUtils {
49 
50     /**
51      * Simulate touching in the center of the screen and dragging one quarter of the way down
52      * @param test The test case that is being run
53      *
54      * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
55      * {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
56      * configuring the Activity under test
57      */
58     @Deprecated
dragQuarterScreenDown(ActivityInstrumentationTestCase test)59     public static void dragQuarterScreenDown(ActivityInstrumentationTestCase test) {
60         dragQuarterScreenDown(test, test.getActivity());
61     }
62 
63     /**
64      * Simulate touching in the center of the screen and dragging one quarter of the way down
65      * @param test The test case that is being run
66      * @param activity The activity that is in the foreground of the test case
67      */
dragQuarterScreenDown(InstrumentationTestCase test, Activity activity)68     public static void dragQuarterScreenDown(InstrumentationTestCase test, Activity activity) {
69         WindowManager wm = activity.getWindowManager();
70         final Size size = getSizeExcludingNavigationBarAndCutout(wm.getCurrentWindowMetrics());
71 
72         final float x = size.getWidth() / 2.0f;
73         final float fromY = size.getHeight() * 0.5f;
74         final float toY = size.getHeight() * 0.75f;
75 
76         drag(test, x, x, fromY, toY, 4);
77     }
78 
79     /**
80      * Simulate touching in the center of the screen and dragging one quarter of the way up
81      * @param test The test case that is being run
82      *
83      * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
84      * {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
85      * configuring the Activity under test
86      */
87     @Deprecated
dragQuarterScreenUp(ActivityInstrumentationTestCase test)88     public static void dragQuarterScreenUp(ActivityInstrumentationTestCase test) {
89         dragQuarterScreenUp(test, test.getActivity());
90     }
91 
92     /**
93      * Simulate touching in the center of the screen and dragging one quarter of the way up
94      * @param test The test case that is being run
95      * @param activity The activity that is in the foreground of the test case
96      */
dragQuarterScreenUp(InstrumentationTestCase test, Activity activity)97     public static void dragQuarterScreenUp(InstrumentationTestCase test, Activity activity) {
98         WindowManager wm = activity.getWindowManager();
99         final Size size = getSizeExcludingNavigationBarAndCutout(wm.getCurrentWindowMetrics());
100 
101         final float x = size.getWidth() / 2.0f;
102         final float fromY = size.getHeight() * 0.5f;
103         final float toY = size.getHeight() * 0.25f;
104 
105         drag(test, x, x, fromY, toY, 4);
106     }
107 
getSizeExcludingNavigationBarAndCutout(WindowMetrics windowMetrics)108     private static Size getSizeExcludingNavigationBarAndCutout(WindowMetrics windowMetrics) {
109         WindowInsets windowInsets = windowMetrics.getWindowInsets();
110         final Insets insetsWithCutout = windowInsets
111                 .getInsetsIgnoringVisibility(navigationBars() | displayCutout());
112         final int insetsWidth = insetsWithCutout.left + insetsWithCutout.right;
113         final int insetsHeight = insetsWithCutout.top + insetsWithCutout.bottom;
114 
115         Rect bounds = windowMetrics.getBounds();
116         return new Size(bounds.width() - insetsWidth, bounds.height() - insetsHeight);
117     }
118 
119     /**
120      * Scroll a ViewGroup to the bottom by repeatedly calling
121      * {@link #dragQuarterScreenUp(InstrumentationTestCase, Activity)}
122      *
123      * @param test The test case that is being run
124      * @param v The ViewGroup that should be dragged
125      *
126      * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
127      * {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
128      * configuring the Activity under test
129      */
130     @Deprecated
scrollToBottom(ActivityInstrumentationTestCase test, ViewGroup v)131     public static void scrollToBottom(ActivityInstrumentationTestCase test, ViewGroup v) {
132         scrollToBottom(test, test.getActivity(), v);
133     }
134 
135     /**
136      * Scroll a ViewGroup to the bottom by repeatedly calling
137      * {@link #dragQuarterScreenUp(InstrumentationTestCase, Activity)}
138      *
139      * @param test The test case that is being run
140      * @param activity The activity that is in the foreground of the test case
141      * @param v The ViewGroup that should be dragged
142      */
scrollToBottom(InstrumentationTestCase test, Activity activity, ViewGroup v)143     public static void scrollToBottom(InstrumentationTestCase test, Activity activity,
144             ViewGroup v) {
145         ViewStateSnapshot prev;
146         ViewStateSnapshot next = new ViewStateSnapshot(v);
147         do {
148             prev = next;
149             TouchUtils.dragQuarterScreenUp(test, activity);
150             next = new ViewStateSnapshot(v);
151         } while (!prev.equals(next));
152     }
153 
154     /**
155      * Scroll a ViewGroup to the top by repeatedly calling
156      * {@link #dragQuarterScreenDown(InstrumentationTestCase, Activity)}
157      *
158      * @param test The test case that is being run
159      * @param v The ViewGroup that should be dragged
160      *
161      * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
162      * {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
163      * configuring the Activity under test
164      */
165     @Deprecated
scrollToTop(ActivityInstrumentationTestCase test, ViewGroup v)166     public static void scrollToTop(ActivityInstrumentationTestCase test, ViewGroup v) {
167         scrollToTop(test, test.getActivity(), v);
168     }
169 
170     /**
171      * Scroll a ViewGroup to the top by repeatedly calling
172      * {@link #dragQuarterScreenDown(InstrumentationTestCase, Activity)}
173      *
174      * @param test The test case that is being run
175      * @param activity The activity that is in the foreground of the test case
176      * @param v The ViewGroup that should be dragged
177      */
scrollToTop(InstrumentationTestCase test, Activity activity, ViewGroup v)178     public static void scrollToTop(InstrumentationTestCase test, Activity activity, ViewGroup v) {
179         ViewStateSnapshot prev;
180         ViewStateSnapshot next = new ViewStateSnapshot(v);
181         do {
182             prev = next;
183             TouchUtils.dragQuarterScreenDown(test, activity);
184             next = new ViewStateSnapshot(v);
185         } while (!prev.equals(next));
186     }
187 
188     /**
189      * Simulate touching the center of a view and dragging to the bottom of the screen.
190      *
191      * @param test The test case that is being run
192      * @param v The view that should be dragged
193      *
194      * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
195      * {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
196      * configuring the Activity under test
197      */
198     @Deprecated
dragViewToBottom(ActivityInstrumentationTestCase test, View v)199     public static void dragViewToBottom(ActivityInstrumentationTestCase test, View v) {
200         dragViewToBottom(test, test.getActivity(), v, 4);
201     }
202 
203     /**
204      * Simulate touching the center of a view and dragging to the bottom of the screen.
205      *
206      * @param test The test case that is being run
207      * @param activity The activity that is in the foreground of the test case
208      * @param v The view that should be dragged
209      */
dragViewToBottom(InstrumentationTestCase test, Activity activity, View v)210     public static void dragViewToBottom(InstrumentationTestCase test, Activity activity, View v) {
211         dragViewToBottom(test, activity, v, 4);
212     }
213 
214     /**
215      * Simulate touching the center of a view and dragging to the bottom of the screen.
216      *
217      * @param test The test case that is being run
218      * @param v The view that should be dragged
219      * @param stepCount How many move steps to include in the drag
220      *
221      * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
222      * {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
223      * configuring the Activity under test
224      */
225     @Deprecated
dragViewToBottom(ActivityInstrumentationTestCase test, View v, int stepCount)226     public static void dragViewToBottom(ActivityInstrumentationTestCase test, View v,
227             int stepCount) {
228         dragViewToBottom(test, test.getActivity(), v, stepCount);
229     }
230 
231     /**
232      * Simulate touching the center of a view and dragging to the bottom of the screen.
233      *
234      * @param test The test case that is being run
235      * @param activity The activity that is in the foreground of the test case
236      * @param v The view that should be dragged
237      * @param stepCount How many move steps to include in the drag
238      */
dragViewToBottom(InstrumentationTestCase test, Activity activity, View v, int stepCount)239     public static void dragViewToBottom(InstrumentationTestCase test, Activity activity, View v,
240             int stepCount) {
241         WindowManager wm = activity.getWindowManager();
242         final int screenHeight = getSizeExcludingNavigationBarAndCutout(
243                 wm.getCurrentWindowMetrics()).getHeight();
244 
245         int[] xy = new int[2];
246         v.getLocationOnScreen(xy);
247 
248         final int viewWidth = v.getWidth();
249         final int viewHeight = v.getHeight();
250 
251         final float x = xy[0] + (viewWidth / 2.0f);
252         float fromY = xy[1] + (viewHeight / 2.0f);
253         float toY = screenHeight - 1;
254 
255         drag(test, x, x, fromY, toY, stepCount);
256     }
257 
258     /**
259      * Simulate touching the center of a view and releasing quickly (before the tap timeout).
260      *
261      * @param test The test case that is being run
262      * @param v The view that should be clicked
263      */
tapView(InstrumentationTestCase test, View v)264     public static void tapView(InstrumentationTestCase test, View v) {
265         int[] xy = new int[2];
266         v.getLocationOnScreen(xy);
267 
268         final int viewWidth = v.getWidth();
269         final int viewHeight = v.getHeight();
270 
271         final float x = xy[0] + (viewWidth / 2.0f);
272         float y = xy[1] + (viewHeight / 2.0f);
273 
274         Instrumentation inst = test.getInstrumentation();
275 
276         long downTime = SystemClock.uptimeMillis();
277         long eventTime = SystemClock.uptimeMillis();
278 
279         MotionEvent event = MotionEvent.obtain(downTime, eventTime,
280                 MotionEvent.ACTION_DOWN, x, y, 0);
281         inst.sendPointerSync(event);
282         inst.waitForIdleSync();
283 
284         eventTime = SystemClock.uptimeMillis();
285         final int touchSlop = ViewConfiguration.get(v.getContext()).getScaledTouchSlop();
286         event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE,
287                 x + (touchSlop / 2.0f), y + (touchSlop / 2.0f), 0);
288         inst.sendPointerSync(event);
289         inst.waitForIdleSync();
290 
291         eventTime = SystemClock.uptimeMillis();
292         event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, x, y, 0);
293         inst.sendPointerSync(event);
294         inst.waitForIdleSync();
295     }
296 
297     /**
298      * Simulate touching the center of a view and cancelling (so no onClick should
299      * fire, etc).
300      *
301      * @param test The test case that is being run
302      * @param v The view that should be clicked
303      */
touchAndCancelView(InstrumentationTestCase test, View v)304     public static void touchAndCancelView(InstrumentationTestCase test, View v) {
305         int[] xy = new int[2];
306         v.getLocationOnScreen(xy);
307 
308         final int viewWidth = v.getWidth();
309         final int viewHeight = v.getHeight();
310 
311         final float x = xy[0] + (viewWidth / 2.0f);
312         float y = xy[1] + (viewHeight / 2.0f);
313 
314         Instrumentation inst = test.getInstrumentation();
315 
316         long downTime = SystemClock.uptimeMillis();
317         long eventTime = SystemClock.uptimeMillis();
318 
319         MotionEvent event = MotionEvent.obtain(downTime, eventTime,
320                 MotionEvent.ACTION_DOWN, x, y, 0);
321         inst.sendPointerSync(event);
322         inst.waitForIdleSync();
323 
324         eventTime = SystemClock.uptimeMillis();
325         final int touchSlop = ViewConfiguration.get(v.getContext()).getScaledTouchSlop();
326         event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_CANCEL,
327                 x + (touchSlop / 2.0f), y + (touchSlop / 2.0f), 0);
328         inst.sendPointerSync(event);
329         inst.waitForIdleSync();
330 
331     }
332 
333     /**
334      * Simulate touching the center of a view and releasing.
335      *
336      * @param test The test case that is being run
337      * @param v The view that should be clicked
338      */
clickView(InstrumentationTestCase test, View v)339     public static void clickView(InstrumentationTestCase test, View v) {
340         int[] xy = new int[2];
341         v.getLocationOnScreen(xy);
342 
343         final int viewWidth = v.getWidth();
344         final int viewHeight = v.getHeight();
345 
346         final float x = xy[0] + (viewWidth / 2.0f);
347         float y = xy[1] + (viewHeight / 2.0f);
348 
349         Instrumentation inst = test.getInstrumentation();
350 
351         long downTime = SystemClock.uptimeMillis();
352         long eventTime = SystemClock.uptimeMillis();
353 
354         MotionEvent event = MotionEvent.obtain(downTime, eventTime,
355                 MotionEvent.ACTION_DOWN, x, y, 0);
356         inst.sendPointerSync(event);
357         inst.waitForIdleSync();
358 
359 
360         eventTime = SystemClock.uptimeMillis();
361         final int touchSlop = ViewConfiguration.get(v.getContext()).getScaledTouchSlop();
362         event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE,
363                 x + (touchSlop / 2.0f), y + (touchSlop / 2.0f), 0);
364         inst.sendPointerSync(event);
365         inst.waitForIdleSync();
366 
367         eventTime = SystemClock.uptimeMillis();
368         event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, x, y, 0);
369         inst.sendPointerSync(event);
370         inst.waitForIdleSync();
371 
372         try {
373             Thread.sleep(1000);
374         } catch (InterruptedException e) {
375             e.printStackTrace();
376         }
377     }
378 
379     /**
380      * Simulate touching the center of a view, holding until it is a long press, and then releasing.
381      *
382      * @param test The test case that is being run
383      * @param v The view that should be clicked
384      *
385      * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
386      * {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
387      * configuring the Activity under test
388      */
389     @Deprecated
longClickView(ActivityInstrumentationTestCase test, View v)390     public static void longClickView(ActivityInstrumentationTestCase test, View v) {
391         longClickView((InstrumentationTestCase) test, v);
392     }
393 
394     /**
395      * Simulate touching the center of a view, holding until it is a long press, and then releasing.
396      *
397      * @param test The test case that is being run
398      * @param v The view that should be clicked
399      */
longClickView(InstrumentationTestCase test, View v)400     public static void longClickView(InstrumentationTestCase test, View v) {
401         int[] xy = new int[2];
402         v.getLocationOnScreen(xy);
403 
404         final int viewWidth = v.getWidth();
405         final int viewHeight = v.getHeight();
406 
407         final float x = xy[0] + (viewWidth / 2.0f);
408         float y = xy[1] + (viewHeight / 2.0f);
409 
410         Instrumentation inst = test.getInstrumentation();
411 
412         long downTime = SystemClock.uptimeMillis();
413         long eventTime = SystemClock.uptimeMillis();
414 
415         MotionEvent event = MotionEvent.obtain(downTime, eventTime,
416                 MotionEvent.ACTION_DOWN, x, y, 0);
417         inst.sendPointerSync(event);
418         inst.waitForIdleSync();
419 
420         eventTime = SystemClock.uptimeMillis();
421         final int touchSlop = ViewConfiguration.get(v.getContext()).getScaledTouchSlop();
422         event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE,
423                 x + touchSlop / 2, y + touchSlop / 2, 0);
424         inst.sendPointerSync(event);
425         inst.waitForIdleSync();
426 
427         try {
428             Thread.sleep((long)(ViewConfiguration.getLongPressTimeout() * 1.5f));
429         } catch (InterruptedException e) {
430             e.printStackTrace();
431         }
432 
433         eventTime = SystemClock.uptimeMillis();
434         event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, x, y, 0);
435         inst.sendPointerSync(event);
436         inst.waitForIdleSync();
437     }
438 
439     /**
440      * Simulate touching the center of a view and dragging to the top of the screen.
441      *
442      * @param test The test case that is being run
443      * @param v The view that should be dragged
444      *
445      * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
446      * {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
447      * configuring the Activity under test
448      */
449     @Deprecated
dragViewToTop(ActivityInstrumentationTestCase test, View v)450     public static void dragViewToTop(ActivityInstrumentationTestCase test, View v) {
451         dragViewToTop((InstrumentationTestCase) test, v, 4);
452     }
453 
454     /**
455      * Simulate touching the center of a view and dragging to the top of the screen.
456      *
457      * @param test The test case that is being run
458      * @param v The view that should be dragged
459      * @param stepCount How many move steps to include in the drag
460      *
461      * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
462      * {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
463      * configuring the Activity under test
464      */
465     @Deprecated
dragViewToTop(ActivityInstrumentationTestCase test, View v, int stepCount)466     public static void dragViewToTop(ActivityInstrumentationTestCase test, View v, int stepCount) {
467         dragViewToTop((InstrumentationTestCase) test, v, stepCount);
468     }
469 
470     /**
471      * Simulate touching the center of a view and dragging to the top of the screen.
472      *
473      * @param test The test case that is being run
474      * @param v The view that should be dragged
475      */
dragViewToTop(InstrumentationTestCase test, View v)476     public static void dragViewToTop(InstrumentationTestCase test, View v) {
477         dragViewToTop(test, v, 4);
478     }
479 
480     /**
481      * Simulate touching the center of a view and dragging to the top of the screen.
482      *
483      * @param test The test case that is being run
484      * @param v The view that should be dragged
485      * @param stepCount How many move steps to include in the drag
486      */
dragViewToTop(InstrumentationTestCase test, View v, int stepCount)487     public static void dragViewToTop(InstrumentationTestCase test, View v, int stepCount) {
488         int[] xy = new int[2];
489         v.getLocationOnScreen(xy);
490 
491         final int viewWidth = v.getWidth();
492         final int viewHeight = v.getHeight();
493 
494         final float x = xy[0] + (viewWidth / 2.0f);
495         float fromY = xy[1] + (viewHeight / 2.0f);
496         float toY = 0;
497 
498         drag(test, x, x, fromY, toY, stepCount);
499     }
500 
501     /**
502      * Get the location of a view. Use the gravity param to specify which part of the view to
503      * return.
504      *
505      * @param v View to find
506      * @param gravity A combination of (TOP, CENTER_VERTICAL, BOTTOM) and (LEFT, CENTER_HORIZONTAL,
507      *        RIGHT)
508      * @param xy Result
509      */
getStartLocation(View v, int gravity, int[] xy)510     private static void getStartLocation(View v, int gravity, int[] xy) {
511         v.getLocationOnScreen(xy);
512 
513         final int viewWidth = v.getWidth();
514         final int viewHeight = v.getHeight();
515 
516         switch (gravity & Gravity.VERTICAL_GRAVITY_MASK) {
517         case Gravity.TOP:
518             break;
519         case Gravity.CENTER_VERTICAL:
520             xy[1] += viewHeight / 2;
521             break;
522         case Gravity.BOTTOM:
523             xy[1] += viewHeight - 1;
524             break;
525         default:
526             // Same as top -- do nothing
527         }
528 
529         switch (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
530         case Gravity.LEFT:
531             break;
532         case Gravity.CENTER_HORIZONTAL:
533             xy[0] += viewWidth / 2;
534             break;
535         case Gravity.RIGHT:
536             xy[0] += viewWidth - 1;
537             break;
538         default:
539             // Same as left -- do nothing
540         }
541     }
542 
543     /**
544      * Simulate touching a view and dragging it by the specified amount.
545      *
546      * @param test The test case that is being run
547      * @param v The view that should be dragged
548      * @param gravity Which part of the view to use for the initial down event. A combination of
549      *        (TOP, CENTER_VERTICAL, BOTTOM) and (LEFT, CENTER_HORIZONTAL, RIGHT)
550      * @param deltaX Amount to drag horizontally in pixels
551      * @param deltaY Amount to drag vertically in pixels
552      *
553      * @return distance in pixels covered by the drag
554      *
555      * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
556      * {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
557      * configuring the Activity under test
558      */
559     @Deprecated
dragViewBy(ActivityInstrumentationTestCase test, View v, int gravity, int deltaX, int deltaY)560     public static int dragViewBy(ActivityInstrumentationTestCase test, View v, int gravity,
561             int deltaX, int deltaY) {
562         return dragViewBy((InstrumentationTestCase) test, v, gravity, deltaX, deltaY);
563     }
564 
565     /**
566      * Simulate touching a view and dragging it by the specified amount.
567      *
568      * @param test The test case that is being run
569      * @param v The view that should be dragged
570      * @param gravity Which part of the view to use for the initial down event. A combination of
571      *        (TOP, CENTER_VERTICAL, BOTTOM) and (LEFT, CENTER_HORIZONTAL, RIGHT)
572      * @param deltaX Amount to drag horizontally in pixels
573      * @param deltaY Amount to drag vertically in pixels
574      *
575      * @return distance in pixels covered by the drag
576      *
577      * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
578      * {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
579      * configuring the Activity under test
580      */
581     @Deprecated
dragViewBy(InstrumentationTestCase test, View v, int gravity, int deltaX, int deltaY)582     public static int dragViewBy(InstrumentationTestCase test, View v, int gravity, int deltaX,
583             int deltaY) {
584         int[] xy = new int[2];
585 
586         getStartLocation(v, gravity, xy);
587 
588         final int fromX = xy[0];
589         final int fromY = xy[1];
590 
591         int distance = (int) Math.hypot(deltaX, deltaY);
592 
593         drag(test, fromX, fromX + deltaX, fromY, fromY + deltaY, distance);
594 
595         return distance;
596     }
597 
598     /**
599      * Simulate touching a view and dragging it to a specified location.
600      *
601      * @param test The test case that is being run
602      * @param v The view that should be dragged
603      * @param gravity Which part of the view to use for the initial down event. A combination of
604      *        (TOP, CENTER_VERTICAL, BOTTOM) and (LEFT, CENTER_HORIZONTAL, RIGHT)
605      * @param toX Final location of the view after dragging
606      * @param toY Final location of the view after dragging
607      *
608      * @return distance in pixels covered by the drag
609      *
610      * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
611      * {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
612      * configuring the Activity under test
613      */
614     @Deprecated
dragViewTo(ActivityInstrumentationTestCase test, View v, int gravity, int toX, int toY)615     public static int dragViewTo(ActivityInstrumentationTestCase test, View v, int gravity, int toX,
616             int toY) {
617         return dragViewTo((InstrumentationTestCase) test, v, gravity, toX, toY);
618     }
619 
620     /**
621      * Simulate touching a view and dragging it to a specified location.
622      *
623      * @param test The test case that is being run
624      * @param v The view that should be dragged
625      * @param gravity Which part of the view to use for the initial down event. A combination of
626      *        (TOP, CENTER_VERTICAL, BOTTOM) and (LEFT, CENTER_HORIZONTAL, RIGHT)
627      * @param toX Final location of the view after dragging
628      * @param toY Final location of the view after dragging
629      *
630      * @return distance in pixels covered by the drag
631      */
dragViewTo(InstrumentationTestCase test, View v, int gravity, int toX, int toY)632     public static int dragViewTo(InstrumentationTestCase test, View v, int gravity, int toX,
633             int toY) {
634         int[] xy = new int[2];
635 
636         getStartLocation(v, gravity, xy);
637 
638         final int fromX = xy[0];
639         final int fromY = xy[1];
640 
641         int deltaX = fromX - toX;
642         int deltaY = fromY - toY;
643 
644         int distance = (int)Math.hypot(deltaX, deltaY);
645         drag(test, fromX, toX, fromY, toY, distance);
646 
647         return distance;
648     }
649 
650     /**
651      * Simulate touching a view and dragging it to a specified location. Only moves horizontally.
652      *
653      * @param test The test case that is being run
654      * @param v The view that should be dragged
655      * @param gravity Which part of the view to use for the initial down event. A combination of
656      *        (TOP, CENTER_VERTICAL, BOTTOM) and (LEFT, CENTER_HORIZONTAL, RIGHT)
657      * @param toX Final location of the view after dragging
658      *
659      * @return distance in pixels covered by the drag
660      *
661      * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
662      * {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
663      * configuring the Activity under test
664      */
665     @Deprecated
dragViewToX(ActivityInstrumentationTestCase test, View v, int gravity, int toX)666     public static int dragViewToX(ActivityInstrumentationTestCase test, View v, int gravity,
667             int toX) {
668         return dragViewToX((InstrumentationTestCase) test, v, gravity, toX);
669     }
670 
671     /**
672      * Simulate touching a view and dragging it to a specified location. Only moves horizontally.
673      *
674      * @param test The test case that is being run
675      * @param v The view that should be dragged
676      * @param gravity Which part of the view to use for the initial down event. A combination of
677      *        (TOP, CENTER_VERTICAL, BOTTOM) and (LEFT, CENTER_HORIZONTAL, RIGHT)
678      * @param toX Final location of the view after dragging
679      *
680      * @return distance in pixels covered by the drag
681      */
dragViewToX(InstrumentationTestCase test, View v, int gravity, int toX)682     public static int dragViewToX(InstrumentationTestCase test, View v, int gravity, int toX) {
683         int[] xy = new int[2];
684 
685         getStartLocation(v, gravity, xy);
686 
687         final int fromX = xy[0];
688         final int fromY = xy[1];
689 
690         int deltaX = fromX - toX;
691 
692         drag(test, fromX, toX, fromY, fromY, deltaX);
693 
694         return deltaX;
695     }
696 
697     /**
698      * Simulate touching a view and dragging it to a specified location. Only moves vertically.
699      *
700      * @param test The test case that is being run
701      * @param v The view that should be dragged
702      * @param gravity Which part of the view to use for the initial down event. A combination of
703      *        (TOP, CENTER_VERTICAL, BOTTOM) and (LEFT, CENTER_HORIZONTAL, RIGHT)
704      * @param toY Final location of the view after dragging
705      *
706      * @return distance in pixels covered by the drag
707      *
708      * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
709      * {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
710      * configuring the Activity under test
711      */
712     @Deprecated
dragViewToY(ActivityInstrumentationTestCase test, View v, int gravity, int toY)713     public static int dragViewToY(ActivityInstrumentationTestCase test, View v, int gravity,
714             int toY) {
715         return dragViewToY((InstrumentationTestCase) test, v, gravity, toY);
716     }
717 
718     /**
719      * Simulate touching a view and dragging it to a specified location. Only moves vertically.
720      *
721      * @param test The test case that is being run
722      * @param v The view that should be dragged
723      * @param gravity Which part of the view to use for the initial down event. A combination of
724      *        (TOP, CENTER_VERTICAL, BOTTOM) and (LEFT, CENTER_HORIZONTAL, RIGHT)
725      * @param toY Final location of the view after dragging
726      *
727      * @return distance in pixels covered by the drag
728      */
dragViewToY(InstrumentationTestCase test, View v, int gravity, int toY)729     public static int dragViewToY(InstrumentationTestCase test, View v, int gravity, int toY) {
730         int[] xy = new int[2];
731 
732         getStartLocation(v, gravity, xy);
733 
734         final int fromX = xy[0];
735         final int fromY = xy[1];
736 
737         int deltaY = fromY - toY;
738 
739         drag(test, fromX, fromX, fromY, toY, deltaY);
740 
741         return deltaY;
742     }
743 
744 
745     /**
746      * Simulate touching a specific location and dragging to a new location.
747      *
748      * @param test The test case that is being run
749      * @param fromX X coordinate of the initial touch, in screen coordinates
750      * @param toX Xcoordinate of the drag destination, in screen coordinates
751      * @param fromY X coordinate of the initial touch, in screen coordinates
752      * @param toY Y coordinate of the drag destination, in screen coordinates
753      * @param stepCount How many move steps to include in the drag
754      *
755      * @deprecated {@link android.test.ActivityInstrumentationTestCase} is deprecated in favor of
756      * {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
757      * configuring the Activity under test
758      */
759     @Deprecated
drag(ActivityInstrumentationTestCase test, float fromX, float toX, float fromY, float toY, int stepCount)760     public static void drag(ActivityInstrumentationTestCase test, float fromX, float toX,
761             float fromY, float toY, int stepCount) {
762         drag((InstrumentationTestCase) test, fromX, toX, fromY, toY, stepCount);
763     }
764 
765     /**
766      * Simulate touching a specific location and dragging to a new location.
767      *
768      * @param test The test case that is being run
769      * @param fromX X coordinate of the initial touch, in screen coordinates
770      * @param toX Xcoordinate of the drag destination, in screen coordinates
771      * @param fromY X coordinate of the initial touch, in screen coordinates
772      * @param toY Y coordinate of the drag destination, in screen coordinates
773      * @param stepCount How many move steps to include in the drag
774      */
drag(InstrumentationTestCase test, float fromX, float toX, float fromY, float toY, int stepCount)775     public static void drag(InstrumentationTestCase test, float fromX, float toX, float fromY,
776             float toY, int stepCount) {
777         Instrumentation inst = test.getInstrumentation();
778 
779         long downTime = SystemClock.uptimeMillis();
780         long eventTime = SystemClock.uptimeMillis();
781 
782         float y = fromY;
783         float x = fromX;
784 
785         float yStep = (toY - fromY) / stepCount;
786         float xStep = (toX - fromX) / stepCount;
787 
788         MotionEvent event = MotionEvent.obtain(downTime, eventTime,
789                 MotionEvent.ACTION_DOWN, x, y, 0);
790         inst.sendPointerSync(event);
791         for (int i = 0; i < stepCount; ++i) {
792             y += yStep;
793             x += xStep;
794             eventTime = SystemClock.uptimeMillis();
795             event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE, x, y, 0);
796             inst.sendPointerSync(event);
797         }
798 
799         eventTime = SystemClock.uptimeMillis();
800         event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, x, y, 0);
801         inst.sendPointerSync(event);
802         inst.waitForIdleSync();
803     }
804 
805     private static class ViewStateSnapshot {
806         final View mFirst;
807         final View mLast;
808         final int mFirstTop;
809         final int mLastBottom;
810         final int mChildCount;
ViewStateSnapshot(ViewGroup viewGroup)811         private ViewStateSnapshot(ViewGroup viewGroup) {
812             mChildCount = viewGroup.getChildCount();
813             if (mChildCount == 0) {
814                 mFirst = mLast = null;
815                 mFirstTop = mLastBottom = Integer.MIN_VALUE;
816             } else {
817                 mFirst = viewGroup.getChildAt(0);
818                 mLast = viewGroup.getChildAt(mChildCount - 1);
819                 mFirstTop = mFirst.getTop();
820                 mLastBottom = mLast.getBottom();
821             }
822         }
823 
824         @Override
equals(Object o)825         public boolean equals(Object o) {
826             if (this == o) {
827                 return true;
828             }
829             if (o == null || getClass() != o.getClass()) {
830                 return false;
831             }
832 
833             final ViewStateSnapshot that = (ViewStateSnapshot) o;
834             return mFirstTop == that.mFirstTop &&
835                     mLastBottom == that.mLastBottom &&
836                     mFirst == that.mFirst &&
837                     mLast == that.mLast &&
838                     mChildCount == that.mChildCount;
839         }
840 
841         @Override
hashCode()842         public int hashCode() {
843             int result = mFirst != null ? mFirst.hashCode() : 0;
844             result = 31 * result + (mLast != null ? mLast.hashCode() : 0);
845             result = 31 * result + mFirstTop;
846             result = 31 * result + mLastBottom;
847             result = 31 * result + mChildCount;
848             return result;
849         }
850     }
851 }
852