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 final 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     * The accessibility service id.
284     * <p>
285     * <strong>Generated by the system.</strong>
286     * </p>
287     *
288     * @return The id.
289     */
290    public static String getId(AccessibilityServiceInfo info) {
291        return IMPL.getId(info);
292    }
293
294    /**
295     * The service {@link ResolveInfo}.
296     * <p>
297     * <strong>Generated by the system.</strong>
298     * </p>
299     *
300     * @return The info.
301     */
302    public static ResolveInfo getResolveInfo(AccessibilityServiceInfo info) {
303        return IMPL.getResolveInfo(info);
304    }
305
306    /**
307     * The settings activity name.
308     * <p>
309     * <strong>Statically set from {@link AccessibilityService#SERVICE_META_DATA
310     * meta-data}.</strong>
311     * </p>
312     *
313     * @return The settings activity name.
314     */
315    public static String getSettingsActivityName(AccessibilityServiceInfo info) {
316        return IMPL.getSettingsActivityName(info);
317    }
318
319    /**
320     * Whether this service can retrieve the current window's content.
321     * <p>
322     * <strong>Statically set from {@link AccessibilityService#SERVICE_META_DATA
323     * meta-data}.</strong>
324     * </p>
325     *
326     * @return True window content can be retrieved.
327     */
328    public static boolean getCanRetrieveWindowContent(AccessibilityServiceInfo info) {
329        return IMPL.getCanRetrieveWindowContent(info);
330    }
331
332    /**
333     * Description of the accessibility service.
334     * <p>
335     * <strong>Statically set from {@link AccessibilityService#SERVICE_META_DATA
336     * meta-data}.</strong>
337     * </p>
338     *
339     * @return The description.
340     */
341    public static String getDescription(AccessibilityServiceInfo info) {
342        return IMPL.getDescription(info);
343    }
344
345    /**
346     * Returns the string representation of a feedback type. For example,
347     * {@link AccessibilityServiceInfo#FEEDBACK_SPOKEN} is represented by the
348     * string FEEDBACK_SPOKEN.
349     *
350     * @param feedbackType The feedback type.
351     * @return The string representation.
352     */
353    public static String feedbackTypeToString(int feedbackType) {
354        StringBuilder builder = new StringBuilder();
355        builder.append("[");
356        while (feedbackType > 0) {
357            final int feedbackTypeFlag = 1 << Integer.numberOfTrailingZeros(feedbackType);
358            feedbackType &= ~feedbackTypeFlag;
359            if (builder.length() > 1) {
360                builder.append(", ");
361            }
362            switch (feedbackTypeFlag) {
363                case AccessibilityServiceInfo.FEEDBACK_AUDIBLE:
364                    builder.append("FEEDBACK_AUDIBLE");
365                    break;
366                case AccessibilityServiceInfo.FEEDBACK_HAPTIC:
367                    builder.append("FEEDBACK_HAPTIC");
368                    break;
369                case AccessibilityServiceInfo.FEEDBACK_GENERIC:
370                    builder.append("FEEDBACK_GENERIC");
371                    break;
372                case AccessibilityServiceInfo.FEEDBACK_SPOKEN:
373                    builder.append("FEEDBACK_SPOKEN");
374                    break;
375                case AccessibilityServiceInfo.FEEDBACK_VISUAL:
376                    builder.append("FEEDBACK_VISUAL");
377                    break;
378            }
379        }
380        builder.append("]");
381        return builder.toString();
382    }
383
384    /**
385     * Returns the string representation of a flag. For example,
386     * {@link AccessibilityServiceInfo#DEFAULT} is represented by the
387     * string DEFAULT.
388     *
389     * @param flag The flag.
390     * @return The string representation.
391     */
392    public static String flagToString(int flag) {
393        switch (flag) {
394            case DEFAULT:
395                return "DEFAULT";
396            case FLAG_INCLUDE_NOT_IMPORTANT_VIEWS:
397                return "FLAG_INCLUDE_NOT_IMPORTANT_VIEWS";
398            case FLAG_REQUEST_TOUCH_EXPLORATION_MODE:
399                return "FLAG_REQUEST_TOUCH_EXPLORATION_MODE";
400            case FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY:
401                return "FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY";
402            case FLAG_REPORT_VIEW_IDS:
403                return "FLAG_REPORT_VIEW_IDS";
404            case FLAG_REQUEST_FILTER_KEY_EVENTS:
405                return "FLAG_REQUEST_FILTER_KEY_EVENTS";
406            default:
407                return null;
408        }
409    }
410
411    /**
412     * Returns the bit mask of capabilities this accessibility service has such as
413     * being able to retrieve the active window content, etc.
414     *
415     * @param info The service info whose capabilities to get.
416     * @return The capability bit mask.
417     *
418     * @see #CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT
419     * @see #CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION
420     * @see #CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY
421     * @see #CAPABILITY_CAN_FILTER_KEY_EVENTS
422     */
423    public static int getCapabilities(AccessibilityServiceInfo info) {
424        return IMPL.getCapabilities(info);
425    }
426
427    /**
428     * Returns the string representation of a capability. For example,
429     * {@link #CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT} is represented
430     * by the string CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT.
431     *
432     * @param capability The capability.
433     * @return The string representation.
434     */
435    public static String capabilityToString(int capability) {
436        switch (capability) {
437            case CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT:
438                return "CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT";
439            case CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION:
440                return "CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION";
441            case CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY:
442                return "CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY";
443            case CAPABILITY_CAN_FILTER_KEY_EVENTS:
444                return "CAPABILITY_CAN_FILTER_KEY_EVENTS";
445            default:
446                return "UNKNOWN";
447        }
448    }
449}
450