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.view;
18
19import android.os.Build;
20import android.os.Bundle;
21import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
22import android.support.v4.view.accessibility.AccessibilityNodeProviderCompat;
23import android.view.View;
24import android.view.ViewGroup;
25import android.view.accessibility.AccessibilityEvent;
26
27/**
28 * Helper for accessing {@link View.AccessibilityDelegate} introduced after
29 * API level 4 in a backwards compatible fashion.
30 * <p>
31 * <strong>Note:</strong> On platform versions prior to
32 * {@link android.os.Build.VERSION_CODES#M API 23}, delegate methods on
33 * views in the {@code android.widget.*} package are called <i>before</i>
34 * host methods. This prevents certain properties such as class name from
35 * being modified by overriding
36 * {@link AccessibilityDelegateCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat)},
37 * as any changes will be overwritten by the host class.
38 * <p>
39 * Starting in {@link android.os.Build.VERSION_CODES#M API 23}, delegate
40 * methods are called <i>after</i> host methods, which all properties to be
41 * modified without being overwritten by the host class.
42 */
43public class AccessibilityDelegateCompat {
44
45    static interface AccessibilityDelegateImpl {
46        public Object newAccessiblityDelegateDefaultImpl();
47        public Object newAccessiblityDelegateBridge(AccessibilityDelegateCompat listener);
48        public boolean dispatchPopulateAccessibilityEvent(Object delegate, View host,
49                AccessibilityEvent event);
50        public void onInitializeAccessibilityEvent(Object delegate, View host,
51                AccessibilityEvent event);
52        public void onInitializeAccessibilityNodeInfo(Object delegate, View host,
53                AccessibilityNodeInfoCompat info);
54        public void onPopulateAccessibilityEvent(Object delegate, View host,
55                AccessibilityEvent event);
56        public boolean onRequestSendAccessibilityEvent(Object delegate, ViewGroup host, View child,
57                AccessibilityEvent event);
58        public void sendAccessibilityEvent(Object delegate, View host, int eventType);
59        public void sendAccessibilityEventUnchecked(Object delegate, View host,
60                AccessibilityEvent event);
61        public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(Object delegate,
62                View host);
63        public boolean performAccessibilityAction(Object delegate, View host, int action,
64                Bundle args);
65    }
66
67    static class AccessibilityDelegateStubImpl implements AccessibilityDelegateImpl {
68        @Override
69        public Object newAccessiblityDelegateDefaultImpl() {
70            return null;
71        }
72
73        @Override
74        public Object newAccessiblityDelegateBridge(AccessibilityDelegateCompat listener) {
75            return null;
76        }
77
78        @Override
79        public boolean dispatchPopulateAccessibilityEvent(Object delegate, View host,
80                AccessibilityEvent event) {
81            return false;
82        }
83
84        @Override
85        public void onInitializeAccessibilityEvent(Object delegate, View host,
86                AccessibilityEvent event) {
87
88        }
89
90        @Override
91        public void onInitializeAccessibilityNodeInfo(Object delegate, View host,
92                AccessibilityNodeInfoCompat info) {
93
94        }
95
96        @Override
97        public void onPopulateAccessibilityEvent(Object delegate, View host,
98                AccessibilityEvent event) {
99
100        }
101
102        @Override
103        public boolean onRequestSendAccessibilityEvent(Object delegate, ViewGroup host, View child,
104                AccessibilityEvent event) {
105            return true;
106        }
107
108        @Override
109        public void sendAccessibilityEvent(Object delegate, View host, int eventType) {
110
111        }
112
113        @Override
114        public void sendAccessibilityEventUnchecked(Object delegate, View host,
115                AccessibilityEvent event) {
116
117        }
118
119        @Override
120        public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(Object delegate,
121                View host) {
122            return null;
123        }
124
125        @Override
126        public boolean performAccessibilityAction(Object delegate, View host, int action,
127                Bundle args) {
128            return false;
129        }
130    }
131
132    static class AccessibilityDelegateIcsImpl extends AccessibilityDelegateStubImpl {
133        @Override
134        public Object newAccessiblityDelegateDefaultImpl() {
135            return AccessibilityDelegateCompatIcs.newAccessibilityDelegateDefaultImpl();
136        }
137
138        @Override
139        public Object newAccessiblityDelegateBridge(final AccessibilityDelegateCompat compat) {
140            return AccessibilityDelegateCompatIcs.newAccessibilityDelegateBridge(
141                    new AccessibilityDelegateCompatIcs.AccessibilityDelegateBridge() {
142                @Override
143                public boolean dispatchPopulateAccessibilityEvent(View host,
144                        AccessibilityEvent event) {
145                    return compat.dispatchPopulateAccessibilityEvent(host, event);
146                }
147
148                @Override
149                public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
150                    compat.onInitializeAccessibilityEvent(host, event);
151                }
152
153                @Override
154                public void onInitializeAccessibilityNodeInfo(View host, Object info) {
155                    compat.onInitializeAccessibilityNodeInfo(host,
156                            new AccessibilityNodeInfoCompat(info));
157                }
158
159                @Override
160                public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
161                    compat.onPopulateAccessibilityEvent(host, event);
162                }
163
164                @Override
165                public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child,
166                        AccessibilityEvent event) {
167                    return compat.onRequestSendAccessibilityEvent(host, child, event);
168                }
169
170                @Override
171                public void sendAccessibilityEvent(View host, int eventType) {
172                    compat.sendAccessibilityEvent(host, eventType);
173                }
174
175                @Override
176                public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event) {
177                    compat.sendAccessibilityEventUnchecked(host, event);
178                }
179            });
180        }
181
182        @Override
183        public boolean dispatchPopulateAccessibilityEvent(Object delegate, View host,
184                AccessibilityEvent event) {
185            return AccessibilityDelegateCompatIcs.dispatchPopulateAccessibilityEvent(delegate,
186                    host, event);
187        }
188
189        @Override
190        public void onInitializeAccessibilityEvent(Object delegate, View host,
191                AccessibilityEvent event) {
192            AccessibilityDelegateCompatIcs.onInitializeAccessibilityEvent(delegate, host, event);
193        }
194
195        @Override
196        public void onInitializeAccessibilityNodeInfo(Object delegate, View host,
197                AccessibilityNodeInfoCompat info) {
198            AccessibilityDelegateCompatIcs.onInitializeAccessibilityNodeInfo(delegate, host,
199                    info.getInfo());
200        }
201
202        @Override
203        public void onPopulateAccessibilityEvent(Object delegate, View host,
204                AccessibilityEvent event) {
205            AccessibilityDelegateCompatIcs.onPopulateAccessibilityEvent(delegate, host, event);
206        }
207
208        @Override
209        public boolean onRequestSendAccessibilityEvent(Object delegate, ViewGroup host, View child,
210                AccessibilityEvent event) {
211            return AccessibilityDelegateCompatIcs.onRequestSendAccessibilityEvent(delegate, host,
212                    child, event);
213        }
214
215        @Override
216        public void sendAccessibilityEvent(Object delegate, View host, int eventType) {
217            AccessibilityDelegateCompatIcs.sendAccessibilityEvent(delegate, host, eventType);
218        }
219
220        @Override
221        public void sendAccessibilityEventUnchecked(Object delegate, View host,
222                AccessibilityEvent event) {
223            AccessibilityDelegateCompatIcs.sendAccessibilityEventUnchecked(delegate, host, event);
224        }
225    }
226
227    static class AccessibilityDelegateJellyBeanImpl extends AccessibilityDelegateIcsImpl {
228        @Override
229        public Object newAccessiblityDelegateBridge(final AccessibilityDelegateCompat compat) {
230            return AccessibilityDelegateCompatJellyBean.newAccessibilityDelegateBridge(
231                    new AccessibilityDelegateCompatJellyBean
232                            .AccessibilityDelegateBridgeJellyBean() {
233                @Override
234                public boolean dispatchPopulateAccessibilityEvent(View host,
235                        AccessibilityEvent event) {
236                    return compat.dispatchPopulateAccessibilityEvent(host, event);
237                }
238
239                @Override
240                public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
241                    compat.onInitializeAccessibilityEvent(host, event);
242                }
243
244                @Override
245                public void onInitializeAccessibilityNodeInfo(View host, Object info) {
246                    compat.onInitializeAccessibilityNodeInfo(host,
247                            new AccessibilityNodeInfoCompat(info));
248                }
249
250                @Override
251                public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
252                    compat.onPopulateAccessibilityEvent(host, event);
253                }
254
255                @Override
256                public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child,
257                        AccessibilityEvent event) {
258                    return compat.onRequestSendAccessibilityEvent(host, child, event);
259                }
260
261                @Override
262                public void sendAccessibilityEvent(View host, int eventType) {
263                    compat.sendAccessibilityEvent(host, eventType);
264                }
265
266                @Override
267                public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event) {
268                    compat.sendAccessibilityEventUnchecked(host, event);
269                }
270
271                @Override
272                public Object getAccessibilityNodeProvider(View host) {
273                    AccessibilityNodeProviderCompat provider =
274                        compat.getAccessibilityNodeProvider(host);
275                    return (provider != null) ? provider.getProvider() : null;
276                }
277
278                @Override
279                public boolean performAccessibilityAction(View host, int action, Bundle args) {
280                    return compat.performAccessibilityAction(host, action, args);
281                }
282            });
283        }
284
285        @Override
286        public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(Object delegate,
287                View host) {
288            Object provider = AccessibilityDelegateCompatJellyBean.getAccessibilityNodeProvider(
289                    delegate, host);
290            if (provider != null) {
291                return new AccessibilityNodeProviderCompat(provider);
292            }
293            return null;
294        }
295
296        @Override
297        public boolean performAccessibilityAction(Object delegate, View host, int action,
298                Bundle args) {
299            return AccessibilityDelegateCompatJellyBean.performAccessibilityAction(delegate,
300                    host, action, args);
301        }
302    }
303
304    private static final AccessibilityDelegateImpl IMPL;
305    private static final Object DEFAULT_DELEGATE;
306
307    static {
308        if (Build.VERSION.SDK_INT >= 16) { // JellyBean
309            IMPL = new AccessibilityDelegateJellyBeanImpl();
310        } else if (Build.VERSION.SDK_INT >= 14) { // ICS
311            IMPL = new AccessibilityDelegateIcsImpl();
312        } else {
313            IMPL = new AccessibilityDelegateStubImpl();
314        }
315        DEFAULT_DELEGATE = IMPL.newAccessiblityDelegateDefaultImpl();
316    }
317
318    final Object mBridge;
319
320    /**
321     * Creates a new instance.
322     */
323    public AccessibilityDelegateCompat() {
324        mBridge = IMPL.newAccessiblityDelegateBridge(this);
325    }
326
327    /**
328     * @return The wrapped bridge implementation.
329     */
330    Object getBridge() {
331        return mBridge;
332    }
333
334    /**
335     * Sends an accessibility event of the given type. If accessibility is not
336     * enabled this method has no effect.
337     * <p>
338     * The default implementation behaves as {@link View#sendAccessibilityEvent(int)
339     * View#sendAccessibilityEvent(int)} for the case of no accessibility delegate
340     * been set.
341     * </p>
342     *
343     * @param host The View hosting the delegate.
344     * @param eventType The type of the event to send.
345     *
346     * @see View#sendAccessibilityEvent(int) View#sendAccessibilityEvent(int)
347     */
348    public void sendAccessibilityEvent(View host, int eventType) {
349        IMPL.sendAccessibilityEvent(DEFAULT_DELEGATE, host, eventType);
350    }
351
352    /**
353     * Sends an accessibility event. This method behaves exactly as
354     * {@link #sendAccessibilityEvent(View, int)} but takes as an argument an
355     * empty {@link AccessibilityEvent} and does not perform a check whether
356     * accessibility is enabled.
357     * <p>
358     * The default implementation behaves as
359     * {@link View#sendAccessibilityEventUnchecked(AccessibilityEvent)
360     * View#sendAccessibilityEventUnchecked(AccessibilityEvent)} for
361     * the case of no accessibility delegate been set.
362     * </p>
363     *
364     * @param host The View hosting the delegate.
365     * @param event The event to send.
366     *
367     * @see View#sendAccessibilityEventUnchecked(AccessibilityEvent)
368     *      View#sendAccessibilityEventUnchecked(AccessibilityEvent)
369     */
370    public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event) {
371        IMPL.sendAccessibilityEventUnchecked(DEFAULT_DELEGATE, host, event);
372    }
373
374    /**
375     * Dispatches an {@link AccessibilityEvent} to the host {@link View} first and then
376     * to its children for adding their text content to the event.
377     * <p>
378     * The default implementation behaves as
379     * {@link View#dispatchPopulateAccessibilityEvent(AccessibilityEvent)
380     * View#dispatchPopulateAccessibilityEvent(AccessibilityEvent)} for
381     * the case of no accessibility delegate been set.
382     * </p>
383     *
384     * @param host The View hosting the delegate.
385     * @param event The event.
386     * @return True if the event population was completed.
387     *
388     * @see View#dispatchPopulateAccessibilityEvent(AccessibilityEvent)
389     *      View#dispatchPopulateAccessibilityEvent(AccessibilityEvent)
390     */
391    public boolean dispatchPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
392        return IMPL.dispatchPopulateAccessibilityEvent(DEFAULT_DELEGATE, host, event);
393    }
394
395    /**
396     * Gives a chance to the host View to populate the accessibility event with its
397     * text content.
398     * <p>
399     * The default implementation behaves as
400     * {@link ViewCompat#onPopulateAccessibilityEvent(View, AccessibilityEvent)
401     * ViewCompat#onPopulateAccessibilityEvent(AccessibilityEvent)} for
402     * the case of no accessibility delegate been set.
403     * </p>
404     *
405     * @param host The View hosting the delegate.
406     * @param event The accessibility event which to populate.
407     *
408     * @see ViewCompat#onPopulateAccessibilityEvent(View ,AccessibilityEvent)
409     *      ViewCompat#onPopulateAccessibilityEvent(View, AccessibilityEvent)
410     */
411    public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
412        IMPL.onPopulateAccessibilityEvent(DEFAULT_DELEGATE, host, event);
413    }
414
415    /**
416     * Initializes an {@link AccessibilityEvent} with information about the
417     * the host View which is the event source.
418     * <p>
419     * The default implementation behaves as
420     * {@link ViewCompat#onInitializeAccessibilityEvent(View v, AccessibilityEvent event)
421     * ViewCompat#onInitalizeAccessibilityEvent(View v, AccessibilityEvent event)} for
422     * the case of no accessibility delegate been set.
423     * </p>
424     *
425     * @param host The View hosting the delegate.
426     * @param event The event to initialize.
427     *
428     * @see ViewCompat#onInitializeAccessibilityEvent(View, AccessibilityEvent)
429     *      ViewCompat#onInitializeAccessibilityEvent(View, AccessibilityEvent)
430     */
431    public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
432        IMPL.onInitializeAccessibilityEvent(DEFAULT_DELEGATE, host, event);
433    }
434
435    /**
436     * Initializes an {@link AccessibilityNodeInfoCompat} with information about the host view.
437     * <p>
438     * The default implementation behaves as
439     * {@link ViewCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat)
440     * ViewCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat)} for
441     * the case of no accessibility delegate been set.
442     * </p>
443     *
444     * @param host The View hosting the delegate.
445     * @param info The instance to initialize.
446     *
447     * @see ViewCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat)
448     *      ViewCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat)
449     */
450    public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
451        IMPL.onInitializeAccessibilityNodeInfo(DEFAULT_DELEGATE, host, info);
452    }
453
454    /**
455     * Called when a child of the host View has requested sending an
456     * {@link AccessibilityEvent} and gives an opportunity to the parent (the host)
457     * to augment the event.
458     * <p>
459     * The default implementation behaves as
460     * {@link ViewGroupCompat#onRequestSendAccessibilityEvent(ViewGroup, View, AccessibilityEvent)
461     * ViewGroupCompat#onRequestSendAccessibilityEvent(ViewGroup, View, AccessibilityEvent)} for
462     * the case of no accessibility delegate been set.
463     * </p>
464     *
465     * @param host The View hosting the delegate.
466     * @param child The child which requests sending the event.
467     * @param event The event to be sent.
468     * @return True if the event should be sent
469     *
470     * @see ViewGroupCompat#onRequestSendAccessibilityEvent(ViewGroup, View, AccessibilityEvent)
471     *      ViewGroupCompat#onRequestSendAccessibilityEvent(ViewGroup, View, AccessibilityEvent)
472     */
473    public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child,
474            AccessibilityEvent event) {
475        return IMPL.onRequestSendAccessibilityEvent(DEFAULT_DELEGATE, host, child, event);
476    }
477
478    /**
479     * Gets the provider for managing a virtual view hierarchy rooted at this View
480     * and reported to {@link android.accessibilityservice.AccessibilityService}s
481     * that explore the window content.
482     * <p>
483     * The default implementation behaves as
484     * {@link ViewCompat#getAccessibilityNodeProvider(View) ViewCompat#getAccessibilityNodeProvider(View)}
485     * for the case of no accessibility delegate been set.
486     * </p>
487     *
488     * @return The provider.
489     *
490     * @see AccessibilityNodeProviderCompat
491     */
492    public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(View host) {
493        return IMPL.getAccessibilityNodeProvider(DEFAULT_DELEGATE, host);
494    }
495
496    /**
497     * Performs the specified accessibility action on the view. For
498     * possible accessibility actions look at {@link AccessibilityNodeInfoCompat}.
499     * <p>
500     * The default implementation behaves as
501     * {@link View#performAccessibilityAction(int, Bundle)
502     *  View#performAccessibilityAction(int, Bundle)} for the case of
503     *  no accessibility delegate been set.
504     * </p>
505     *
506     * @param action The action to perform.
507     * @return Whether the action was performed.
508     *
509     * @see View#performAccessibilityAction(int, Bundle)
510     *      View#performAccessibilityAction(int, Bundle)
511     */
512    public boolean performAccessibilityAction(View host, int action, Bundle args) {
513        return IMPL.performAccessibilityAction(DEFAULT_DELEGATE, host, action, args);
514    }
515}
516