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 public static class AccessibilityEventTypeMatcher extends TypeSafeMatcher<AccessibilityEvent> { 104 private int mType; 105 AccessibilityEventTypeMatcher(int type)106 public AccessibilityEventTypeMatcher(int type) { 107 super(); 108 mType = type; 109 } 110 111 @Override matchesSafely(AccessibilityEvent event)112 protected boolean matchesSafely(AccessibilityEvent event) { 113 return event.getEventType() == mType; 114 } 115 116 @Override describeTo(Description description)117 public void describeTo(Description description) { 118 description.appendText("Matching to type " + mType); 119 } 120 } 121 122 public static class WindowChangesMatcher extends TypeSafeMatcher<AccessibilityEvent> { 123 private int mWindowChanges; 124 WindowChangesMatcher(int windowChanges)125 public WindowChangesMatcher(int windowChanges) { 126 super(); 127 mWindowChanges = windowChanges; 128 } 129 130 @Override matchesSafely(AccessibilityEvent event)131 protected boolean matchesSafely(AccessibilityEvent event) { 132 return (event.getWindowChanges() & mWindowChanges) == mWindowChanges; 133 } 134 135 @Override describeTo(Description description)136 public void describeTo(Description description) { 137 description.appendText("With window change type " + mWindowChanges); 138 } 139 } 140 141 public static class ContentChangesMatcher extends TypeSafeMatcher<AccessibilityEvent> { 142 private int mContentChanges; 143 ContentChangesMatcher(int contentChanges)144 public ContentChangesMatcher(int contentChanges) { 145 super(); 146 mContentChanges = contentChanges; 147 } 148 149 @Override matchesSafely(AccessibilityEvent event)150 protected boolean matchesSafely(AccessibilityEvent event) { 151 return (event.getContentChangeTypes() & mContentChanges) == mContentChanges; 152 } 153 154 @Override describeTo(Description description)155 public void describeTo(Description description) { 156 description.appendText("With content change type " + mContentChanges); 157 } 158 } 159 160 public static class PropertyMatcher<T> extends TypeSafeMatcher<AccessibilityEvent> { 161 private T mProperty; 162 private String mDescription; 163 private BiPredicate<AccessibilityEvent, T> mComparator; 164 PropertyMatcher(T property, String description, BiPredicate<AccessibilityEvent, T> comparator)165 public PropertyMatcher(T property, String description, 166 BiPredicate<AccessibilityEvent, T> comparator) { 167 super(); 168 mProperty = property; 169 mDescription = description; 170 mComparator = comparator; 171 } 172 173 @Override matchesSafely(AccessibilityEvent event)174 protected boolean matchesSafely(AccessibilityEvent event) { 175 return mComparator.test(event, mProperty); 176 } 177 178 @Override describeTo(Description description)179 public void describeTo(Description description) { 180 description.appendText("Matching to " + mDescription + " " + mProperty.toString()); 181 } 182 } 183 184 public static class WindowTitleMatcher extends TypeSafeMatcher<AccessibilityEvent> { 185 private final UiAutomation mUiAutomation; 186 private final String mTitle; 187 WindowTitleMatcher(@onNull UiAutomation uiAutomation, @NonNull String title)188 public WindowTitleMatcher(@NonNull UiAutomation uiAutomation, @NonNull String title) { 189 super(); 190 mUiAutomation = uiAutomation; 191 mTitle = title; 192 } 193 194 @Override matchesSafely(AccessibilityEvent event)195 protected boolean matchesSafely(AccessibilityEvent event) { 196 final List<AccessibilityWindowInfo> windows = mUiAutomation.getWindows(); 197 final int eventWindowId = event.getWindowId(); 198 for (AccessibilityWindowInfo info : windows) { 199 if (eventWindowId == info.getId() && mTitle.equals(info.getTitle())) { 200 return true; 201 } 202 } 203 return false; 204 } 205 206 @Override describeTo(Description description)207 public void describeTo(Description description) { 208 description.appendText("With window title " + mTitle); 209 } 210 } 211 212 public static class WindowIdMatcher extends TypeSafeMatcher<AccessibilityEvent> { 213 private int mWindowId; 214 WindowIdMatcher(int windowId)215 public WindowIdMatcher(int windowId) { 216 super(); 217 mWindowId = windowId; 218 } 219 220 @Override matchesSafely(AccessibilityEvent event)221 protected boolean matchesSafely(AccessibilityEvent event) { 222 return event.getWindowId() == mWindowId; 223 } 224 225 @Override describeTo(Description description)226 public void describeTo(Description description) { 227 description.appendText("With window Id " + mWindowId); 228 } 229 } 230 } 231