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