1 /*
2  * Copyright (C) 2014 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 
17 package androidx.test.uiautomator;
18 
19 import android.graphics.Point;
20 import android.graphics.Rect;
21 import android.view.ViewConfiguration;
22 
23 /** Factory methods for constructing {@link PointerGesture}s. */
24 class Gestures {
25 
26     // Duration of a long press (with multiplier to ensure detection).
27     private static final long LONG_PRESS_DURATION_MS =
28             (long) (ViewConfiguration.getLongPressTimeout() * 1.5f);
29 
30     // Constants used by pinch gestures
31     private static final int INNER = 0;
32     private static final int OUTER = 1;
33 
34     private final int mDisplayId;
35     private final int mWindowId;
36 
37     /**
38      * Construct a {@link Gestures} factory for building gestures that target the given display and
39      * window IDs.
40      *
41      * @param displayId The ID of the display the gesture is on.
42      * @param windowId The ID of the window the gesture is on.
43      */
Gestures(int displayId, int windowId)44     Gestures(int displayId, int windowId) {
45         mDisplayId = displayId;
46         mWindowId = windowId;
47     }
48 
49     /**
50      * Returns a {@link PointerGesture} representing a click at the given {@code point}.
51      *
52      * @param point The point to click.
53      * @return The {@link PointerGesture} representing this click.
54      */
click(Point point)55     public PointerGesture click(Point point) {
56         // A basic click is a touch down and touch up over the same point with no delay.
57         return click(point, 0);
58     }
59 
60     /**
61      * Returns a {@link PointerGesture} representing a click at the given {@code point} that lasts
62      * for {@code duration} milliseconds.
63      *
64      * @param point The point to click.
65      * @param duration The duration of the click in milliseconds.
66      * @return The {@link PointerGesture} representing this click.
67      */
click(Point point, long duration)68     public PointerGesture click(Point point, long duration) {
69         // A click is a touch down and touch up over the same point with an optional delay inbetween
70         return new PointerGesture(point, mDisplayId, mWindowId).pause(duration);
71     }
72 
73     /**
74      * Returns a {@link PointerGesture} representing a long click at the given {@code point}.
75      *
76      * @param point The point to click.
77      * @return The {@link PointerGesture} representing this long click.
78      */
longClick(Point point)79     public PointerGesture longClick(Point point) {
80         // A long click is a click with a duration that exceeds a certain threshold.
81         return click(point, LONG_PRESS_DURATION_MS);
82     }
83 
84     /**
85      * Returns a {@link PointerGesture} representing a swipe.
86      *
87      * @param start The touch down point for the swipe.
88      * @param end The touch up point for the swipe.
89      * @param speed The speed at which to move in pixels per second.
90      * @return The {@link PointerGesture} representing this swipe.
91      */
swipe(Point start, Point end, int speed)92     public PointerGesture swipe(Point start, Point end, int speed) {
93         // A swipe is a click that moves before releasing the pointer.
94         return new PointerGesture(start, mDisplayId, mWindowId).move(end, speed);
95     }
96 
97     /**
98      * Returns a {@link PointerGesture} representing a horizontal or vertical swipe over an area.
99      *
100      * @param area The area to swipe over.
101      * @param direction The direction in which to swipe.
102      * @param percent The size of the swipe as a percentage of the total area.
103      * @param speed The speed at which to move in pixels per second.
104      * @return The {@link PointerGesture} representing this swipe.
105      */
swipeRect(Rect area, Direction direction, float percent, int speed)106     public PointerGesture swipeRect(Rect area, Direction direction, float percent, int speed) {
107         Point start, end;
108         // TODO: Reverse horizontal direction if locale is RTL
109         switch (direction) {
110             case LEFT:
111                 start = new Point(area.right, area.centerY());
112                 end   = new Point(area.right - (int)(area.width() * percent), area.centerY());
113                 break;
114             case RIGHT:
115                 start = new Point(area.left, area.centerY());
116                 end   = new Point(area.left + (int)(area.width() * percent), area.centerY());
117                 break;
118             case UP:
119                 start = new Point(area.centerX(), area.bottom);
120                 end   = new Point(area.centerX(), area.bottom - (int)(area.height() * percent));
121                 break;
122             case DOWN:
123                 start = new Point(area.centerX(), area.top);
124                 end   = new Point(area.centerX(), area.top + (int)(area.height() * percent));
125                 break;
126             default:
127                 throw new RuntimeException();
128         }
129 
130         return swipe(start, end, speed);
131     }
132 
133     /**
134      * Returns a {@link PointerGesture} representing a click and drag.
135      *
136      * @param start The touch down point for the swipe.
137      * @param end The touch up point for the swipe.
138      * @param speed The speed at which to move in pixels per second.
139      * @return The {@link PointerGesture} representing this swipe.
140      */
drag(Point start, Point end, int speed)141     public PointerGesture drag(Point start, Point end, int speed) {
142         // A drag is a swipe that starts with a long click.
143         return longClick(start).move(end, speed);
144     }
145 
146     /**
147      * Returns an array of {@link PointerGesture}s representing a pinch close.
148      *
149      * @param area The area to pinch over.
150      * @param percent The size of the pinch as a percentage of the total area.
151      * @param speed The speed at which to move in pixels per second.
152      * @return An array containing the two PointerGestures representing this pinch.
153      */
pinchClose(Rect area, float percent, int speed)154     public PointerGesture[] pinchClose(Rect area, float percent, int speed) {
155         Point[] bottomLeft = new Point[2];
156         Point[] topRight = new Point[2];
157         calcPinchCoordinates(area, percent, bottomLeft, topRight);
158 
159         // A pinch close is a multi-point gesture composed of two swipes moving from the outer
160         // coordinates to the inner ones.
161         return new PointerGesture[] {
162             swipe(bottomLeft[OUTER], bottomLeft[INNER], speed).pause(250),
163             swipe(topRight[OUTER], topRight[INNER], speed).pause(250)
164         };
165     }
166 
167     /**
168      * Returns an array of {@link PointerGesture}s representing a pinch close.
169      *
170      * @param area The area to pinch over.
171      * @param percent The size of the pinch as a percentage of the total area.
172      * @param speed The speed at which to move in pixels per second.
173      * @return An array containing the two PointerGestures representing this pinch.
174      */
pinchOpen(Rect area, float percent, int speed)175     public PointerGesture[] pinchOpen(Rect area, float percent, int speed) {
176         Point[] bottomLeft = new Point[2];
177         Point[] topRight = new Point[2];
178         calcPinchCoordinates(area, percent, bottomLeft, topRight);
179 
180         // A pinch open is a multi-point gesture composed of two swipes moving from the inner
181         // coordinates to the outer ones.
182         return new PointerGesture[] {
183             swipe(bottomLeft[INNER], bottomLeft[OUTER], speed),
184             swipe(topRight[INNER], topRight[OUTER], speed)
185         };
186     }
187 
188     /** Calculates the inner and outer coordinates used in a pinch gesture. */
calcPinchCoordinates(Rect area, float percent, Point[] bottomLeft, Point[] topRight)189     private static void calcPinchCoordinates(Rect area, float percent,
190             Point[] bottomLeft, Point[] topRight) {
191 
192         int offsetX = (int) (area.width() / 2 * percent);
193         int offsetY = (int) (area.height() / 2 * percent);
194 
195         // Outer set of pinch coordinates
196         bottomLeft[OUTER] = new Point(area.left, area.bottom);
197         topRight[OUTER]   = new Point(area.right, area.top);
198 
199         // Inner set of pinch coordinates
200         bottomLeft[INNER] = new Point(bottomLeft[OUTER]);
201         bottomLeft[INNER].offset(offsetX, -offsetY);
202         topRight[INNER] = new Point(topRight[OUTER]);
203         topRight[INNER].offset(-offsetX, offsetY);
204     }
205 }
206