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.List;
31 import java.util.function.BiPredicate;
32 
33 /**
34  * Utility class for creating AccessibilityEventFilters
35  */
36 public class AccessibilityEventFilterUtils {
filterForEventType(int eventType)37     public static AccessibilityEventFilter filterForEventType(int eventType) {
38         return (new AccessibilityEventTypeMatcher(eventType))::matches;
39     }
40 
filterWindowContentChangedWithChangeTypes(int changes)41     public static AccessibilityEventFilter filterWindowContentChangedWithChangeTypes(int changes) {
42         return (both(new AccessibilityEventTypeMatcher(
43                 AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED)).and(
44                         new ContentChangesMatcher(changes)))::matches;
45     }
46 
filterWindowsChangedWithChangeTypes(int changes)47     public static AccessibilityEventFilter filterWindowsChangedWithChangeTypes(int changes) {
48         return (both(new AccessibilityEventTypeMatcher(AccessibilityEvent.TYPE_WINDOWS_CHANGED))
49                         .and(new WindowChangesMatcher(changes)))::matches;
50     }
51 
filterForEventTypeWithResource(int eventType, String ResourceName)52     public static AccessibilityEventFilter filterForEventTypeWithResource(int eventType,
53             String ResourceName) {
54         TypeSafeMatcher<AccessibilityEvent> matchResourceName = new PropertyMatcher<>(
55                 ResourceName, "Resource name",
56                 (event, expect) -> event.getSource() != null
57                         && event.getSource().getViewIdResourceName().equals(expect));
58         return (both(new AccessibilityEventTypeMatcher(eventType)).and(matchResourceName))::matches;
59     }
60 
filterForEventTypeWithAction(int eventType, int action)61     public static AccessibilityEventFilter filterForEventTypeWithAction(int eventType, int action) {
62         TypeSafeMatcher<AccessibilityEvent> matchAction =
63                 new PropertyMatcher<>(
64                         action, "Action", (event, expect) -> event.getAction() == action);
65         return (both(new AccessibilityEventTypeMatcher(eventType)).and(matchAction))::matches;
66     }
67 
filterWindowsChangeTypesAndWindowTitle( @onNull UiAutomation uiAutomation, int changeTypes, @NonNull String title)68     public static AccessibilityEventFilter filterWindowsChangeTypesAndWindowTitle(
69             @NonNull UiAutomation uiAutomation, int changeTypes, @NonNull String title) {
70         return allOf(new AccessibilityEventTypeMatcher(AccessibilityEvent.TYPE_WINDOWS_CHANGED),
71                 new WindowChangesMatcher(changeTypes),
72                 new WindowTitleMatcher(uiAutomation, title))::matches;
73     }
74 
filterWindowsChangTypesAndWindowId(int windowId, int changeTypes)75     public static AccessibilityEventFilter filterWindowsChangTypesAndWindowId(int windowId,
76             int changeTypes) {
77         return allOf(new AccessibilityEventTypeMatcher(AccessibilityEvent.TYPE_WINDOWS_CHANGED),
78                 new WindowChangesMatcher(changeTypes),
79                 new WindowIdMatcher(windowId))::matches;
80     }
81 
82     public static class AccessibilityEventTypeMatcher extends TypeSafeMatcher<AccessibilityEvent> {
83         private int mType;
84 
AccessibilityEventTypeMatcher(int type)85         public AccessibilityEventTypeMatcher(int type) {
86             super();
87             mType = type;
88         }
89 
90         @Override
matchesSafely(AccessibilityEvent event)91         protected boolean matchesSafely(AccessibilityEvent event) {
92             return event.getEventType() == mType;
93         }
94 
95         @Override
describeTo(Description description)96         public void describeTo(Description description) {
97             description.appendText("Matching to type " + mType);
98         }
99     }
100 
101     public static class WindowChangesMatcher extends TypeSafeMatcher<AccessibilityEvent> {
102         private int mWindowChanges;
103 
WindowChangesMatcher(int windowChanges)104         public WindowChangesMatcher(int windowChanges) {
105             super();
106             mWindowChanges = windowChanges;
107         }
108 
109         @Override
matchesSafely(AccessibilityEvent event)110         protected boolean matchesSafely(AccessibilityEvent event) {
111             return (event.getWindowChanges() & mWindowChanges) == mWindowChanges;
112         }
113 
114         @Override
describeTo(Description description)115         public void describeTo(Description description) {
116             description.appendText("With window change type " + mWindowChanges);
117         }
118     }
119 
120     public static class ContentChangesMatcher extends TypeSafeMatcher<AccessibilityEvent> {
121         private int mContentChanges;
122 
ContentChangesMatcher(int contentChanges)123         public ContentChangesMatcher(int contentChanges) {
124             super();
125             mContentChanges = contentChanges;
126         }
127 
128         @Override
matchesSafely(AccessibilityEvent event)129         protected boolean matchesSafely(AccessibilityEvent event) {
130             return (event.getContentChangeTypes() & mContentChanges) == mContentChanges;
131         }
132 
133         @Override
describeTo(Description description)134         public void describeTo(Description description) {
135             description.appendText("With content change type " + mContentChanges);
136         }
137     }
138 
139     public static class PropertyMatcher<T> extends TypeSafeMatcher<AccessibilityEvent> {
140         private T mProperty;
141         private String mDescription;
142         private BiPredicate<AccessibilityEvent, T> mComparator;
143 
PropertyMatcher(T property, String description, BiPredicate<AccessibilityEvent, T> comparator)144         public PropertyMatcher(T property, String description,
145                 BiPredicate<AccessibilityEvent, T> comparator) {
146             super();
147             mProperty = property;
148             mDescription = description;
149             mComparator = comparator;
150         }
151 
152         @Override
matchesSafely(AccessibilityEvent event)153         protected boolean matchesSafely(AccessibilityEvent event) {
154             return mComparator.test(event, mProperty);
155         }
156 
157         @Override
describeTo(Description description)158         public void describeTo(Description description) {
159             description.appendText("Matching to " + mDescription + " " + mProperty.toString());
160         }
161     }
162 
163     public static class WindowTitleMatcher extends TypeSafeMatcher<AccessibilityEvent> {
164         private final UiAutomation mUiAutomation;
165         private final String mTitle;
166 
WindowTitleMatcher(@onNull UiAutomation uiAutomation, @NonNull String title)167         public WindowTitleMatcher(@NonNull UiAutomation uiAutomation, @NonNull String title) {
168             super();
169             mUiAutomation = uiAutomation;
170             mTitle = title;
171         }
172 
173         @Override
matchesSafely(AccessibilityEvent event)174         protected boolean matchesSafely(AccessibilityEvent event) {
175             final List<AccessibilityWindowInfo> windows = mUiAutomation.getWindows();
176             final int eventWindowId = event.getWindowId();
177             for (AccessibilityWindowInfo info : windows) {
178                 if (eventWindowId == info.getId() && mTitle.equals(info.getTitle())) {
179                     return true;
180                 }
181             }
182             return false;
183         }
184 
185         @Override
describeTo(Description description)186         public void describeTo(Description description) {
187             description.appendText("With window title " + mTitle);
188         }
189     }
190 
191     public static class WindowIdMatcher extends TypeSafeMatcher<AccessibilityEvent> {
192         private int mWindowId;
193 
WindowIdMatcher(int windowId)194         public WindowIdMatcher(int windowId) {
195             super();
196             mWindowId = windowId;
197         }
198 
199         @Override
matchesSafely(AccessibilityEvent event)200         protected boolean matchesSafely(AccessibilityEvent event) {
201             return event.getWindowId() == mWindowId;
202         }
203 
204         @Override
describeTo(Description description)205         public void describeTo(Description description) {
206             description.appendText("With window Id " + mWindowId);
207         }
208     }
209 }
210