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