1 /*
2  * Copyright (C) 2017 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 android.accessibilityservice.cts.utils;
18 
19 import android.accessibility.cts.common.InstrumentedAccessibilityService;
20 import android.accessibilityservice.AccessibilityService.GestureResultCallback;
21 import android.accessibilityservice.GestureDescription;
22 import android.accessibilityservice.GestureDescription.StrokeDescription;
23 import android.graphics.Path;
24 import android.graphics.PointF;
25 import android.view.ViewConfiguration;
26 
27 import java.util.concurrent.CompletableFuture;
28 
29 public class GestureUtils {
30 
GestureUtils()31     private GestureUtils() {}
32 
dispatchGesture( InstrumentedAccessibilityService service, GestureDescription gesture)33     public static CompletableFuture<Void> dispatchGesture(
34             InstrumentedAccessibilityService service,
35             GestureDescription gesture) {
36         CompletableFuture<Void> result = new CompletableFuture<>();
37         GestureResultCallback callback = new GestureResultCallback() {
38             @Override
39             public void onCompleted(GestureDescription gestureDescription) {
40                 result.complete(null);
41             }
42 
43             @Override
44             public void onCancelled(GestureDescription gestureDescription) {
45                 result.cancel(false);
46             }
47         };
48         service.runOnServiceSync(() -> {
49             if (!service.dispatchGesture(gesture, callback, null)) {
50                 result.completeExceptionally(new IllegalStateException());
51             }
52         });
53         return result;
54     }
55 
pointerDown(PointF point)56     public static StrokeDescription pointerDown(PointF point) {
57         return new StrokeDescription(path(point), 0, ViewConfiguration.getTapTimeout(), true);
58     }
59 
pointerUp(StrokeDescription lastStroke)60     public static StrokeDescription pointerUp(StrokeDescription lastStroke) {
61         return lastStroke.continueStroke(path(lastPointOf(lastStroke)),
62                 endTimeOf(lastStroke), ViewConfiguration.getTapTimeout(), false);
63     }
64 
lastPointOf(StrokeDescription stroke)65     public static PointF lastPointOf(StrokeDescription stroke) {
66         float[] p = stroke.getPath().approximate(0.3f);
67         return new PointF(p[p.length - 2], p[p.length - 1]);
68     }
69 
click(PointF point)70     public static StrokeDescription click(PointF point) {
71         return new StrokeDescription(path(point), 0, ViewConfiguration.getTapTimeout());
72     }
73 
longClick(PointF point)74     public static StrokeDescription longClick(PointF point) {
75         return new StrokeDescription(path(point), 0,
76                 ViewConfiguration.getLongPressTimeout() * 3 / 2);
77     }
78 
swipe(PointF from, PointF to)79     public static StrokeDescription swipe(PointF from, PointF to) {
80         return swipe(from, to, ViewConfiguration.getTapTimeout());
81     }
82 
swipe(PointF from, PointF to, long duration)83     public static StrokeDescription swipe(PointF from, PointF to, long duration) {
84         return new StrokeDescription(path(from, to), 0, duration);
85     }
86 
drag(StrokeDescription from, PointF to)87     public static StrokeDescription drag(StrokeDescription from, PointF to) {
88         return from.continueStroke(
89                 path(lastPointOf(from), to),
90                 endTimeOf(from), ViewConfiguration.getTapTimeout(), true);
91     }
92 
path(PointF first, PointF... rest)93     public static Path path(PointF first, PointF... rest) {
94         Path path = new Path();
95         path.moveTo(first.x, first.y);
96         for (PointF point : rest) {
97             path.lineTo(point.x, point.y);
98         }
99         return path;
100     }
101 
startingAt(long timeMs, StrokeDescription prototype)102     public static StrokeDescription startingAt(long timeMs, StrokeDescription prototype) {
103         return new StrokeDescription(
104                 prototype.getPath(), timeMs, prototype.getDuration(), prototype.willContinue());
105     }
106 
endTimeOf(StrokeDescription stroke)107     public static long endTimeOf(StrokeDescription stroke) {
108         return stroke.getStartTime() + stroke.getDuration();
109     }
110 
distance(PointF a, PointF b)111     public static float distance(PointF a, PointF b) {
112         if (a == null) throw new NullPointerException();
113         if (b == null) throw new NullPointerException();
114         return (float) Math.hypot(a.x - b.x, a.y - b.y);
115     }
116 
add(PointF a, float x, float y)117     public static PointF add(PointF a, float x, float y) {
118         return new PointF(a.x + x, a.y + y);
119     }
120 
add(PointF a, PointF b)121     public static PointF add(PointF a, PointF b) {
122         return add(a, b.x, b.y);
123     }
124 
diff(PointF a, PointF b)125     public static PointF diff(PointF a, PointF b) {
126         return add(a, -b.x, -b.y);
127     }
128 
negate(PointF p)129     public static PointF negate(PointF p) {
130         return times(-1, p);
131     }
132 
times(float mult, PointF p)133     public static PointF times(float mult, PointF p) {
134         return new PointF(p.x * mult, p.y * mult);
135     }
136 
length(PointF p)137     public static float length(PointF p) {
138         return (float) Math.hypot(p.x, p.y);
139     }
140 
ceil(PointF p)141     public static PointF ceil(PointF p) {
142         return new PointF((float) Math.ceil(p.x), (float) Math.ceil(p.y));
143     }
144 }
145