1// Copyright 2013 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.android_webview.test.util;
6
7import android.os.SystemClock;
8import android.view.MotionEvent;
9import android.view.View;
10
11import java.util.concurrent.CountDownLatch;
12
13/**
14 * A touch utility class that injects the events directly into the view.
15 * TODO(mkosiba): Merge with TestTouchUtils.
16 *
17 * This is similar to TestTouchUtils but injects the events directly into the view class rather
18 * than going through Instrumentation. This is so that we can avoid the INJECT_PERMISSIONS
19 * exception when a modal dialog pops over the test activity.
20 */
21public class AwTestTouchUtils {
22    private static void sendAction(View view, int action, long downTime, float x, float y) {
23        long eventTime = SystemClock.uptimeMillis();
24        MotionEvent event = MotionEvent.obtain(downTime, eventTime, action, x, y, 0);
25        view.onTouchEvent(event);
26    }
27
28    private static long dragStart(View view, float x, float y) {
29        long downTime = SystemClock.uptimeMillis();
30        sendAction(view, MotionEvent.ACTION_DOWN, downTime, x, y);
31        return downTime;
32    }
33
34    private static void dragTo(View view, float fromX, float toX, float fromY,
35            float toY, int stepCount, long downTime) {
36        float x = fromX;
37        float y = fromY;
38        float yStep = (toY - fromY) / stepCount;
39        float xStep = (toX - fromX) / stepCount;
40        for (int i = 0; i < stepCount; ++i) {
41            y += yStep;
42            x += xStep;
43            sendAction(view, MotionEvent.ACTION_MOVE, downTime, x, y);
44        }
45    }
46
47    private static void dragEnd(View view, float x, float y, long downTime) {
48        sendAction(view, MotionEvent.ACTION_UP, downTime, x, y);
49    }
50
51    /**
52     * Performs a drag between the given coordinates, specified relative to the given view.
53     * This is safe to call from the instrumentation thread and will invoke the drag
54     * asynchronously.
55     *
56     * @param view The view the coordinates are relative to.
57     * @param fromX The relative x-coordinate of the start point of the drag.
58     * @param toX The relative x-coordinate of the end point of the drag.
59     * @param fromY The relative y-coordinate of the start point of the drag.
60     * @param toY The relative y-coordinate of the end point of the drag.
61     * @param stepCount The total number of motion events that should be generated during the drag.
62     * @param completionLatch The .countDown method is called on this latch once the drag finishes.
63     */
64    public static void dragCompleteView(final View view, final int fromX, final int toX,
65            final int fromY, final int toY, final int stepCount,
66            final CountDownLatch completionLatch) {
67        view.post(new Runnable() {
68            @Override
69            public void run() {
70                long downTime = dragStart(view, fromX, fromY);
71                dragTo(view, fromX, toX, fromY, toY, stepCount, downTime);
72                dragEnd(view, toX, toY, downTime);
73                if (completionLatch != null) {
74                    completionLatch.countDown();
75                }
76            }
77        });
78    }
79
80    /**
81     * Performs a single touch on the center of the supplied view.
82     * This is safe to call from the instrumentation thread and will invoke the touch
83     * asynchronously.
84     *
85     * @param view The view the coordinates are relative to.
86     */
87    public static void simulateTouchCenterOfView(final View view) throws Throwable {
88        view.post(new Runnable() {
89            @Override
90            public void run() {
91                long eventTime = SystemClock.uptimeMillis();
92                float x = (float) (view.getRight() - view.getLeft()) / 2;
93                float y = (float) (view.getBottom() - view.getTop()) / 2;
94                view.onTouchEvent(MotionEvent.obtain(
95                        eventTime, eventTime, MotionEvent.ACTION_DOWN,
96                        x, y, 0));
97                view.onTouchEvent(MotionEvent.obtain(
98                        eventTime, eventTime, MotionEvent.ACTION_UP,
99                        x, y, 0));
100            }
101        });
102    }
103}
104
105