TouchCommon.java revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5package org.chromium.content.browser.test.util;
6
7import android.os.SystemClock;
8import android.view.MotionEvent;
9import android.view.View;
10import android.view.ViewConfiguration;
11import android.test.ActivityInstrumentationTestCase2;
12
13/**
14 * Touch-related functionality reused across test cases.
15 */
16public class TouchCommon {
17    private ActivityInstrumentationTestCase2 mActivityTestCase;
18
19    // TODO(leandrogracia): This method should receive and use an activity
20    // instead of the ActivityInstrumentationTestCase2. However this is causing
21    // problems downstream. Any fix for this should be landed downstream first.
22    public TouchCommon(ActivityInstrumentationTestCase2 activityTestCase) {
23        mActivityTestCase = activityTestCase;
24    }
25
26    /**
27     * Starts (synchronously) a drag motion. Normally followed by dragTo() and dragEnd().
28     *
29     * @param x
30     * @param y
31     * @param downTime (in ms)
32     * @see TouchUtils
33     */
34    public void dragStart(float x, float y, long downTime) {
35        MotionEvent event = MotionEvent.obtain(downTime, downTime,
36                MotionEvent.ACTION_DOWN, x, y, 0);
37        dispatchTouchEvent(event);
38    }
39
40    /**
41     * Drags / moves (synchronously) to the specified coordinates. Normally preceeded by
42     * dragStart() and followed by dragEnd()
43     *
44     * @param fromX
45     * @param toX
46     * @param fromY
47     * @param toY
48     * @param stepCount
49     * @param downTime (in ms)
50     * @see TouchUtils
51     */
52    public void dragTo(float fromX, float toX, float fromY,
53            float toY, int stepCount, long downTime) {
54        float x = fromX;
55        float y = fromY;
56        float yStep = (toY - fromY) / stepCount;
57        float xStep = (toX - fromX) / stepCount;
58        for (int i = 0; i < stepCount; ++i) {
59            y += yStep;
60            x += xStep;
61            long eventTime = SystemClock.uptimeMillis();
62            MotionEvent event = MotionEvent.obtain(downTime, eventTime,
63                    MotionEvent.ACTION_MOVE, x, y, 0);
64            dispatchTouchEvent(event);
65        }
66    }
67
68    /**
69     * Finishes (synchronously) a drag / move at the specified coordinate.
70     * Normally preceeded by dragStart() and dragTo().
71     *
72     * @param x
73     * @param y
74     * @param downTime (in ms)
75     * @see TouchUtils
76     */
77    public void dragEnd(float x, float y, long downTime) {
78        long eventTime = SystemClock.uptimeMillis();
79        MotionEvent event = MotionEvent.obtain(downTime, eventTime,
80                MotionEvent.ACTION_UP, x, y, 0);
81        dispatchTouchEvent(event);
82    }
83
84    /**
85     * Sends (synchronously) a single click to an absolute screen coordinates.
86     *
87     * @param x screen absolute
88     * @param y screen absolute
89     * @see TouchUtils
90     */
91    public void singleClick(float x, float y) {
92
93        long downTime = SystemClock.uptimeMillis();
94        long eventTime = SystemClock.uptimeMillis();
95
96        MotionEvent event = MotionEvent.obtain(downTime, eventTime,
97                                               MotionEvent.ACTION_DOWN, x, y, 0);
98        dispatchTouchEvent(event);
99
100        eventTime = SystemClock.uptimeMillis();
101        event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP,
102                                   x, y, 0);
103        dispatchTouchEvent(event);
104    }
105
106    /**
107     * Sends (synchronously) a single click to the View at the specified coordinates.
108     *
109     * @param v The view to be clicked.
110     * @param x Relative x location to v
111     * @param y Relative y location to v
112     */
113    public void singleClickView(View v, int x, int y) {
114        int location[] = getAbsoluteLocationFromRelative(v, x, y);
115        int absoluteX = location[0];
116        int absoluteY = location[1];
117        singleClick(absoluteX, absoluteY);
118    }
119
120    /**
121     * Sends (synchronously) a single click on the specified relative coordinates inside
122     * a given view.
123     *
124     * @param view The view to be clicked.
125     * @param x screen absolute
126     * @param y screen absolute
127     * @see TouchUtils
128     */
129    public void singleClickViewRelative(View view, int x, int y) {
130        long downTime = SystemClock.uptimeMillis();
131        long eventTime = SystemClock.uptimeMillis();
132
133        MotionEvent event = MotionEvent.obtain(downTime, eventTime,
134                                               MotionEvent.ACTION_DOWN, x, y, 0);
135        dispatchTouchEvent(view, event);
136
137        eventTime = SystemClock.uptimeMillis();
138        event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP,
139                                   x, y, 0);
140        dispatchTouchEvent(view, event);
141    }
142
143    /**
144     * Sends (synchronously) a long press to an absolute screen coordinates.
145     *
146     * @param x screen absolute
147     * @param y screen absolute
148     * @see TouchUtils
149     */
150    public void longPress(float x, float y) {
151
152        long downTime = SystemClock.uptimeMillis();
153        long eventTime = SystemClock.uptimeMillis();
154
155        MotionEvent event = MotionEvent.obtain(downTime, eventTime,
156                                               MotionEvent.ACTION_DOWN, x, y, 0);
157        dispatchTouchEvent(event);
158
159        int longPressTimeout = ViewConfiguration.get(
160                mActivityTestCase.getActivity()).getLongPressTimeout();
161
162        // Long press is flaky with just longPressTimeout. Doubling the time to be safe.
163        SystemClock.sleep(longPressTimeout * 2);
164
165        eventTime = SystemClock.uptimeMillis();
166        event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP,
167                                   x, y, 0);
168        dispatchTouchEvent(event);
169    }
170
171    /**
172     * Sends (synchronously) a long press to the View at the specified coordinates.
173     *
174     * @param v The view to be clicked.
175     * @param x Relative x location to v
176     * @param y Relative y location to v
177     */
178    public void longPressView(View v, int x, int y) {
179        int location[] = getAbsoluteLocationFromRelative(v, x, y);
180        int absoluteX = location[0];
181        int absoluteY = location[1];
182        longPress(absoluteX, absoluteY);
183    }
184
185    /**
186     * Send a MotionEvent to the root view of the activity.
187     * @param event
188     */
189    private void dispatchTouchEvent(final MotionEvent event) {
190        View view =
191                mActivityTestCase.getActivity().findViewById(android.R.id.content).getRootView();
192        dispatchTouchEvent(view, event);
193    }
194
195    /**
196     * Send a MotionEvent to the specified view instead of the root view.
197     * For example AutofillPopup window that is above the root view.
198     * @param view The view that should receive the event.
199     * @param event The view to be dispatched.
200     */
201    private void dispatchTouchEvent(final View view, final MotionEvent event) {
202        try {
203            mActivityTestCase.runTestOnUiThread(new Runnable() {
204                @Override
205                public void run() {
206                    view.dispatchTouchEvent(event);
207                }
208            });
209        } catch(Throwable e) {
210            throw new RuntimeException("Dispatching touch event failed", e);
211        }
212    }
213
214    /**
215     * Returns the absolute location in screen coordinates from location relative
216     * to view.
217     * @param v The view the coordinates are relative to.
218     * @param x Relative x location.
219     * @param y Relative y location.
220     * @return absolute x and y location in an array.
221     */
222    private static int[] getAbsoluteLocationFromRelative(View v, int x, int y) {
223        int location[] = new int[2];
224        v.getLocationOnScreen(location);
225        location[0] += x;
226        location[1] += y;
227        return location;
228    }
229}
230