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 
17 package android.support.v4.view;
18 
19 import android.os.Build;
20 import android.os.Bundle;
21 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
22 import android.support.v4.view.accessibility.AccessibilityNodeProviderCompat;
23 import android.view.View;
24 import android.view.ViewGroup;
25 import 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  */
31 public class AccessibilityDelegateCompat {
32 
33     static interface AccessibilityDelegateImpl {
newAccessiblityDelegateDefaultImpl()34         public Object newAccessiblityDelegateDefaultImpl();
newAccessiblityDelegateBridge(AccessibilityDelegateCompat listener)35         public Object newAccessiblityDelegateBridge(AccessibilityDelegateCompat listener);
dispatchPopulateAccessibilityEvent(Object delegate, View host, AccessibilityEvent event)36         public boolean dispatchPopulateAccessibilityEvent(Object delegate, View host,
37                 AccessibilityEvent event);
onInitializeAccessibilityEvent(Object delegate, View host, AccessibilityEvent event)38         public void onInitializeAccessibilityEvent(Object delegate, View host,
39                 AccessibilityEvent event);
onInitializeAccessibilityNodeInfo(Object delegate, View host, AccessibilityNodeInfoCompat info)40         public void onInitializeAccessibilityNodeInfo(Object delegate, View host,
41                 AccessibilityNodeInfoCompat info);
onPopulateAccessibilityEvent(Object delegate, View host, AccessibilityEvent event)42         public void onPopulateAccessibilityEvent(Object delegate, View host,
43                 AccessibilityEvent event);
onRequestSendAccessibilityEvent(Object delegate, ViewGroup host, View child, AccessibilityEvent event)44         public boolean onRequestSendAccessibilityEvent(Object delegate, ViewGroup host, View child,
45                 AccessibilityEvent event);
sendAccessibilityEvent(Object delegate, View host, int eventType)46         public void sendAccessibilityEvent(Object delegate, View host, int eventType);
sendAccessibilityEventUnchecked(Object delegate, View host, AccessibilityEvent event)47         public void sendAccessibilityEventUnchecked(Object delegate, View host,
48                 AccessibilityEvent event);
getAccessibilityNodeProvider(Object delegate, View host)49         public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(Object delegate,
50                 View host);
performAccessibilityAction(Object delegate, View host, int action, Bundle args)51         public boolean performAccessibilityAction(Object delegate, View host, int action,
52                 Bundle args);
53     }
54 
55     static class AccessibilityDelegateStubImpl implements AccessibilityDelegateImpl {
newAccessiblityDelegateDefaultImpl()56         public Object newAccessiblityDelegateDefaultImpl() {
57             return null;
58         }
59 
60         @Override
newAccessiblityDelegateBridge(AccessibilityDelegateCompat listener)61         public Object newAccessiblityDelegateBridge(AccessibilityDelegateCompat listener) {
62             return null;
63         }
64 
65         @Override
dispatchPopulateAccessibilityEvent(Object delegate, View host, AccessibilityEvent event)66         public boolean dispatchPopulateAccessibilityEvent(Object delegate, View host,
67                 AccessibilityEvent event) {
68             return false;
69         }
70 
71         @Override
onInitializeAccessibilityEvent(Object delegate, View host, AccessibilityEvent event)72         public void onInitializeAccessibilityEvent(Object delegate, View host,
73                 AccessibilityEvent event) {
74 
75         }
76 
77         @Override
onInitializeAccessibilityNodeInfo(Object delegate, View host, AccessibilityNodeInfoCompat info)78         public void onInitializeAccessibilityNodeInfo(Object delegate, View host,
79                 AccessibilityNodeInfoCompat info) {
80 
81         }
82 
83         @Override
onPopulateAccessibilityEvent(Object delegate, View host, AccessibilityEvent event)84         public void onPopulateAccessibilityEvent(Object delegate, View host,
85                 AccessibilityEvent event) {
86 
87         }
88 
89         @Override
onRequestSendAccessibilityEvent(Object delegate, ViewGroup host, View child, AccessibilityEvent event)90         public boolean onRequestSendAccessibilityEvent(Object delegate, ViewGroup host, View child,
91                 AccessibilityEvent event) {
92             return true;
93         }
94 
95         @Override
sendAccessibilityEvent(Object delegate, View host, int eventType)96         public void sendAccessibilityEvent(Object delegate, View host, int eventType) {
97 
98         }
99 
100         @Override
sendAccessibilityEventUnchecked(Object delegate, View host, AccessibilityEvent event)101         public void sendAccessibilityEventUnchecked(Object delegate, View host,
102                 AccessibilityEvent event) {
103 
104         }
105 
106         @Override
getAccessibilityNodeProvider(Object delegate, View host)107         public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(Object delegate,
108                 View host) {
109             return null;
110         }
111 
112         @Override
performAccessibilityAction(Object delegate, View host, int action, Bundle args)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
newAccessiblityDelegateDefaultImpl()121         public Object newAccessiblityDelegateDefaultImpl() {
122             return AccessibilityDelegateCompatIcs.newAccessibilityDelegateDefaultImpl();
123         }
124 
125         @Override
newAccessiblityDelegateBridge(final AccessibilityDelegateCompat compat)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
dispatchPopulateAccessibilityEvent(Object delegate, View host, AccessibilityEvent event)170         public boolean dispatchPopulateAccessibilityEvent(Object delegate, View host,
171                 AccessibilityEvent event) {
172             return AccessibilityDelegateCompatIcs.dispatchPopulateAccessibilityEvent(delegate,
173                     host, event);
174         }
175 
176         @Override
onInitializeAccessibilityEvent(Object delegate, View host, AccessibilityEvent event)177         public void onInitializeAccessibilityEvent(Object delegate, View host,
178                 AccessibilityEvent event) {
179             AccessibilityDelegateCompatIcs.onInitializeAccessibilityEvent(delegate, host, event);
180         }
181 
182         @Override
onInitializeAccessibilityNodeInfo(Object delegate, View host, AccessibilityNodeInfoCompat info)183         public void onInitializeAccessibilityNodeInfo(Object delegate, View host,
184                 AccessibilityNodeInfoCompat info) {
185             AccessibilityDelegateCompatIcs.onInitializeAccessibilityNodeInfo(delegate, host,
186                     info.getInfo());
187         }
188 
189         @Override
onPopulateAccessibilityEvent(Object delegate, View host, AccessibilityEvent event)190         public void onPopulateAccessibilityEvent(Object delegate, View host,
191                 AccessibilityEvent event) {
192             AccessibilityDelegateCompatIcs.onPopulateAccessibilityEvent(delegate, host, event);
193         }
194 
195         @Override
onRequestSendAccessibilityEvent(Object delegate, ViewGroup host, View child, AccessibilityEvent event)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
sendAccessibilityEvent(Object delegate, View host, int eventType)203         public void sendAccessibilityEvent(Object delegate, View host, int eventType) {
204             AccessibilityDelegateCompatIcs.sendAccessibilityEvent(delegate, host, eventType);
205         }
206 
207         @Override
sendAccessibilityEventUnchecked(Object delegate, View host, AccessibilityEvent event)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