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