• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 com.android.server.accessibility.gestures;
18 
19 import static org.mockito.ArgumentMatchers.argThat;
20 import static org.mockito.Mockito.mock;
21 import static org.mockito.Mockito.reset;
22 import static org.mockito.Mockito.verify;
23 import static org.mockito.Mockito.when;
24 
25 import android.accessibilityservice.AccessibilityService;
26 import android.content.Context;
27 import android.graphics.Point;
28 import android.graphics.PointF;
29 import android.os.Handler;
30 import android.view.MotionEvent;
31 
32 import androidx.test.InstrumentationRegistry;
33 
34 import org.junit.Before;
35 import org.junit.Test;
36 
37 import java.util.ArrayList;
38 
39 /**
40  * Tests for GestureManifold
41  */
42 public class GestureManifoldTest {
43 
44     // Constants for testRecognizeGesturePath()
45     private static final PointF PATH_START = new PointF(300f, 300f);
46     private static final int PATH_STEP_PIXELS = 200;
47     private static final long PATH_STEP_MILLISEC = 100;
48 
49     // Data used by all tests
50     private GestureManifold mManifold;
51     private TouchState mState;
52     private GestureManifold.Listener mResultListener;
53 
54     @Before
setUp()55     public void setUp() {
56         Context context = InstrumentationRegistry.getContext();
57         // Construct a testable GestureManifold.
58         mResultListener = mock(GestureManifold.Listener.class);
59         mState = new TouchState();
60         Handler handler = new Handler(context.getMainLooper());
61         mManifold = new GestureManifold(context, mResultListener, mState, handler);
62         // Play the role of touch explorer in updating the shared state.
63         when(mResultListener.onGestureStarted()).thenReturn(onGestureStarted());
64 
65 
66     }
67 
68 
69     @Test
testRecognizeGesturePath()70     public void testRecognizeGesturePath() {
71         final int d = 1000;  // Length of each segment in the test gesture, in pixels.
72 
73         testPath(p(-d, +0), AccessibilityService.GESTURE_SWIPE_LEFT);
74         testPath(p(+d, +0), AccessibilityService.GESTURE_SWIPE_RIGHT);
75         testPath(p(+0, -d), AccessibilityService.GESTURE_SWIPE_UP);
76         testPath(p(+0, +d), AccessibilityService.GESTURE_SWIPE_DOWN);
77 
78         testPath(p(-d, +0), p((-d - d), +0), AccessibilityService.GESTURE_SWIPE_LEFT);
79         testPath(p(-d, +0), p(+0, +0), AccessibilityService.GESTURE_SWIPE_LEFT_AND_RIGHT);
80         testPath(p(-d, +0), p(-d, -d), AccessibilityService.GESTURE_SWIPE_LEFT_AND_UP);
81         testPath(p(-d, +0), p(-d, +d), AccessibilityService.GESTURE_SWIPE_LEFT_AND_DOWN);
82 
83         testPath(p(+d, +0), p(+0, +0), AccessibilityService.GESTURE_SWIPE_RIGHT_AND_LEFT);
84         testPath(p(+d, +0), p((+d + d), +0), AccessibilityService.GESTURE_SWIPE_RIGHT);
85         testPath(p(+d, +0), p(+d, -d), AccessibilityService.GESTURE_SWIPE_RIGHT_AND_UP);
86         testPath(p(+d, +0), p(+d, +d), AccessibilityService.GESTURE_SWIPE_RIGHT_AND_DOWN);
87 
88         testPath(p(+0, -d), p(-d, -d), AccessibilityService.GESTURE_SWIPE_UP_AND_LEFT);
89         testPath(p(+0, -d), p(+d, -d), AccessibilityService.GESTURE_SWIPE_UP_AND_RIGHT);
90         testPath(p(+0, -d), p(+0, (-d - d)), AccessibilityService.GESTURE_SWIPE_UP);
91         testPath(p(+0, -d), p(+0, +0), AccessibilityService.GESTURE_SWIPE_UP_AND_DOWN);
92 
93         testPath(p(+0, +d), p(-d, +d), AccessibilityService.GESTURE_SWIPE_DOWN_AND_LEFT);
94         testPath(p(+0, +d), p(+d, +d), AccessibilityService.GESTURE_SWIPE_DOWN_AND_RIGHT);
95         testPath(p(+0, +d), p(+0, +0), AccessibilityService.GESTURE_SWIPE_DOWN_AND_UP);
96         testPath(p(+0, +d), p(+0, (+d + d)), AccessibilityService.GESTURE_SWIPE_DOWN);
97     }
98 
99     /** Convenient short alias to make a Point. */
p(int x, int y)100     private static Point p(int x, int y) {
101         return new Point(x, y);
102     }
103 
104     /** Test recognizing path from PATH_START to PATH_START+delta. */
testPath(Point delta, int gestureId)105     private void testPath(Point delta, int gestureId) {
106         ArrayList<PointF> path = new ArrayList<>();
107         path.add(PATH_START);
108 
109         PointF segmentEnd = new PointF(PATH_START.x + delta.x, PATH_START.y + delta.y);
110         fillPath(PATH_START, segmentEnd, path);
111 
112         testPath(path, gestureId);
113     }
114 
115     /** Test recognizing path from PATH_START to PATH_START+delta1 to PATH_START+delta2. */
testPath(Point delta1, Point delta2, int gestureId)116     private void testPath(Point delta1, Point delta2, int gestureId) {
117         ArrayList<PointF> path = new ArrayList<>();
118         path.add(PATH_START);
119 
120         PointF startPlusDelta1 = new PointF(PATH_START.x + delta1.x, PATH_START.y + delta1.y);
121         fillPath(PATH_START, startPlusDelta1, path);
122 
123         PointF startPlusDelta2 = new PointF(PATH_START.x + delta2.x, PATH_START.y + delta2.y);
124         fillPath(startPlusDelta1, startPlusDelta2, path);
125 
126         testPath(path, gestureId);
127     }
128 
129     /** Fill in movement points from start to end, appending points to path. */
fillPath(PointF start, PointF end, ArrayList<PointF> path)130     private void fillPath(PointF start, PointF end, ArrayList<PointF> path) {
131         // Calculate number of path steps needed.
132         float deltaX = end.x - start.x;
133         float deltaY = end.y - start.y;
134         float distance = (float) Math.hypot(deltaX, deltaY);
135         float numSteps = distance / (float) PATH_STEP_PIXELS;
136         float stepX = (float) deltaX / numSteps;
137         float stepY = (float) deltaY / numSteps;
138 
139         // For each path step from start (non-inclusive) to end ... add a motion point.
140         for (int step = 1; step < numSteps; ++step) {
141             path.add(new PointF(
142                     (start.x + (stepX * (float) step)),
143                     (start.y + (stepY * (float) step))));
144         }
145     }
146 
147     /** Test recognizing a path made of motion event points. */
testPath(ArrayList<PointF> path, int gestureId)148     private void testPath(ArrayList<PointF> path, int gestureId) {
149         // Clear last recognition result.
150         reset(mResultListener);
151 
152         int policyFlags = 0;
153         long eventDownTimeMs = 0;
154         long eventTimeMs = eventDownTimeMs;
155 
156         // For each path point...
157         for (int pointIndex = 0; pointIndex < path.size(); ++pointIndex) {
158 
159             // Create motion event.
160             PointF point = path.get(pointIndex);
161             int action = MotionEvent.ACTION_MOVE;
162             if (pointIndex == 0) {
163                 action = MotionEvent.ACTION_DOWN;
164             } else if (pointIndex == path.size() - 1) {
165                 action = MotionEvent.ACTION_UP;
166             }
167             MotionEvent event = MotionEvent.obtain(eventDownTimeMs, eventTimeMs, action,
168                     point.x, point.y, 0);
169 
170             // Send event.
171             mState.onReceivedMotionEvent(event);
172             mManifold.onMotionEvent(event, event, policyFlags);
173             eventTimeMs += PATH_STEP_MILLISEC;
174             if (mState.isClear()) {
175                 mState.startTouchInteracting();
176             }
177         }
178 
179         mState.clear();
180         // Check that correct gesture was recognized.
181         verify(mResultListener).onGestureCompleted(
182                 argThat(gestureEvent -> gestureEvent.getGestureId() == gestureId));
183     }
184 
onGestureStarted()185     private boolean onGestureStarted() {
186         mState.startGestureDetecting();
187         return false;
188     }
189 }
190