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
17package android.test;
18
19import android.app.Activity;
20import android.app.Instrumentation;
21import android.graphics.Point;
22import android.os.SystemClock;
23import android.view.Display;
24import android.view.Gravity;
25import android.view.MotionEvent;
26import android.view.View;
27import android.view.ViewConfiguration;
28import 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
41public 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
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     */
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
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     */
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
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     */
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
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     */
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
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     */
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
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     */
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     */
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     */
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     */
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
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     */
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
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
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     */
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     */
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     */
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
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
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
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     */
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
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     */
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
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     */
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
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     */
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;
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
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
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