1 /** 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 * 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.utils; 16 17 import static org.hamcrest.CoreMatchers.allOf; 18 import static org.hamcrest.CoreMatchers.both; 19 20 import android.app.UiAutomation; 21 import android.app.UiAutomation.AccessibilityEventFilter; 22 import android.util.SparseArray; 23 import android.view.Display; 24 import android.view.accessibility.AccessibilityEvent; 25 import android.view.accessibility.AccessibilityWindowInfo; 26 27 import androidx.annotation.NonNull; 28 29 import org.hamcrest.Description; 30 import org.hamcrest.TypeSafeMatcher; 31 32 import java.util.Arrays; 33 import java.util.LinkedList; 34 import java.util.List; 35 import java.util.function.BiPredicate; 36 37 /** 38 * Utility class for creating AccessibilityEventFilters 39 */ 40 public class AccessibilityEventFilterUtils { filterForEventType(int eventType)41 public static AccessibilityEventFilter filterForEventType(int eventType) { 42 return (new AccessibilityEventTypeMatcher(eventType))::matches; 43 } 44 filterWindowContentChangedWithChangeTypes(int changes)45 public static AccessibilityEventFilter filterWindowContentChangedWithChangeTypes(int changes) { 46 return (both(new AccessibilityEventTypeMatcher( 47 AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED)).and( 48 new ContentChangesMatcher(changes)))::matches; 49 } 50 filterWindowsChangedWithChangeTypes(int changes)51 public static AccessibilityEventFilter filterWindowsChangedWithChangeTypes(int changes) { 52 return (both(new AccessibilityEventTypeMatcher(AccessibilityEvent.TYPE_WINDOWS_CHANGED)) 53 .and(new WindowChangesMatcher(changes)))::matches; 54 } 55 filterForEventTypeWithResource(int eventType, String ResourceName)56 public static AccessibilityEventFilter filterForEventTypeWithResource(int eventType, 57 String ResourceName) { 58 TypeSafeMatcher<AccessibilityEvent> matchResourceName = new PropertyMatcher<>( 59 ResourceName, "Resource name", 60 (event, expect) -> event.getSource() != null 61 && event.getSource().getViewIdResourceName().equals(expect)); 62 return (both(new AccessibilityEventTypeMatcher(eventType)).and(matchResourceName))::matches; 63 } 64 filterForEventTypeWithAction(int eventType, int action)65 public static AccessibilityEventFilter filterForEventTypeWithAction(int eventType, int action) { 66 TypeSafeMatcher<AccessibilityEvent> matchAction = 67 new PropertyMatcher<>( 68 action, "Action", (event, expect) -> event.getAction() == action); 69 return (both(new AccessibilityEventTypeMatcher(eventType)).and(matchAction))::matches; 70 } 71 filterWindowsChangeTypesAndWindowTitle( @onNull UiAutomation uiAutomation, int changeTypes, @NonNull String title)72 public static AccessibilityEventFilter filterWindowsChangeTypesAndWindowTitle( 73 @NonNull UiAutomation uiAutomation, int changeTypes, @NonNull String title) { 74 return filterWindowsChangeTypesAndWindowTitle(uiAutomation, changeTypes, title, 75 Display.DEFAULT_DISPLAY); 76 } 77 filterWindowsChangeTypesAndWindowTitle( @onNull UiAutomation uiAutomation, int changeTypes, @NonNull String title, int displayId)78 public static AccessibilityEventFilter filterWindowsChangeTypesAndWindowTitle( 79 @NonNull UiAutomation uiAutomation, int changeTypes, @NonNull String title, 80 int displayId) { 81 return allOf(new AccessibilityEventTypeMatcher(AccessibilityEvent.TYPE_WINDOWS_CHANGED), 82 new WindowChangesMatcher(changeTypes), 83 new WindowTitleMatcher(uiAutomation, title, displayId))::matches; 84 } 85 filterWindowsChangTypesAndWindowId(int windowId, int changeTypes)86 public static AccessibilityEventFilter filterWindowsChangTypesAndWindowId(int windowId, 87 int changeTypes) { 88 return allOf(new AccessibilityEventTypeMatcher(AccessibilityEvent.TYPE_WINDOWS_CHANGED), 89 new WindowChangesMatcher(changeTypes), 90 new WindowIdMatcher(windowId))::matches; 91 } 92 93 /** 94 * Creates an {@link AccessibilityEventFilter} that returns {@code true} once all the given 95 * filters return {@code true} for any event. 96 * Each given filters are invoked on every AccessibilityEvent until it returns {@code true}. 97 * After all filters return {@code true} once, the created filter returns {@code true} forever. 98 */ filterWaitForAll(AccessibilityEventFilter... filters)99 public static AccessibilityEventFilter filterWaitForAll(AccessibilityEventFilter... filters) { 100 return new AccessibilityEventFilter() { 101 private final List<AccessibilityEventFilter> mUnresolved = 102 new LinkedList<>(Arrays.asList(filters)); 103 104 @Override 105 public boolean accept(AccessibilityEvent event) { 106 mUnresolved.removeIf(filter -> filter.accept(event)); 107 return mUnresolved.isEmpty(); 108 } 109 }; 110 } 111 112 /** 113 * Returns a matcher for a display id from getDisplayId(). 114 * @param displayId the display id to match. 115 * @return a matcher for comparing display ids. 116 */ matcherForDisplayId(int displayId)117 public static TypeSafeMatcher<AccessibilityEvent> matcherForDisplayId(int displayId) { 118 final TypeSafeMatcher<AccessibilityEvent> matchAction = 119 new PropertyMatcher<>( 120 displayId, "Display id", 121 (event, expect) -> event.getDisplayId() == displayId); 122 return matchAction; 123 } 124 125 /** 126 * Returns a matcher for a class name from getClassName(). 127 * @param className the class name to match. 128 * @return a matcher for comparing class names. 129 */ matcherForClassName(CharSequence className)130 public static TypeSafeMatcher<AccessibilityEvent> matcherForClassName(CharSequence className) { 131 final TypeSafeMatcher<AccessibilityEvent> matchAction = 132 new PropertyMatcher<>( 133 className, "Class name", 134 (event, expect) -> event.getClassName().equals(className)); 135 return matchAction; 136 } 137 138 /** 139 * Returns a matcher for the first text instance from getText(). 140 * @param text the text to match. 141 * @return a matcher for comparing first text instances. 142 */ matcherForFirstText(CharSequence text)143 public static TypeSafeMatcher<AccessibilityEvent> matcherForFirstText(CharSequence text) { 144 final TypeSafeMatcher<AccessibilityEvent> matchAction = 145 new PropertyMatcher<>( 146 text, "Text", 147 (event, expect) -> { 148 if (event.getText() != null && event.getText().size() > 0) { 149 return event.getText().get(0).equals(text); 150 } 151 return false; 152 }); 153 return matchAction; 154 } 155 156 public static class AccessibilityEventTypeMatcher extends TypeSafeMatcher<AccessibilityEvent> { 157 private int mType; 158 AccessibilityEventTypeMatcher(int type)159 public AccessibilityEventTypeMatcher(int type) { 160 super(); 161 mType = type; 162 } 163 164 @Override matchesSafely(AccessibilityEvent event)165 protected boolean matchesSafely(AccessibilityEvent event) { 166 return event.getEventType() == mType; 167 } 168 169 @Override describeTo(Description description)170 public void describeTo(Description description) { 171 description.appendText("Matching to type " + mType); 172 } 173 } 174 175 public static class WindowChangesMatcher extends TypeSafeMatcher<AccessibilityEvent> { 176 private int mWindowChanges; 177 WindowChangesMatcher(int windowChanges)178 public WindowChangesMatcher(int windowChanges) { 179 super(); 180 mWindowChanges = windowChanges; 181 } 182 183 @Override matchesSafely(AccessibilityEvent event)184 protected boolean matchesSafely(AccessibilityEvent event) { 185 return (event.getWindowChanges() & mWindowChanges) == mWindowChanges; 186 } 187 188 @Override describeTo(Description description)189 public void describeTo(Description description) { 190 description.appendText("With window change type " + mWindowChanges); 191 } 192 } 193 194 public static class ContentChangesMatcher extends TypeSafeMatcher<AccessibilityEvent> { 195 private int mContentChanges; 196 ContentChangesMatcher(int contentChanges)197 public ContentChangesMatcher(int contentChanges) { 198 super(); 199 mContentChanges = contentChanges; 200 } 201 202 @Override matchesSafely(AccessibilityEvent event)203 protected boolean matchesSafely(AccessibilityEvent event) { 204 return (event.getContentChangeTypes() & mContentChanges) == mContentChanges; 205 } 206 207 @Override describeTo(Description description)208 public void describeTo(Description description) { 209 description.appendText("With content change type " + mContentChanges); 210 } 211 } 212 213 public static class PropertyMatcher<T> extends TypeSafeMatcher<AccessibilityEvent> { 214 private T mProperty; 215 private String mDescription; 216 private BiPredicate<AccessibilityEvent, T> mComparator; 217 PropertyMatcher(T property, String description, BiPredicate<AccessibilityEvent, T> comparator)218 public PropertyMatcher(T property, String description, 219 BiPredicate<AccessibilityEvent, T> comparator) { 220 super(); 221 mProperty = property; 222 mDescription = description; 223 mComparator = comparator; 224 } 225 226 @Override matchesSafely(AccessibilityEvent event)227 protected boolean matchesSafely(AccessibilityEvent event) { 228 return mComparator.test(event, mProperty); 229 } 230 231 @Override describeTo(Description description)232 public void describeTo(Description description) { 233 description.appendText("Matching to " + mDescription + " " + mProperty.toString()); 234 } 235 } 236 237 public static class WindowTitleMatcher extends TypeSafeMatcher<AccessibilityEvent> { 238 private final UiAutomation mUiAutomation; 239 private final String mTitle; 240 private final int mDisplayId; 241 WindowTitleMatcher(@onNull UiAutomation uiAutomation, @NonNull String title, int displayId)242 public WindowTitleMatcher(@NonNull UiAutomation uiAutomation, @NonNull String title, 243 int displayId) { 244 super(); 245 mUiAutomation = uiAutomation; 246 mTitle = title; 247 mDisplayId = displayId; 248 } 249 250 @Override matchesSafely(AccessibilityEvent event)251 protected boolean matchesSafely(AccessibilityEvent event) { 252 final SparseArray<List<AccessibilityWindowInfo>> windowsOnAllDisplays = 253 mUiAutomation.getWindowsOnAllDisplays(); 254 final List<AccessibilityWindowInfo> windows = windowsOnAllDisplays.get(mDisplayId); 255 final int eventWindowId = event.getWindowId(); 256 for (AccessibilityWindowInfo info : windows) { 257 if (eventWindowId == info.getId() && mTitle.equals(info.getTitle())) { 258 return true; 259 } 260 } 261 return false; 262 } 263 264 @Override describeTo(Description description)265 public void describeTo(Description description) { 266 description.appendText("With window title " + mTitle); 267 } 268 } 269 270 public static class WindowIdMatcher extends TypeSafeMatcher<AccessibilityEvent> { 271 private int mWindowId; 272 WindowIdMatcher(int windowId)273 public WindowIdMatcher(int windowId) { 274 super(); 275 mWindowId = windowId; 276 } 277 278 @Override matchesSafely(AccessibilityEvent event)279 protected boolean matchesSafely(AccessibilityEvent event) { 280 return event.getWindowId() == mWindowId; 281 } 282 283 @Override describeTo(Description description)284 public void describeTo(Description description) { 285 description.appendText("With window Id " + mWindowId); 286 } 287 } 288 } 289