1 /** 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 * except in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the 10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 * express or implied. See the License for the specific language governing permissions and 12 * limitations under the License. 13 */ 14 15 package android.accessibilityservice.cts; 16 17 import static org.junit.Assert.assertEquals; 18 import static org.junit.Assert.fail; 19 20 import android.accessibility.cts.common.InstrumentedAccessibilityService; 21 import android.accessibilityservice.AccessibilityGestureEvent; 22 import android.view.Display; 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 if (displayId == Display.DEFAULT_DISPLAY) { 158 String expected = AccessibilityGestureEvent.gestureIdToString(gestureId); 159 if (getGesturesSize() == 0) { 160 fail("No gesture received when expecting " + expected); 161 } else if (getGesturesSize() > 1) { 162 List<String> received = new ArrayList<>(); 163 for (int i = 0; i < getGesturesSize(); ++i) { 164 received.add(AccessibilityGestureEvent.gestureIdToString(getGesture(i))); 165 } 166 fail("Expected " + expected + " but received " + received); 167 } else { 168 String received = AccessibilityGestureEvent.gestureIdToString(getGesture(0)); 169 assertEquals(expected, received); 170 } 171 } 172 String expected = AccessibilityGestureEvent.gestureIdToString(gestureId); 173 if (getGestureInfoSize() == 0) { 174 fail("No gesture received when expecting " + expected); 175 } else if (getGestureInfoSize() > 1) { 176 List<String> received = new ArrayList<>(); 177 for (int i = 0; i < getGesturesSize(); ++i) { 178 received.add( 179 AccessibilityGestureEvent.gestureIdToString( 180 getGestureInfo(i).getGestureId())); 181 } 182 fail("Expected " + expected + " but received " + received); 183 } 184 AccessibilityGestureEvent expectedGestureEvent = 185 new AccessibilityGestureEvent(gestureId, displayId); 186 AccessibilityGestureEvent actualGestureEvent = getGestureInfo(0); 187 if (!expectedGestureEvent.toString().equals(actualGestureEvent.toString())) { 188 fail( 189 "Unexpected gesture received, " 190 + "Received " 191 + actualGestureEvent 192 + ", Expected " 193 + expectedGestureEvent); 194 } 195 clearGestures(); 196 } 197 198 /** Insure that the specified accessibility events have been received. */ assertPropagated(int... events)199 public void assertPropagated(int... events) { 200 waitUntilEvent(events.length); 201 // Set up readable error reporting. 202 List<String> received = new ArrayList<>(); 203 List<String> expected = new ArrayList<>(); 204 for (int event : events) { 205 expected.add(AccessibilityEvent.eventTypeToString(event)); 206 } 207 for (int i = 0; i < getEventsSize(); ++i) { 208 received.add(AccessibilityEvent.eventTypeToString(getEvent(i))); 209 } 210 211 if (events.length != getEventsSize()) { 212 String message = 213 String.format( 214 "Received %d events when expecting %d. Received %s, expected %s", 215 received.size(), 216 expected.size(), 217 received.toString(), 218 expected.toString()); 219 fail(message); 220 } 221 else if (!expected.equals(received)) { 222 String message = 223 String.format( 224 "Received %s, expected %s", 225 received.toString(), 226 expected.toString()); 227 fail(message); 228 } 229 } 230 } 231