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 | |
5 | package org.chromium.content.browser.test.util; |
6 | |
7 | import android.os.SystemClock; |
8 | import android.view.MotionEvent; |
9 | import android.view.View; |
10 | import android.view.ViewConfiguration; |
11 | import android.test.ActivityInstrumentationTestCase2; |
12 | |
13 | /** |
14 | * Touch-related functionality reused across test cases. |
15 | */ |
16 | public 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 to the center of the View. |
122 | */ |
123 | public void singleClickView(View v) { |
124 | singleClickView(v, v.getWidth() / 2, v.getHeight() / 2); |
125 | } |
126 | |
127 | /** |
128 | * Sends (synchronously) a single click on the specified relative coordinates inside |
129 | * a given view. |
130 | * |
131 | * @param view The view to be clicked. |
132 | * @param x screen absolute |
133 | * @param y screen absolute |
134 | * @see TouchUtils |
135 | */ |
136 | public void singleClickViewRelative(View view, int x, int y) { |
137 | long downTime = SystemClock.uptimeMillis(); |
138 | long eventTime = SystemClock.uptimeMillis(); |
139 | |
140 | MotionEvent event = MotionEvent.obtain(downTime, eventTime, |
141 | MotionEvent.ACTION_DOWN, x, y, 0); |
142 | dispatchTouchEvent(view, event); |
143 | |
144 | eventTime = SystemClock.uptimeMillis(); |
145 | event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, |
146 | x, y, 0); |
147 | dispatchTouchEvent(view, event); |
148 | } |
149 | |
150 | /** |
151 | * Sends (synchronously) a long press to an absolute screen coordinates. |
152 | * |
153 | * @param x screen absolute |
154 | * @param y screen absolute |
155 | * @see TouchUtils |
156 | */ |
157 | public void longPress(float x, float y) { |
158 | |
159 | long downTime = SystemClock.uptimeMillis(); |
160 | long eventTime = SystemClock.uptimeMillis(); |
161 | |
162 | MotionEvent event = MotionEvent.obtain(downTime, eventTime, |
163 | MotionEvent.ACTION_DOWN, x, y, 0); |
164 | dispatchTouchEvent(event); |
165 | |
166 | int longPressTimeout = ViewConfiguration.get( |
167 | mActivityTestCase.getActivity()).getLongPressTimeout(); |
168 | |
169 | // Long press is flaky with just longPressTimeout. Doubling the time to be safe. |
170 | SystemClock.sleep(longPressTimeout * 2); |
171 | |
172 | eventTime = SystemClock.uptimeMillis(); |
173 | event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, |
174 | x, y, 0); |
175 | dispatchTouchEvent(event); |
176 | } |
177 | |
178 | /** |
179 | * Sends (synchronously) a long press to the View at the specified coordinates. |
180 | * |
181 | * @param v The view to be clicked. |
182 | * @param x Relative x location to v |
183 | * @param y Relative y location to v |
184 | */ |
185 | public void longPressView(View v, int x, int y) { |
186 | int location[] = getAbsoluteLocationFromRelative(v, x, y); |
187 | int absoluteX = location[0]; |
188 | int absoluteY = location[1]; |
189 | longPress(absoluteX, absoluteY); |
190 | } |
191 | |
192 | /** |
193 | * Send a MotionEvent to the root view of the activity. |
194 | * @param event |
195 | */ |
196 | private void dispatchTouchEvent(final MotionEvent event) { |
197 | View view = |
198 | mActivityTestCase.getActivity().findViewById(android.R.id.content).getRootView(); |
199 | dispatchTouchEvent(view, event); |
200 | } |
201 | |
202 | /** |
203 | * Send a MotionEvent to the specified view instead of the root view. |
204 | * For example AutofillPopup window that is above the root view. |
205 | * @param view The view that should receive the event. |
206 | * @param event The view to be dispatched. |
207 | */ |
208 | private void dispatchTouchEvent(final View view, final MotionEvent event) { |
209 | try { |
210 | mActivityTestCase.runTestOnUiThread(new Runnable() { |
211 | @Override |
212 | public void run() { |
213 | view.dispatchTouchEvent(event); |
214 | } |
215 | }); |
216 | } catch(Throwable e) { |
217 | throw new RuntimeException("Dispatching touch event failed", e); |
218 | } |
219 | } |
220 | |
221 | /** |
222 | * Returns the absolute location in screen coordinates from location relative |
223 | * to view. |
224 | * @param v The view the coordinates are relative to. |
225 | * @param x Relative x location. |
226 | * @param y Relative y location. |
227 | * @return absolute x and y location in an array. |
228 | */ |
229 | private static int[] getAbsoluteLocationFromRelative(View v, int x, int y) { |
230 | int location[] = new int[2]; |
231 | v.getLocationOnScreen(location); |
232 | location[0] += x; |
233 | location[1] += y; |
234 | return location; |
235 | } |
236 | } |