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