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 androidx.core.view.accessibility;
18 
19 import android.os.Build;
20 import android.os.Bundle;
21 import android.view.accessibility.AccessibilityNodeInfo;
22 import android.view.accessibility.AccessibilityNodeProvider;
23 
24 import androidx.annotation.RequiresApi;
25 
26 import org.jspecify.annotations.NonNull;
27 import org.jspecify.annotations.Nullable;
28 
29 import java.util.ArrayList;
30 import java.util.List;
31 
32 /**
33  * Helper for accessing {@link android.view.accessibility.AccessibilityNodeProvider}.
34  * <p>
35  * <aside class="note">
36  * <b>Note:</b> Consider using a {@link androidx.customview.widget.ExploreByTouchHelper}, a utility
37  * extension of AccessibilityNodeProvider, to simplify many aspects of providing information to
38  * accessibility services and managing accessibility focus. </aside>
39  */
40 public class AccessibilityNodeProviderCompat {
41 
42     static class AccessibilityNodeProviderApi19 extends AccessibilityNodeProvider {
43         final AccessibilityNodeProviderCompat mCompat;
44 
AccessibilityNodeProviderApi19(AccessibilityNodeProviderCompat compat)45         AccessibilityNodeProviderApi19(AccessibilityNodeProviderCompat compat) {
46             mCompat = compat;
47         }
48 
49         @Override
createAccessibilityNodeInfo(int virtualViewId)50         public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) {
51             final AccessibilityNodeInfoCompat compatInfo =
52                     mCompat.createAccessibilityNodeInfo(virtualViewId);
53             if (compatInfo == null) {
54                 return null;
55             } else {
56                 return compatInfo.unwrap();
57             }
58         }
59 
60         @Override
findAccessibilityNodeInfosByText( String text, int virtualViewId)61         public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(
62                 String text, int virtualViewId) {
63             final List<AccessibilityNodeInfoCompat> compatInfos =
64                     mCompat.findAccessibilityNodeInfosByText(text, virtualViewId);
65             if (compatInfos == null) {
66                 return null;
67             } else {
68                 final List<AccessibilityNodeInfo> infoList = new ArrayList<>();
69                 final int infoCount = compatInfos.size();
70                 for (int i = 0; i < infoCount; i++) {
71                     AccessibilityNodeInfoCompat infoCompat = compatInfos.get(i);
72                     infoList.add(infoCompat.unwrap());
73                 }
74                 return infoList;
75             }
76         }
77 
78         @Override
performAction(int virtualViewId, int action, Bundle arguments)79         public boolean performAction(int virtualViewId, int action, Bundle arguments) {
80             return mCompat.performAction(virtualViewId, action, arguments);
81         }
82 
83         @Override
findFocus(int focus)84         public AccessibilityNodeInfo findFocus(int focus) {
85             final AccessibilityNodeInfoCompat compatInfo = mCompat.findFocus(focus);
86             if (compatInfo == null) {
87                 return null;
88             } else {
89                 return compatInfo.unwrap();
90             }
91         }
92     }
93 
94     @RequiresApi(26)
95     static class AccessibilityNodeProviderApi26 extends AccessibilityNodeProviderApi19 {
AccessibilityNodeProviderApi26(AccessibilityNodeProviderCompat compat)96         AccessibilityNodeProviderApi26(AccessibilityNodeProviderCompat compat) {
97             super(compat);
98         }
99 
100         @Override
addExtraDataToAccessibilityNodeInfo(int virtualViewId, AccessibilityNodeInfo info, String extraDataKey, Bundle arguments)101         public void addExtraDataToAccessibilityNodeInfo(int virtualViewId,
102                 AccessibilityNodeInfo info, String extraDataKey, Bundle arguments) {
103             mCompat.addExtraDataToAccessibilityNodeInfo(virtualViewId,
104                     AccessibilityNodeInfoCompat.wrap(info), extraDataKey, arguments);
105         }
106     }
107 
108     /**
109      * The virtual id for the hosting View.
110      */
111     public static final int HOST_VIEW_ID = -1;
112 
113     private final @Nullable Object mProvider;
114 
115     /**
116      * Creates a new instance.
117      */
AccessibilityNodeProviderCompat()118     public AccessibilityNodeProviderCompat() {
119         if (Build.VERSION.SDK_INT >= 26) {
120             mProvider = new AccessibilityNodeProviderApi26(this);
121         } else {
122             mProvider = new AccessibilityNodeProviderApi19(this);
123         }
124     }
125 
126     /**
127      * Creates a new instance wrapping an
128      * {@link android.view.accessibility.AccessibilityNodeProvider}.
129      *
130      * @param provider The provider.
131      */
AccessibilityNodeProviderCompat(@ullable Object provider)132     public AccessibilityNodeProviderCompat(@Nullable Object provider) {
133         mProvider = provider;
134     }
135 
136     /**
137      * @return The wrapped {@link android.view.accessibility.AccessibilityNodeProvider}.
138      */
getProvider()139     public @Nullable Object getProvider() {
140         return mProvider;
141     }
142 
143     /**
144      * Returns an {@link AccessibilityNodeInfoCompat} representing a virtual view,
145      * i.e. a descendant of the host View, with the given <code>virtualViewId</code>
146      * or the host View itself if <code>virtualViewId</code> equals to {@link #HOST_VIEW_ID}.
147      * <p>
148      * A virtual descendant is an imaginary View that is reported as a part of the view
149      * hierarchy for accessibility purposes. This enables custom views that draw complex
150      * content to report them selves as a tree of virtual views, thus conveying their
151      * logical structure.
152      * </p>
153      * <p>
154      * The implementer is responsible for obtaining an accessibility node info from the
155      * pool of reusable instances and setting the desired properties of the node info
156      * before returning it.
157      * </p>
158      *
159      * @param virtualViewId A client defined virtual view id.
160      * @return A populated {@link AccessibilityNodeInfoCompat} for a virtual descendant
161      *     or the host View.
162      *
163      * @see AccessibilityNodeInfoCompat
164      */
createAccessibilityNodeInfo(int virtualViewId)165     public @Nullable AccessibilityNodeInfoCompat createAccessibilityNodeInfo(int virtualViewId) {
166         return null;
167     }
168 
169     /**
170      * Performs an accessibility action on a virtual view, i.e. a descendant of the
171      * host View, with the given <code>virtualViewId</code> or the host View itself
172      * if <code>virtualViewId</code> equals to {@link #HOST_VIEW_ID}.
173      *
174      * @param virtualViewId A client defined virtual view id.
175      * @param action The action to perform.
176      * @param arguments Optional arguments.
177      * @return True if the action was performed.
178      *
179      * @see #createAccessibilityNodeInfo(int)
180      * @see AccessibilityNodeInfoCompat
181      */
182     @SuppressWarnings("unused")
performAction(int virtualViewId, int action, @Nullable Bundle arguments)183     public boolean performAction(int virtualViewId, int action, @Nullable Bundle arguments) {
184         return false;
185     }
186 
187     /**
188      * Finds {@link AccessibilityNodeInfoCompat}s by text. The match is case insensitive
189      * containment. The search is relative to the virtual view, i.e. a descendant of the
190      * host View, with the given <code>virtualViewId</code> or the host View itself
191      * <code>virtualViewId</code> equals to {@link #HOST_VIEW_ID}.
192      *
193      * @param virtualViewId A client defined virtual view id which defined
194      *     the root of the tree in which to perform the search.
195      * @param text The searched text.
196      * @return A list of node info.
197      *
198      * @see #createAccessibilityNodeInfo(int)
199      * @see AccessibilityNodeInfoCompat
200      */
201     @SuppressWarnings("unused")
findAccessibilityNodeInfosByText( @onNull String text, int virtualViewId)202     public @Nullable List<AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByText(
203             @NonNull String text, int virtualViewId) {
204         return null;
205     }
206 
207     /**
208      * Find the virtual view, i.e. a descendant of the host View, that has the
209      * specified focus type.
210      *
211      * @param focus The focus to find. One of
212      *            {@link AccessibilityNodeInfoCompat#FOCUS_INPUT} or
213      *            {@link AccessibilityNodeInfoCompat#FOCUS_ACCESSIBILITY}.
214      * @return The node info of the focused view or null.
215      * @see AccessibilityNodeInfoCompat#FOCUS_INPUT
216      * @see AccessibilityNodeInfoCompat#FOCUS_ACCESSIBILITY
217      */
218     @SuppressWarnings("unused")
findFocus(int focus)219     public @Nullable AccessibilityNodeInfoCompat findFocus(int focus) {
220         return null;
221     }
222 
223     /**
224      * Adds extra data to an {@link AccessibilityNodeInfoCompat} based on an explicit request for
225      * the additional data.
226      * <p>
227      * This method only needs to be implemented if a virtual view offers to provide additional
228      * data.
229      * </p>
230      *
231      * @param virtualViewId The virtual view id used to create the node
232      * @param info The info to which to add the extra data
233      * @param extraDataKey A key specifying the type of extra data to add to the info. The
234      *                     extra data should be added to the {@link Bundle} returned by
235      *                     the info's {@link AccessibilityNodeInfoCompat#getExtras} method.
236      * @param arguments A {@link Bundle} holding any arguments relevant for this request.
237      *
238      * @see AccessibilityNodeInfo#setAvailableExtraData(List)
239      */
240     @SuppressWarnings("unused")
addExtraDataToAccessibilityNodeInfo(int virtualViewId, @NonNull AccessibilityNodeInfoCompat info, @NonNull String extraDataKey, @Nullable Bundle arguments)241     public void addExtraDataToAccessibilityNodeInfo(int virtualViewId,
242             @NonNull AccessibilityNodeInfoCompat info, @NonNull String extraDataKey,
243             @Nullable Bundle arguments) {
244     }
245 }
246