1/*
2 * Copyright (C) 2015 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.support.v7.util;
18
19import android.app.Instrumentation;
20import android.os.SystemClock;
21import android.support.v7.widget.RecyclerView;
22import android.view.Gravity;
23import android.view.MotionEvent;
24import android.view.View;
25import android.view.ViewConfiguration;
26
27/**
28 * RV specific layout tests.
29 */
30public class TouchUtils {
31    public static void tapView(Instrumentation inst, RecyclerView recyclerView,
32            View v) {
33        int[] xy = new int[2];
34        v.getLocationOnScreen(xy);
35
36        final int viewWidth = v.getWidth();
37        final int viewHeight = v.getHeight();
38
39        final float x = xy[0] + (viewWidth / 2.0f);
40        float y = xy[1] + (viewHeight / 2.0f);
41
42        long downTime = SystemClock.uptimeMillis();
43        long eventTime = SystemClock.uptimeMillis();
44
45        MotionEvent event = MotionEvent.obtain(downTime, eventTime,
46                MotionEvent.ACTION_DOWN, x, y, 0);
47        inst.sendPointerSync(event);
48        inst.waitForIdleSync();
49
50        eventTime = SystemClock.uptimeMillis();
51        final int touchSlop = ViewConfiguration.get(v.getContext()).getScaledTouchSlop();
52        event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE,
53                x + (touchSlop / 2.0f), y + (touchSlop / 2.0f), 0);
54        inst.sendPointerSync(event);
55        inst.waitForIdleSync();
56
57        eventTime = SystemClock.uptimeMillis();
58        event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, x, y, 0);
59        inst.sendPointerSync(event);
60        inst.waitForIdleSync();
61    }
62
63    public static void touchAndCancelView(Instrumentation inst, View v) {
64        int[] xy = new int[2];
65        v.getLocationOnScreen(xy);
66
67        final int viewWidth = v.getWidth();
68        final int viewHeight = v.getHeight();
69
70        final float x = xy[0] + (viewWidth / 2.0f);
71        float y = xy[1] + (viewHeight / 2.0f);
72
73        long downTime = SystemClock.uptimeMillis();
74        long eventTime = SystemClock.uptimeMillis();
75
76        MotionEvent event = MotionEvent.obtain(downTime, eventTime,
77                MotionEvent.ACTION_DOWN, x, y, 0);
78        inst.sendPointerSync(event);
79        inst.waitForIdleSync();
80
81        eventTime = SystemClock.uptimeMillis();
82        final int touchSlop = ViewConfiguration.get(v.getContext()).getScaledTouchSlop();
83        event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_CANCEL,
84                x + (touchSlop / 2.0f), y + (touchSlop / 2.0f), 0);
85        inst.sendPointerSync(event);
86        inst.waitForIdleSync();
87
88    }
89
90    public static void clickView(Instrumentation inst, View v) {
91        int[] xy = new int[2];
92        v.getLocationOnScreen(xy);
93
94        final int viewWidth = v.getWidth();
95        final int viewHeight = v.getHeight();
96
97        final float x = xy[0] + (viewWidth / 2.0f);
98        float y = xy[1] + (viewHeight / 2.0f);
99
100        long downTime = SystemClock.uptimeMillis();
101        long eventTime = SystemClock.uptimeMillis();
102
103        MotionEvent event = MotionEvent.obtain(downTime, eventTime,
104                MotionEvent.ACTION_DOWN, x, y, 0);
105        inst.sendPointerSync(event);
106        inst.waitForIdleSync();
107
108        eventTime = SystemClock.uptimeMillis();
109        final int touchSlop = ViewConfiguration.get(v.getContext()).getScaledTouchSlop();
110        event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE,
111                x + (touchSlop / 2.0f), y + (touchSlop / 2.0f), 0);
112        inst.sendPointerSync(event);
113        inst.waitForIdleSync();
114
115        eventTime = SystemClock.uptimeMillis();
116        event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, x, y, 0);
117        inst.sendPointerSync(event);
118        inst.waitForIdleSync();
119
120        try {
121            Thread.sleep(1000);
122        } catch (InterruptedException e) {
123            e.printStackTrace();
124        }
125    }
126
127    public static void longClickView(Instrumentation inst, View v) {
128        int[] xy = new int[2];
129        v.getLocationOnScreen(xy);
130
131        final int viewWidth = v.getWidth();
132        final int viewHeight = v.getHeight();
133
134        final float x = xy[0] + (viewWidth / 2.0f);
135        float y = xy[1] + (viewHeight / 2.0f);
136
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        inst.sendPointerSync(event);
143        inst.waitForIdleSync();
144
145        eventTime = SystemClock.uptimeMillis();
146        final int touchSlop = ViewConfiguration.get(v.getContext()).getScaledTouchSlop();
147        event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE,
148                x + touchSlop / 2, y + touchSlop / 2, 0);
149        inst.sendPointerSync(event);
150        inst.waitForIdleSync();
151
152        try {
153            Thread.sleep((long) (ViewConfiguration.getLongPressTimeout() * 1.5f));
154        } catch (InterruptedException e) {
155            e.printStackTrace();
156        }
157
158        eventTime = SystemClock.uptimeMillis();
159        event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, x, y, 0);
160        inst.sendPointerSync(event);
161        inst.waitForIdleSync();
162    }
163
164    public static void dragViewToTop(Instrumentation inst, View v) {
165        dragViewToTop(inst, v, calculateStepsForDistance(v.getTop()));
166    }
167
168    public static void dragViewToTop(Instrumentation inst, View v, int stepCount) {
169        int[] xy = new int[2];
170        v.getLocationOnScreen(xy);
171
172        final int viewWidth = v.getWidth();
173        final int viewHeight = v.getHeight();
174
175        final float x = xy[0] + (viewWidth / 2.0f);
176        float fromY = xy[1] + (viewHeight / 2.0f);
177        float toY = 0;
178
179        drag(inst, x, x, fromY, toY, stepCount);
180    }
181
182    private static void getStartLocation(View v, int gravity, int[] xy) {
183        v.getLocationOnScreen(xy);
184
185        final int viewWidth = v.getWidth();
186        final int viewHeight = v.getHeight();
187
188        switch (gravity & Gravity.VERTICAL_GRAVITY_MASK) {
189            case Gravity.TOP:
190                break;
191            case Gravity.CENTER_VERTICAL:
192                xy[1] += viewHeight / 2;
193                break;
194            case Gravity.BOTTOM:
195                xy[1] += viewHeight - 1;
196                break;
197            default:
198                // Same as top -- do nothing
199        }
200
201        switch (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
202            case Gravity.LEFT:
203                break;
204            case Gravity.CENTER_HORIZONTAL:
205                xy[0] += viewWidth / 2;
206                break;
207            case Gravity.RIGHT:
208                xy[0] += viewWidth - 1;
209                break;
210            default:
211                // Same as left -- do nothing
212        }
213    }
214
215    public static int dragViewTo(Instrumentation inst, View v, int gravity, int toX,
216            int toY) {
217        int[] xy = new int[2];
218
219        getStartLocation(v, gravity, xy);
220
221        final int fromX = xy[0];
222        final int fromY = xy[1];
223
224        int deltaX = fromX - toX;
225        int deltaY = fromY - toY;
226
227        int distance = (int) Math.sqrt(deltaX * deltaX + deltaY * deltaY);
228        drag(inst, fromX, toX, fromY, toY, calculateStepsForDistance(distance));
229
230        return distance;
231    }
232
233    public static int dragViewToX(Instrumentation inst, View v, int gravity, int toX) {
234        int[] xy = new int[2];
235
236        getStartLocation(v, gravity, xy);
237
238        final int fromX = xy[0];
239        final int fromY = xy[1];
240
241        int deltaX = fromX - toX;
242
243        drag(inst, fromX, toX, fromY, fromY, calculateStepsForDistance(deltaX));
244
245        return deltaX;
246    }
247
248    public static int dragViewToY(Instrumentation inst, View v, int gravity, int toY) {
249        int[] xy = new int[2];
250
251        getStartLocation(v, gravity, xy);
252
253        final int fromX = xy[0];
254        final int fromY = xy[1];
255
256        int deltaY = fromY - toY;
257
258        drag(inst, fromX, fromX, fromY, toY, calculateStepsForDistance(deltaY));
259
260        return deltaY;
261    }
262
263
264    public static void drag(Instrumentation inst, float fromX, float toX, float fromY,
265            float toY, int stepCount) {
266        long downTime = SystemClock.uptimeMillis();
267        long eventTime = SystemClock.uptimeMillis();
268
269        float y = fromY;
270        float x = fromX;
271
272        float yStep = (toY - fromY) / stepCount;
273        float xStep = (toX - fromX) / stepCount;
274
275        MotionEvent event = MotionEvent.obtain(downTime, eventTime,
276                MotionEvent.ACTION_DOWN, x, y, 0);
277        inst.sendPointerSync(event);
278        for (int i = 0; i < stepCount; ++i) {
279            y += yStep;
280            x += xStep;
281            eventTime = SystemClock.uptimeMillis();
282            event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE, x, y, 0);
283            inst.sendPointerSync(event);
284        }
285
286        eventTime = SystemClock.uptimeMillis();
287        event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, x, y, 0);
288        inst.sendPointerSync(event);
289        inst.waitForIdleSync();
290    }
291
292    private static int calculateStepsForDistance(int distance) {
293        return 1 + Math.abs(distance) / 10;
294    }
295}
296