1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.support.v4.accessibilityservice;
18
19import android.accessibilityservice.AccessibilityService;
20import android.accessibilityservice.AccessibilityServiceInfo;
21import android.content.pm.ResolveInfo;
22import android.os.Build;
23import android.view.View;
24
25/**
26 * Helper for accessing features in {@link android.accessibilityservice.AccessibilityService}
27 * introduced after API level 4 in a backwards compatible fashion.
28 */
29public class AccessibilityServiceInfoCompat {
30
31    static interface AccessibilityServiceInfoVersionImpl {
32        public String getId(AccessibilityServiceInfo info);
33        public ResolveInfo getResolveInfo(AccessibilityServiceInfo info);
34        public boolean getCanRetrieveWindowContent(AccessibilityServiceInfo info);
35        public String getDescription(AccessibilityServiceInfo info);
36        public String getSettingsActivityName(AccessibilityServiceInfo info);
37        public int getCapabilities(AccessibilityServiceInfo info);
38    }
39
40    static class AccessibilityServiceInfoStubImpl implements AccessibilityServiceInfoVersionImpl {
41
42        public boolean getCanRetrieveWindowContent(AccessibilityServiceInfo info) {
43            return false;
44        }
45
46        public String getDescription(AccessibilityServiceInfo info) {
47            return null;
48        }
49
50        public String getId(AccessibilityServiceInfo info) {
51            return null;
52        }
53
54        public ResolveInfo getResolveInfo(AccessibilityServiceInfo info) {
55            return null;
56        }
57
58        public String getSettingsActivityName(AccessibilityServiceInfo info) {
59            return null;
60        }
61
62        public int getCapabilities(AccessibilityServiceInfo info) {
63            return 0;
64        }
65    }
66
67    static class AccessibilityServiceInfoIcsImpl extends AccessibilityServiceInfoStubImpl {
68
69        @Override
70        public boolean getCanRetrieveWindowContent(AccessibilityServiceInfo info) {
71            return AccessibilityServiceInfoCompatIcs.getCanRetrieveWindowContent(info);
72        }
73
74        @Override
75        public String getDescription(AccessibilityServiceInfo info) {
76            return AccessibilityServiceInfoCompatIcs.getDescription(info);
77        }
78
79        @Override
80        public String getId(AccessibilityServiceInfo info) {
81            return AccessibilityServiceInfoCompatIcs.getId(info);
82        }
83
84        @Override
85        public ResolveInfo getResolveInfo(AccessibilityServiceInfo info) {
86            return AccessibilityServiceInfoCompatIcs.getResolveInfo(info);
87        }
88
89        @Override
90        public String getSettingsActivityName(AccessibilityServiceInfo info) {
91            return AccessibilityServiceInfoCompatIcs.getSettingsActivityName(info);
92        }
93
94        @Override
95        public int getCapabilities(AccessibilityServiceInfo info) {
96            if (getCanRetrieveWindowContent(info)) {
97                return CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT;
98            }
99            return 0;
100        }
101    }
102
103    static class AccessibilityServiceInfoJellyBeanMr2 extends AccessibilityServiceInfoIcsImpl {
104        @Override
105        public int getCapabilities(AccessibilityServiceInfo info) {
106            return AccessibilityServiceInfoCompatJellyBeanMr2.getCapabilities(info);
107        }
108    }
109
110    static {
111        if (Build.VERSION.SDK_INT >= 18) { // JellyBean MR2
112            IMPL = new AccessibilityServiceInfoJellyBeanMr2();
113        } else if (Build.VERSION.SDK_INT >= 14) { // ICS
114            IMPL = new AccessibilityServiceInfoIcsImpl();
115        } else {
116            IMPL = new AccessibilityServiceInfoStubImpl();
117        }
118    }
119
120    // Capabilities
121
122    private static final AccessibilityServiceInfoVersionImpl IMPL;
123
124    /**
125     * Capability: This accessibility service can retrieve the active window content.
126     */
127    public static final int CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT = 0x00000001;
128
129    /**
130     * Capability: This accessibility service can request touch exploration mode in which
131     * touched items are spoken aloud and the UI can be explored via gestures.
132     */
133    public static final int CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION = 0x00000002;
134
135    /**
136     * Capability: This accessibility service can request enhanced web accessibility
137     * enhancements. For example, installing scripts to make app content more accessible.
138     */
139    public static final int CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 0x00000004;
140
141    /**
142     * Capability: This accessibility service can filter the key event stream.
143     */
144    public static final int CAPABILITY_CAN_FILTER_KEY_EVENTS = 0x00000008;
145
146    // Feedback types
147
148    /**
149     * Denotes braille feedback.
150     */
151    public static final int FEEDBACK_BRAILLE = 0x0000020;
152
153    /**
154     * Mask for all feedback types.
155     *
156     * @see AccessibilityServiceInfo#FEEDBACK_SPOKEN
157     * @see AccessibilityServiceInfo#FEEDBACK_HAPTIC
158     * @see AccessibilityServiceInfo#FEEDBACK_AUDIBLE
159     * @see AccessibilityServiceInfo#FEEDBACK_VISUAL
160     * @see AccessibilityServiceInfo#FEEDBACK_GENERIC
161     * @see FEEDBACK_BRAILLE
162     */
163    public static final int FEEDBACK_ALL_MASK = 0xFFFFFFFF;
164
165    // Flags
166
167    /**
168     * If an {@link AccessibilityService} is the default for a given type.
169     * Default service is invoked only if no package specific one exists. In case of
170     * more than one package specific service only the earlier registered is notified.
171     */
172    public static final int DEFAULT = 0x0000001;
173
174    /**
175     * If this flag is set the system will regard views that are not important
176     * for accessibility in addition to the ones that are important for accessibility.
177     * That is, views that are marked as not important for accessibility via
178     * {@link View#IMPORTANT_FOR_ACCESSIBILITY_NO} or
179     * {@link View#IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS} and views that are
180     * marked as potentially important for accessibility via
181     * {@link View#IMPORTANT_FOR_ACCESSIBILITY_AUTO} for which the system has determined
182     * that are not important for accessibility, are both reported while querying the
183     * window content and also the accessibility service will receive accessibility events
184     * from them.
185     * <p>
186     * <strong>Note:</strong> For accessibility services targeting API version
187     * {@link Build.VERSION_CODES#JELLY_BEAN} or higher this flag has to be explicitly
188     * set for the system to regard views that are not important for accessibility. For
189     * accessibility services targeting API version lower than
190     * {@link Build.VERSION_CODES#JELLY_BEAN} this flag is ignored and all views are
191     * regarded for accessibility purposes.
192     * </p>
193     * <p>
194     * Usually views not important for accessibility are layout managers that do not
195     * react to user actions, do not draw any content, and do not have any special
196     * semantics in the context of the screen content. For example, a three by three
197     * grid can be implemented as three horizontal linear layouts and one vertical,
198     * or three vertical linear layouts and one horizontal, or one grid layout, etc.
199     * In this context the actual layout mangers used to achieve the grid configuration
200     * are not important, rather it is important that there are nine evenly distributed
201     * elements.
202     * </p>
203     */
204    public static final int FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 0x0000002;
205
206    /**
207     * This flag requests that the system gets into touch exploration mode.
208     * In this mode a single finger moving on the screen behaves as a mouse
209     * pointer hovering over the user interface. The system will also detect
210     * certain gestures performed on the touch screen and notify this service.
211     * The system will enable touch exploration mode if there is at least one
212     * accessibility service that has this flag set. Hence, clearing this
213     * flag does not guarantee that the device will not be in touch exploration
214     * mode since there may be another enabled service that requested it.
215     * <p>
216     * For accessibility services targeting API version higher than
217     * {@link Build.VERSION_CODES#JELLY_BEAN_MR1} that want to set
218     * this flag have to declare this capability in their meta-data by setting
219     * the attribute canRequestTouchExplorationMode to true, otherwise this flag
220     * will be ignored. For how to declare the meta-data of a service refer to
221     * {@value AccessibilityService#SERVICE_META_DATA}.
222     * </p>
223     * <p>
224     * Services targeting API version equal to or lower than
225     * {@link Build.VERSION_CODES#JELLY_BEAN_MR1} will work normally, i.e.
226     * the first time they are run, if this flag is specified, a dialog is
227     * shown to the user to confirm enabling explore by touch.
228     * </p>
229     */
230    public static final int FLAG_REQUEST_TOUCH_EXPLORATION_MODE = 0x0000004;
231
232    /**
233     * This flag requests from the system to enable web accessibility enhancing
234     * extensions. Such extensions aim to provide improved accessibility support
235     * for content presented in a {@link android.webkit.WebView}. An example of such
236     * an extension is injecting JavaScript from a secure source. The system will enable
237     * enhanced web accessibility if there is at least one accessibility service
238     * that has this flag set. Hence, clearing this flag does not guarantee that the
239     * device will not have enhanced web accessibility enabled since there may be
240     * another enabled service that requested it.
241     * <p>
242     * Services that want to set this flag have to declare this capability
243     * in their meta-data by setting the attribute canRequestEnhancedWebAccessibility
244     * to true, otherwise this flag will be ignored. For how to declare the meta-data
245     * of a service refer to {@value AccessibilityService#SERVICE_META_DATA}.
246     * </p>
247     */
248    public static final int FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 0x00000008;
249
250    /**
251     * This flag requests that the AccessibilityNodeInfos obtained
252     * by an {@link AccessibilityService} contain the id of the source view.
253     * The source view id will be a fully qualified resource name of the
254     * form "package:id/name", for example "foo.bar:id/my_list", and it is
255     * useful for UI test automation. This flag is not set by default.
256     */
257    public static final int FLAG_REPORT_VIEW_IDS = 0x00000010;
258
259    /**
260     * This flag requests from the system to filter key events. If this flag
261     * is set the accessibility service will receive the key events before
262     * applications allowing it implement global shortcuts. Setting this flag
263     * does not guarantee that this service will filter key events since only
264     * one service can do so at any given time. This avoids user confusion due
265     * to behavior change in case different key filtering services are enabled.
266     * If there is already another key filtering service enabled, this one will
267     * not receive key events.
268     * <p>
269     * Services that want to set this flag have to declare this capability
270     * in their meta-data by setting the attribute canRequestFilterKeyEvents
271     * to true, otherwise this flag will be ignored. For how to declare the meta
272     * -data of a service refer to {@value AccessibilityService#SERVICE_META_DATA}.
273     * </p>
274     */
275    public static final int FLAG_REQUEST_FILTER_KEY_EVENTS = 0x00000020;
276
277    /*
278     * Hide constructor
279     */
280    private AccessibilityServiceInfoCompat() {
281
282    }
283
284    /**
285     * The accessibility service id.
286     * <p>
287     * <strong>Generated by the system.</strong>
288     * </p>
289     *
290     * @return The id.
291     */
292    public static String getId(AccessibilityServiceInfo info) {
293        return IMPL.getId(info);
294    }
295
296    /**
297     * The service {@link ResolveInfo}.
298     * <p>
299     * <strong>Generated by the system.</strong>
300     * </p>
301     *
302     * @return The info.
303     */
304    public static ResolveInfo getResolveInfo(AccessibilityServiceInfo info) {
305        return IMPL.getResolveInfo(info);
306    }
307
308    /**
309     * The settings activity name.
310     * <p>
311     * <strong>Statically set from {@link AccessibilityService#SERVICE_META_DATA
312     * meta-data}.</strong>
313     * </p>
314     *
315     * @return The settings activity name.
316     */
317    public static String getSettingsActivityName(AccessibilityServiceInfo info) {
318        return IMPL.getSettingsActivityName(info);
319    }
320
321    /**
322     * Whether this service can retrieve the current window's content.
323     * <p>
324     * <strong>Statically set from {@link AccessibilityService#SERVICE_META_DATA
325     * meta-data}.</strong>
326     * </p>
327     *
328     * @return True window content can be retrieved.
329     */
330    public static boolean getCanRetrieveWindowContent(AccessibilityServiceInfo info) {
331        return IMPL.getCanRetrieveWindowContent(info);
332    }
333
334    /**
335     * Description of the accessibility service.
336     * <p>
337     * <strong>Statically set from {@link AccessibilityService#SERVICE_META_DATA
338     * meta-data}.</strong>
339     * </p>
340     *
341     * @return The description.
342     */
343    public static String getDescription(AccessibilityServiceInfo info) {
344        return IMPL.getDescription(info);
345    }
346
347    /**
348     * Returns the string representation of a feedback type. For example,
349     * {@link AccessibilityServiceInfo#FEEDBACK_SPOKEN} is represented by the
350     * string FEEDBACK_SPOKEN.
351     *
352     * @param feedbackType The feedback type.
353     * @return The string representation.
354     */
355    public static String feedbackTypeToString(int feedbackType) {
356        StringBuilder builder = new StringBuilder();
357        builder.append("[");
358        while (feedbackType > 0) {
359            final int feedbackTypeFlag = 1 << Integer.numberOfTrailingZeros(feedbackType);
360            feedbackType &= ~feedbackTypeFlag;
361            if (builder.length() > 1) {
362                builder.append(", ");
363            }
364            switch (feedbackTypeFlag) {
365                case AccessibilityServiceInfo.FEEDBACK_AUDIBLE:
366                    builder.append("FEEDBACK_AUDIBLE");
367                    break;
368                case AccessibilityServiceInfo.FEEDBACK_HAPTIC:
369                    builder.append("FEEDBACK_HAPTIC");
370                    break;
371                case AccessibilityServiceInfo.FEEDBACK_GENERIC:
372                    builder.append("FEEDBACK_GENERIC");
373                    break;
374                case AccessibilityServiceInfo.FEEDBACK_SPOKEN:
375                    builder.append("FEEDBACK_SPOKEN");
376                    break;
377                case AccessibilityServiceInfo.FEEDBACK_VISUAL:
378                    builder.append("FEEDBACK_VISUAL");
379                    break;
380            }
381        }
382        builder.append("]");
383        return builder.toString();
384    }
385
386    /**
387     * Returns the string representation of a flag. For example,
388     * {@link AccessibilityServiceInfo#DEFAULT} is represented by the
389     * string DEFAULT.
390     *
391     * @param flag The flag.
392     * @return The string representation.
393     */
394    public static String flagToString(int flag) {
395        switch (flag) {
396            case DEFAULT:
397                return "DEFAULT";
398            case FLAG_INCLUDE_NOT_IMPORTANT_VIEWS:
399                return "FLAG_INCLUDE_NOT_IMPORTANT_VIEWS";
400            case FLAG_REQUEST_TOUCH_EXPLORATION_MODE:
401                return "FLAG_REQUEST_TOUCH_EXPLORATION_MODE";
402            case FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY:
403                return "FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY";
404            case FLAG_REPORT_VIEW_IDS:
405                return "FLAG_REPORT_VIEW_IDS";
406            case FLAG_REQUEST_FILTER_KEY_EVENTS:
407                return "FLAG_REQUEST_FILTER_KEY_EVENTS";
408            default:
409                return null;
410        }
411    }
412
413    /**
414     * Returns the bit mask of capabilities this accessibility service has such as
415     * being able to retrieve the active window content, etc.
416     *
417     * @param info The service info whose capabilities to get.
418     * @return The capability bit mask.
419     *
420     * @see #CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT
421     * @see #CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION
422     * @see #CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY
423     * @see #CAPABILITY_CAN_FILTER_KEY_EVENTS
424     */
425    public static int getCapabilities(AccessibilityServiceInfo info) {
426        return IMPL.getCapabilities(info);
427    }
428
429    /**
430     * Returns the string representation of a capability. For example,
431     * {@link #CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT} is represented
432     * by the string CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT.
433     *
434     * @param capability The capability.
435     * @return The string representation.
436     */
437    public static String capabilityToString(int capability) {
438        switch (capability) {
439            case CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT:
440                return "CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT";
441            case CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION:
442                return "CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION";
443            case CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY:
444                return "CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY";
445            case CAPABILITY_CAN_FILTER_KEY_EVENTS:
446                return "CAPABILITY_CAN_FILTER_KEY_EVENTS";
447            default:
448                return "UNKNOWN";
449        }
450    }
451}
452