1 /*
2  * Copyright (C) 2012 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.accessibility;
18 
19 import android.os.Build;
20 import android.os.Bundle;
21 import android.support.annotation.Nullable;
22 import android.view.View;
23 
24 import java.util.ArrayList;
25 import java.util.List;
26 
27 /**
28  * Helper for accessing {@link android.view.accessibility.AccessibilityNodeProvider}
29  * introduced after API level 4 in a backwards compatible fashion.
30  */
31 public class AccessibilityNodeProviderCompat {
32 
33     interface AccessibilityNodeProviderImpl {
newAccessibilityNodeProviderBridge(AccessibilityNodeProviderCompat compat)34         Object newAccessibilityNodeProviderBridge(AccessibilityNodeProviderCompat compat);
35     }
36 
37     static class AccessibilityNodeProviderStubImpl implements AccessibilityNodeProviderImpl {
38         @Override
newAccessibilityNodeProviderBridge(AccessibilityNodeProviderCompat compat)39         public Object newAccessibilityNodeProviderBridge(AccessibilityNodeProviderCompat compat) {
40             return null;
41         }
42     }
43 
44     private static class AccessibilityNodeProviderJellyBeanImpl
45             extends AccessibilityNodeProviderStubImpl {
46         @Override
newAccessibilityNodeProviderBridge( final AccessibilityNodeProviderCompat compat)47         public Object newAccessibilityNodeProviderBridge(
48                 final AccessibilityNodeProviderCompat compat) {
49             return AccessibilityNodeProviderCompatJellyBean.newAccessibilityNodeProviderBridge(
50                     new AccessibilityNodeProviderCompatJellyBean.AccessibilityNodeInfoBridge() {
51                         @Override
52                         public boolean performAction(int virtualViewId, int action,
53                                 Bundle arguments) {
54                             return compat.performAction(virtualViewId, action, arguments);
55                         }
56 
57                         @Override
58                         public List<Object> findAccessibilityNodeInfosByText(
59                                             String text, int virtualViewId) {
60                             final List<AccessibilityNodeInfoCompat> compatInfos =
61                                 compat.findAccessibilityNodeInfosByText(text, virtualViewId);
62                             if (compatInfos == null) {
63                                 return null;
64                             } else {
65                                 final List<Object> infos = new ArrayList<>();
66                                 final int infoCount = compatInfos.size();
67                                 for (int i = 0; i < infoCount; i++) {
68                                     AccessibilityNodeInfoCompat infoCompat = compatInfos.get(i);
69                                     infos.add(infoCompat.getInfo());
70                                 }
71                                 return infos;
72                             }
73                         }
74 
75                         @Override
76                         public Object createAccessibilityNodeInfo(
77                                 int virtualViewId) {
78                             final AccessibilityNodeInfoCompat compatInfo =
79                                     compat.createAccessibilityNodeInfo(virtualViewId);
80                             if (compatInfo == null) {
81                                 return null;
82                             } else {
83                                 return compatInfo.getInfo();
84                             }
85                         }
86                     });
87         }
88     }
89 
90     private static class AccessibilityNodeProviderKitKatImpl
91             extends AccessibilityNodeProviderStubImpl {
92         @Override
93         public Object newAccessibilityNodeProviderBridge(
94                 final AccessibilityNodeProviderCompat compat) {
95             return AccessibilityNodeProviderCompatKitKat.newAccessibilityNodeProviderBridge(
96                     new AccessibilityNodeProviderCompatKitKat.AccessibilityNodeInfoBridge() {
97                         @Override
98                         public boolean performAction(
99                                 int virtualViewId, int action, Bundle arguments) {
100                             return compat.performAction(virtualViewId, action, arguments);
101                         }
102 
103                         @Override
104                         public List<Object> findAccessibilityNodeInfosByText(
105                                 String text, int virtualViewId) {
106                             final List<AccessibilityNodeInfoCompat> compatInfos =
107                                     compat.findAccessibilityNodeInfosByText(text, virtualViewId);
108                             if (compatInfos == null) {
109                                 return null;
110                             } else {
111                                 final List<Object> infos = new ArrayList<>();
112                                 final int infoCount = compatInfos.size();
113                                 for (int i = 0; i < infoCount; i++) {
114                                     AccessibilityNodeInfoCompat infoCompat = compatInfos.get(i);
115                                     infos.add(infoCompat.getInfo());
116                                 }
117                                 return infos;
118                             }
119                         }
120 
121                         @Override
122                         public Object createAccessibilityNodeInfo(int virtualViewId) {
123                             final AccessibilityNodeInfoCompat compatInfo =
124                                     compat.createAccessibilityNodeInfo(virtualViewId);
125                             if (compatInfo == null) {
126                                 return null;
127                             } else {
128                                 return compatInfo.getInfo();
129                             }
130                         }
131 
132                         @Override
133                         public Object findFocus(int focus) {
134                             final AccessibilityNodeInfoCompat compatInfo = compat.findFocus(focus);
135                             if (compatInfo == null) {
136                                 return null;
137                             } else {
138                                 return compatInfo.getInfo();
139                             }
140                         }
141                     });
142         }
143     }
144 
145     private static final AccessibilityNodeProviderImpl IMPL;
146 
147     private final Object mProvider;
148 
149     static {
150         if (Build.VERSION.SDK_INT >= 19) { // KitKat
151             IMPL = new AccessibilityNodeProviderKitKatImpl();
152         } else if (Build.VERSION.SDK_INT >= 16) { // JellyBean
153             IMPL = new AccessibilityNodeProviderJellyBeanImpl();
154         } else {
155             IMPL = new AccessibilityNodeProviderStubImpl();
156         }
157     }
158 
159     /**
160      * Creates a new instance.
161      */
162     public AccessibilityNodeProviderCompat() {
163         mProvider = IMPL.newAccessibilityNodeProviderBridge(this);
164     }
165 
166     /**
167      * Creates a new instance wrapping an
168      * {@link android.view.accessibility.AccessibilityNodeProvider}.
169      *
170      * @param provider The provider.
171      */
172     public AccessibilityNodeProviderCompat(Object provider) {
173         mProvider = provider;
174     }
175 
176     /**
177      * @return The wrapped {@link android.view.accessibility.AccessibilityNodeProvider}.
178      */
179     public Object getProvider() {
180         return mProvider;
181     }
182 
183     /**
184      * Returns an {@link AccessibilityNodeInfoCompat} representing a virtual view,
185      * i.e. a descendant of the host View, with the given <code>virtualViewId</code>
186      * or the host View itself if <code>virtualViewId</code> equals to {@link View#NO_ID}.
187      * <p>
188      * A virtual descendant is an imaginary View that is reported as a part of the view
189      * hierarchy for accessibility purposes. This enables custom views that draw complex
190      * content to report them selves as a tree of virtual views, thus conveying their
191      * logical structure.
192      * </p>
193      * <p>
194      * The implementer is responsible for obtaining an accessibility node info from the
195      * pool of reusable instances and setting the desired properties of the node info
196      * before returning it.
197      * </p>
198      *
199      * @param virtualViewId A client defined virtual view id.
200      * @return A populated {@link AccessibilityNodeInfoCompat} for a virtual descendant
201      *     or the host View.
202      *
203      * @see AccessibilityNodeInfoCompat
204      */
205     @Nullable
206     public AccessibilityNodeInfoCompat createAccessibilityNodeInfo(int virtualViewId) {
207         return null;
208     }
209 
210     /**
211      * Performs an accessibility action on a virtual view, i.e. a descendant of the
212      * host View, with the given <code>virtualViewId</code> or the host View itself
213      * if <code>virtualViewId</code> equals to {@link View#NO_ID}.
214      *
215      * @param virtualViewId A client defined virtual view id.
216      * @param action The action to perform.
217      * @param arguments Optional arguments.
218      * @return True if the action was performed.
219      *
220      * @see #createAccessibilityNodeInfo(int)
221      * @see AccessibilityNodeInfoCompat
222      */
223     public boolean performAction(int virtualViewId, int action, Bundle arguments) {
224         return false;
225     }
226 
227     /**
228      * Finds {@link AccessibilityNodeInfoCompat}s by text. The match is case insensitive
229      * containment. The search is relative to the virtual view, i.e. a descendant of the
230      * host View, with the given <code>virtualViewId</code> or the host View itself
231      * <code>virtualViewId</code> equals to {@link View#NO_ID}.
232      *
233      * @param virtualViewId A client defined virtual view id which defined
234      *     the root of the tree in which to perform the search.
235      * @param text The searched text.
236      * @return A list of node info.
237      *
238      * @see #createAccessibilityNodeInfo(int)
239      * @see AccessibilityNodeInfoCompat
240      */
241     @Nullable
242     public List<AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByText(String text,
243             int virtualViewId) {
244         return null;
245     }
246 
247     /**
248      * Find the virtual view, i.e. a descendant of the host View, that has the
249      * specified focus type.
250      *
251      * @param focus The focus to find. One of
252      *            {@link AccessibilityNodeInfoCompat#FOCUS_INPUT} or
253      *            {@link AccessibilityNodeInfoCompat#FOCUS_ACCESSIBILITY}.
254      * @return The node info of the focused view or null.
255      * @see AccessibilityNodeInfoCompat#FOCUS_INPUT
256      * @see AccessibilityNodeInfoCompat#FOCUS_ACCESSIBILITY
257      */
258     @Nullable
259     public AccessibilityNodeInfoCompat findFocus(int focus) {
260         return null;
261     }
262 }
263