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.view.accessibility;
18 
19 import android.accessibilityservice.AccessibilityService;
20 import android.annotation.Nullable;
21 import android.os.Bundle;
22 import android.view.View;
23 
24 import java.util.List;
25 
26 /**
27  * This class is the contract a client should implement to enable support of a
28  * virtual view hierarchy rooted at a given view for accessibility purposes. A virtual
29  * view hierarchy is a tree of imaginary Views that is reported as a part of the view
30  * hierarchy when an {@link AccessibilityService} explores the window content.
31  * Since the virtual View tree does not exist this class is responsible for
32  * managing the {@link AccessibilityNodeInfo}s describing that tree to accessibility
33  * services.
34  * </p>
35  * <p>
36  * The main use case of these APIs is to enable a custom view that draws complex content,
37  * for example a monthly calendar grid, to be presented as a tree of logical nodes,
38  * for example month days each containing events, thus conveying its logical structure.
39  * <p>
40  * <p>
41  * A typical use case is to override {@link View#getAccessibilityNodeProvider()} of the
42  * View that is a root of a virtual View hierarchy to return an instance of this class.
43  * In such a case this instance is responsible for managing {@link AccessibilityNodeInfo}s
44  * describing the virtual sub-tree rooted at the View including the one representing the
45  * View itself. Similarly the returned instance is responsible for performing accessibility
46  * actions on any virtual view or the root view itself. For example:
47  * </p>
48  * <div>
49  * <div class="ds-selector-tabs"><section><h3 id="kotlin">Kotlin</h3>
50  * <pre class="prettyprint lang-kotlin">
51  * // "view" is the View instance on which this class performs accessibility functions.
52  * class MyCalendarViewAccessibilityDelegate(
53  *       private var view: MyCalendarView) : AccessibilityDelegate() {
54  *     override fun getAccessibilityNodeProvider(host: View): AccessibilityNodeProvider {
55  *         return object : AccessibilityNodeProvider() {
56  *             override fun createAccessibilityNodeInfo(virtualViewId: Int):
57  *                     AccessibilityNodeInfo? {
58  *                 when (virtualViewId) {
59  *                     <var>host-view-id</var> -&gt; {
60  *                         val node = AccessibilityNodeInfo.obtain(view)
61  *                         node.addChild(view, <var>child-view-id</var>)
62  *                         // Set other attributes like screenReaderFocusable
63  *                         // and contentDescription.
64  *                         return node
65  *                     }
66  *                     <var>child-view-id</var> -&gt; {
67  *                         val node = AccessibilityNodeInfo
68  *                                 .obtain(view, virtualViewId)
69  *                         node.setParent(view)
70  *                         node.addAction(ACTION_SCROLL_UP)
71  *                         node.addAction(ACTION_SCROLL_DOWN)
72  *                         // Set other attributes like focusable and visibleToUser.
73  *                         node.setBoundsInScreen(
74  *                                 Rect(<var>coords-of-edges-relative-to-screen</var>))
75  *                         return node
76  *                     }
77  *                     else -&gt; return null
78  *                 }
79  *             }
80  *
81  *             override fun performAction(
82  *                 virtualViewId: Int,
83  *                 action: Int,
84  *                 arguments: Bundle
85  *             ): Boolean {
86  *                 if (virtualViewId == <var>host-view-id</var>) {
87  *                     return view.performAccessibilityAction(action, arguments)
88  *                 }
89  *                 when (action) {
90  *                     ACTION_SCROLL_UP.id -&gt; {
91  *                         // Implement logic in a separate method.
92  *                         navigateToPreviousMonth()
93  *
94  *                         return true
95  *                     }
96  *                     ACTION_SCROLL_DOWN.id -&gt;
97  *                         // Implement logic in a separate method.
98  *                         navigateToNextMonth()
99  *
100  *                         return true
101  *                     else -&gt; return false
102  *                 }
103  *             }
104  *         }
105  *     }
106  * }
107  * </pre>
108  * </section><section><h3 id="java">Java</h3>
109  * <pre class="prettyprint lang-java">
110  * final class MyCalendarViewAccessibilityDelegate extends AccessibilityDelegate {
111  *     // The View instance on which this class performs accessibility functions.
112  *     private final MyCalendarView view;
113  *
114  *     MyCalendarViewAccessibilityDelegate(MyCalendarView view) {
115  *         this.view = view;
116  *     }
117  *
118  *     &#64;Override
119  *     public AccessibilityNodeProvider getAccessibilityNodeProvider(View host) {
120  *         return new AccessibilityNodeProvider() {
121  *             &#64;Override
122  *             &#64;Nullable
123  *             public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) {
124  *                 if (virtualViewId == <var>host-view-id</var>) {
125  *                     AccessibilityNodeInfo node = AccessibilityNodeInfo.obtain(view);
126  *                     node.addChild(view, <var>child-view-id</var>);
127  *                     // Set other attributes like screenReaderFocusable and contentDescription.
128  *                     return node;
129  *                 } else if (virtualViewId == <var>child-view-id</var>) {
130  *                     AccessibilityNodeInfo node =
131  *                         AccessibilityNodeInfo.obtain(view, virtualViewId);
132  *                     node.setParent(view);
133  *                     node.addAction(ACTION_SCROLL_UP);
134  *                     node.addAction(ACTION_SCROLL_DOWN);
135  *                     // Set other attributes like focusable and visibleToUser.
136  *                     node.setBoundsInScreen(
137  *                         new Rect(<var>coordinates-of-edges-relative-to-screen</var>));
138  *                     return node;
139  *                 } else {
140  *                     return null;
141  *                 }
142  *             }
143  *
144  *             &#64;Override
145  *             public boolean performAction(int virtualViewId, int action, Bundle arguments) {
146  *                 if (virtualViewId == <var>host-view-id</var>) {
147  *                     return view.performAccessibilityAction(action, arguments);
148  *                 }
149  *
150  *                 if (action == ACTION_SCROLL_UP.getId()) {
151  *                     // Implement logic in a separate method.
152  *                     navigateToPreviousMonth();
153  *
154  *                     return true;
155  *                 } else if (action == ACTION_SCROLL_DOWN.getId()) {
156  *                     // Implement logic in a separate method.
157  *                     navigateToNextMonth();
158  *
159  *                     return true;
160  *                 } else {
161  *                     return false;
162  *                 }
163  *             }
164  *         };
165  *     }
166  * }
167  * </pre></section></div></div>
168  */
169 public abstract class AccessibilityNodeProvider {
170 
171     /**
172      * The virtual id for the hosting View.
173      */
174     public static final int HOST_VIEW_ID = -1;
175 
176     /**
177      * Returns an {@link AccessibilityNodeInfo} representing a virtual view,
178      * such as a descendant of the host View, with the given <code>virtualViewId</code>
179      * or the host View itself if <code>virtualViewId</code> equals to {@link #HOST_VIEW_ID}.
180      * <p>
181      * A virtual descendant is an imaginary View that is reported as a part of the view
182      * hierarchy for accessibility purposes. This enables custom views that draw complex
183      * content to report them selves as a tree of virtual views, thus conveying their
184      * logical structure.
185      * </p>
186      * <p>
187      * The implementer is responsible for obtaining an accessibility node info from the
188      * pool of reusable instances and setting the desired properties of the node info
189      * before returning it.
190      * </p>
191      *
192      * @param virtualViewId A client defined virtual view id.
193      * @return A populated {@link AccessibilityNodeInfo} for a virtual descendant or the
194      *     host View.
195      *
196      * @see View#createAccessibilityNodeInfo()
197      * @see AccessibilityNodeInfo
198      */
createAccessibilityNodeInfo(int virtualViewId)199     public @Nullable AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) {
200         return null;
201     }
202 
203     /**
204      * Adds extra data to an {@link AccessibilityNodeInfo} based on an explicit request for the
205      * additional data.
206      * <p>
207      * This method only needs to be implemented if a virtual view offers to provide additional
208      * data.
209      * </p>
210      *
211      * @param virtualViewId The virtual view id used to create the node
212      * @param info The info to which to add the extra data
213      * @param extraDataKey A key specifying the type of extra data to add to the info. The
214      *                     extra data should be added to the {@link Bundle} returned by
215      *                     the info's {@link AccessibilityNodeInfo#getExtras} method.
216      * @param arguments A {@link Bundle} holding any arguments relevant for this request.
217      *
218      * @see AccessibilityNodeInfo#setAvailableExtraData(List)
219      */
addExtraDataToAccessibilityNodeInfo( int virtualViewId, AccessibilityNodeInfo info, String extraDataKey, Bundle arguments)220     public void addExtraDataToAccessibilityNodeInfo(
221             int virtualViewId, AccessibilityNodeInfo info, String extraDataKey, Bundle arguments) {
222     }
223 
224     /**
225      * Performs an accessibility action on a virtual view, such as a descendant of the
226      * host View, with the given <code>virtualViewId</code> or the host View itself
227      * if <code>virtualViewId</code> equals to {@link #HOST_VIEW_ID}.
228      *
229      * @param virtualViewId A client defined virtual view id.
230      * @param action The action to perform.
231      * @param arguments Optional action arguments.
232      * @return True if the action was performed.
233      *
234      * @see View#performAccessibilityAction(int, Bundle)
235      * @see #createAccessibilityNodeInfo(int)
236      * @see AccessibilityNodeInfo
237      */
performAction(int virtualViewId, int action, @Nullable Bundle arguments)238     public boolean performAction(int virtualViewId, int action, @Nullable Bundle arguments) {
239         return false;
240     }
241 
242     /**
243      * Finds {@link AccessibilityNodeInfo}s by text. The match is case insensitive
244      * containment. The search is relative to the virtual view, i.e. a descendant of the
245      * host View, with the given <code>virtualViewId</code> or the host View itself
246      * <code>virtualViewId</code> equals to {@link #HOST_VIEW_ID}.
247      *
248      * @param virtualViewId A client defined virtual view id which defined
249      *     the root of the tree in which to perform the search.
250      * @param text The searched text.
251      * @return A list of node info.
252      *
253      * @see #createAccessibilityNodeInfo(int)
254      * @see AccessibilityNodeInfo
255      */
findAccessibilityNodeInfosByText(String text, int virtualViewId)256     public @Nullable List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String text,
257             int virtualViewId) {
258         return null;
259     }
260 
261     /**
262      * Find the virtual view, such as a descendant of the host View, that has the
263      * specified focus type.
264      *
265      * @param focus The focus to find. One of
266      *            {@link AccessibilityNodeInfo#FOCUS_INPUT} or
267      *            {@link AccessibilityNodeInfo#FOCUS_ACCESSIBILITY}.
268      * @return The node info of the focused view or null.
269      * @see AccessibilityNodeInfo#FOCUS_INPUT
270      * @see AccessibilityNodeInfo#FOCUS_ACCESSIBILITY
271      */
findFocus(int focus)272     public @Nullable AccessibilityNodeInfo findFocus(int focus) {
273         return null;
274     }
275 }
276