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