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