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