1 /*
2  * Copyright (C) 2019 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 package android.accessibilityservice.cts;
17 
18 import static com.google.common.truth.Truth.assertThat;
19 import static com.google.common.truth.Truth.assertWithMessage;
20 
21 import android.accessibility.cts.common.InstrumentedAccessibilityService;
22 import android.accessibilityservice.AccessibilityGestureEvent;
23 import android.view.accessibility.AccessibilityEvent;
24 
25 import java.util.ArrayList;
26 import java.util.List;
27 
28 /** Accessibility service stub, which will collect recognized gestures. */
29 public class GestureDetectionStubAccessibilityService extends InstrumentedAccessibilityService {
30     private static final long GESTURE_RECOGNIZE_TIMEOUT_MS = 3000;
31     protected static final long EVENT_RECOGNIZE_TIMEOUT_MS = 5000;
32     // Member variables
33     protected final Object mLock = new Object();
34     private ArrayList<Integer> mCollectedGestures = new ArrayList();
35     private ArrayList<AccessibilityGestureEvent> mCollectedGestureEvents = new ArrayList();
36     protected ArrayList<Integer> mCollectedEvents = new ArrayList();
37 
38     @Override
onGesture(int gestureId)39     protected boolean onGesture(int gestureId) {
40         synchronized (mCollectedGestures) {
41             mCollectedGestures.add(gestureId);
42         }
43         return true;
44     }
45 
46     @Override
onGesture(AccessibilityGestureEvent gestureEvent)47     public boolean onGesture(AccessibilityGestureEvent gestureEvent) {
48         super.onGesture(gestureEvent);
49         synchronized (mCollectedGestureEvents) {
50             mCollectedGestureEvents.add(gestureEvent);
51             mCollectedGestureEvents.notifyAll(); // Stop waiting for gesture.
52         }
53         return true;
54     }
55 
clearGestures()56     public void clearGestures() {
57         synchronized (mCollectedGestures) {
58             mCollectedGestures.clear();
59         }
60         synchronized (mCollectedGestureEvents) {
61             mCollectedGestureEvents.clear();
62         }
63     }
64 
getGesturesSize()65     public int getGesturesSize() {
66         synchronized (mCollectedGestures) {
67             return mCollectedGestures.size();
68         }
69     }
70 
getGesture(int index)71     public int getGesture(int index) {
72         synchronized (mCollectedGestures) {
73             return mCollectedGestures.get(index);
74         }
75     }
76 
77     /** Waits for {@link #onGesture(AccessibilityGestureEvent)} to collect next gesture. */
waitUntilGestureInfo()78     public void waitUntilGestureInfo() {
79         synchronized (mCollectedGestureEvents) {
80             // Assume the size of mCollectedGestures is changed before mCollectedGestureEvents.
81             if (mCollectedGestureEvents.size() > 0) {
82                 return;
83             }
84             try {
85                 mCollectedGestureEvents.wait(GESTURE_RECOGNIZE_TIMEOUT_MS);
86             } catch (InterruptedException e) {
87                 throw new RuntimeException(e);
88             }
89         }
90     }
91 
getGestureInfoSize()92     public int getGestureInfoSize() {
93         synchronized (mCollectedGestureEvents) {
94             return mCollectedGestureEvents.size();
95         }
96     }
97 
getGestureInfo(int index)98     public AccessibilityGestureEvent getGestureInfo(int index) {
99         synchronized (mCollectedGestureEvents) {
100             return mCollectedGestureEvents.get(index);
101         }
102     }
103 
104     @Override
onAccessibilityEvent(AccessibilityEvent event)105     public void onAccessibilityEvent(AccessibilityEvent event) {
106         synchronized (mLock) {
107             switch (event.getEventType()) {
108                 case AccessibilityEvent.TYPE_TOUCH_INTERACTION_END:
109                     mCollectedEvents.add(event.getEventType());
110                     mLock.notifyAll();
111                     break;
112                 case AccessibilityEvent.TYPE_TOUCH_INTERACTION_START:
113                 case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START:
114                 case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END:
115                     mCollectedEvents.add(event.getEventType());
116             }
117         }
118         super.onAccessibilityEvent(event);
119     }
120 
clearEvents()121     public void clearEvents() {
122         synchronized (mLock) {
123             mCollectedEvents.clear();
124         }
125     }
126 
getEventsSize()127     public int getEventsSize() {
128         synchronized (mLock) {
129             return mCollectedEvents.size();
130         }
131     }
132 
getEvent(int index)133     public int getEvent(int index) {
134         synchronized (mLock) {
135             return mCollectedEvents.get(index);
136         }
137     }
138 
139     /** Wait for onAccessibilityEvent() to collect next gesture. */
waitUntilEvent(int count)140     public void waitUntilEvent(int count) {
141         synchronized (mLock) {
142             if (mCollectedEvents.size() >= count) {
143                 return;
144             }
145             try {
146                 mLock.wait(EVENT_RECOGNIZE_TIMEOUT_MS);
147             } catch (InterruptedException e) {
148                 throw new RuntimeException(e);
149             }
150         }
151     }
152 
153     /** Ensure that the specified accessibility gesture event has been received. */
assertGestureReceived(int gestureId, int displayId)154     public void assertGestureReceived(int gestureId, int displayId) {
155         // Wait for gesture recognizer, and check recognized gesture.
156         waitUntilGestureInfo();
157         AccessibilityGestureEvent expected = new AccessibilityGestureEvent(gestureId, displayId);
158         assertWithMessage("Expected " + expected.toString())
159                 .that(getGestureInfoSize())
160                 .isEqualTo(1);
161         AccessibilityGestureEvent received = getGestureInfo(0);
162         assertThat(expected.toString()).isEqualTo(received.toString());
163         clearGestures();
164     }
165 
166     /** Insure that the specified accessibility events have been received. */
assertPropagated(int... events)167     public void assertPropagated(int... events) {
168         waitUntilEvent(events.length);
169         // Set up readable error reporting.
170         List<String> received = new ArrayList<>();
171         List<String> expected = new ArrayList<>();
172         for (int event : events) {
173             expected.add(AccessibilityEvent.eventTypeToString(event));
174         }
175         for (int i = 0; i < getEventsSize(); ++i) {
176             received.add(AccessibilityEvent.eventTypeToString(getEvent(i)));
177         }
178         assertThat(expected).isEqualTo(received);
179     }
180 }
181