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 17package android.support.v4.view; 18 19import android.os.Build; 20import android.os.Bundle; 21import android.support.annotation.RequiresApi; 22import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; 23import android.support.v4.view.accessibility.AccessibilityNodeProviderCompat; 24import android.view.View; 25import android.view.View.AccessibilityDelegate; 26import android.view.ViewGroup; 27import android.view.accessibility.AccessibilityEvent; 28import android.view.accessibility.AccessibilityNodeInfo; 29import android.view.accessibility.AccessibilityNodeProvider; 30 31/** 32 * Helper for accessing {@link AccessibilityDelegate} introduced after 33 * API level 4 in a backwards compatible fashion. 34 * <p> 35 * <strong>Note:</strong> On platform versions prior to 36 * {@link android.os.Build.VERSION_CODES#M API 23}, delegate methods on 37 * views in the {@code android.widget.*} package are called <i>before</i> 38 * host methods. This prevents certain properties such as class name from 39 * being modified by overriding 40 * {@link AccessibilityDelegateCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat)}, 41 * as any changes will be overwritten by the host class. 42 * <p> 43 * Starting in {@link android.os.Build.VERSION_CODES#M API 23}, delegate 44 * methods are called <i>after</i> host methods, which all properties to be 45 * modified without being overwritten by the host class. 46 */ 47public class AccessibilityDelegateCompat { 48 49 static class AccessibilityDelegateBaseImpl { 50 public AccessibilityDelegate newAccessibilityDelegateBridge( 51 final AccessibilityDelegateCompat compat) { 52 return new AccessibilityDelegate() { 53 @Override 54 public boolean dispatchPopulateAccessibilityEvent(View host, 55 AccessibilityEvent event) { 56 return compat.dispatchPopulateAccessibilityEvent(host, event); 57 } 58 59 @Override 60 public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) { 61 compat.onInitializeAccessibilityEvent(host, event); 62 } 63 64 @Override 65 public void onInitializeAccessibilityNodeInfo( 66 View host, AccessibilityNodeInfo info) { 67 compat.onInitializeAccessibilityNodeInfo(host, 68 AccessibilityNodeInfoCompat.wrap(info)); 69 } 70 71 @Override 72 public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) { 73 compat.onPopulateAccessibilityEvent(host, event); 74 } 75 76 @Override 77 public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child, 78 AccessibilityEvent event) { 79 return compat.onRequestSendAccessibilityEvent(host, child, event); 80 } 81 82 @Override 83 public void sendAccessibilityEvent(View host, int eventType) { 84 compat.sendAccessibilityEvent(host, eventType); 85 } 86 87 @Override 88 public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event) { 89 compat.sendAccessibilityEventUnchecked(host, event); 90 } 91 }; 92 } 93 94 public AccessibilityNodeProviderCompat getAccessibilityNodeProvider( 95 AccessibilityDelegate delegate, View host) { 96 // Do nothing. Added in API 16. 97 return null; 98 } 99 100 public boolean performAccessibilityAction(AccessibilityDelegate delegate, View host, 101 int action, Bundle args) { 102 // Do nothing. Added in API 16. 103 return false; 104 } 105 } 106 107 @RequiresApi(16) 108 static class AccessibilityDelegateApi16Impl extends AccessibilityDelegateBaseImpl { 109 @Override 110 public AccessibilityDelegate newAccessibilityDelegateBridge( 111 final AccessibilityDelegateCompat compat) { 112 return new AccessibilityDelegate() { 113 @Override 114 public boolean dispatchPopulateAccessibilityEvent(View host, 115 AccessibilityEvent event) { 116 return compat.dispatchPopulateAccessibilityEvent(host, event); 117 } 118 119 @Override 120 public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) { 121 compat.onInitializeAccessibilityEvent(host, event); 122 } 123 124 @Override 125 public void onInitializeAccessibilityNodeInfo( 126 View host, AccessibilityNodeInfo info) { 127 compat.onInitializeAccessibilityNodeInfo(host, 128 AccessibilityNodeInfoCompat.wrap(info)); 129 } 130 131 @Override 132 public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) { 133 compat.onPopulateAccessibilityEvent(host, event); 134 } 135 136 @Override 137 public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child, 138 AccessibilityEvent event) { 139 return compat.onRequestSendAccessibilityEvent(host, child, event); 140 } 141 142 @Override 143 public void sendAccessibilityEvent(View host, int eventType) { 144 compat.sendAccessibilityEvent(host, eventType); 145 } 146 147 @Override 148 public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event) { 149 compat.sendAccessibilityEventUnchecked(host, event); 150 } 151 152 @Override 153 public AccessibilityNodeProvider getAccessibilityNodeProvider(View host) { 154 AccessibilityNodeProviderCompat provider = 155 compat.getAccessibilityNodeProvider(host); 156 return (provider != null) 157 ? (AccessibilityNodeProvider) provider.getProvider() : null; 158 } 159 160 @Override 161 public boolean performAccessibilityAction(View host, int action, Bundle args) { 162 return compat.performAccessibilityAction(host, action, args); 163 } 164 }; 165 } 166 167 @Override 168 public AccessibilityNodeProviderCompat getAccessibilityNodeProvider( 169 AccessibilityDelegate delegate, View host) { 170 Object provider = delegate.getAccessibilityNodeProvider(host); 171 if (provider != null) { 172 return new AccessibilityNodeProviderCompat(provider); 173 } 174 return null; 175 } 176 177 @Override 178 public boolean performAccessibilityAction(AccessibilityDelegate delegate, View host, 179 int action, Bundle args) { 180 return delegate.performAccessibilityAction(host, action, args); 181 } 182 } 183 184 private static final AccessibilityDelegateBaseImpl IMPL; 185 private static final AccessibilityDelegate DEFAULT_DELEGATE; 186 187 static { 188 if (Build.VERSION.SDK_INT >= 16) { // JellyBean 189 IMPL = new AccessibilityDelegateApi16Impl(); 190 } else { 191 IMPL = new AccessibilityDelegateBaseImpl(); 192 } 193 DEFAULT_DELEGATE = new AccessibilityDelegate(); 194 } 195 196 final AccessibilityDelegate mBridge; 197 198 /** 199 * Creates a new instance. 200 */ 201 public AccessibilityDelegateCompat() { 202 mBridge = IMPL.newAccessibilityDelegateBridge(this); 203 } 204 205 /** 206 * @return The wrapped bridge implementation. 207 */ 208 AccessibilityDelegate getBridge() { 209 return mBridge; 210 } 211 212 /** 213 * Sends an accessibility event of the given type. If accessibility is not 214 * enabled this method has no effect. 215 * <p> 216 * The default implementation behaves as {@link View#sendAccessibilityEvent(int) 217 * View#sendAccessibilityEvent(int)} for the case of no accessibility delegate 218 * been set. 219 * </p> 220 * 221 * @param host The View hosting the delegate. 222 * @param eventType The type of the event to send. 223 * 224 * @see View#sendAccessibilityEvent(int) View#sendAccessibilityEvent(int) 225 */ 226 public void sendAccessibilityEvent(View host, int eventType) { 227 DEFAULT_DELEGATE.sendAccessibilityEvent(host, eventType); 228 } 229 230 /** 231 * Sends an accessibility event. This method behaves exactly as 232 * {@link #sendAccessibilityEvent(View, int)} but takes as an argument an 233 * empty {@link AccessibilityEvent} and does not perform a check whether 234 * accessibility is enabled. 235 * <p> 236 * The default implementation behaves as 237 * {@link View#sendAccessibilityEventUnchecked(AccessibilityEvent) 238 * View#sendAccessibilityEventUnchecked(AccessibilityEvent)} for 239 * the case of no accessibility delegate been set. 240 * </p> 241 * 242 * @param host The View hosting the delegate. 243 * @param event The event to send. 244 * 245 * @see View#sendAccessibilityEventUnchecked(AccessibilityEvent) 246 * View#sendAccessibilityEventUnchecked(AccessibilityEvent) 247 */ 248 public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event) { 249 DEFAULT_DELEGATE.sendAccessibilityEventUnchecked(host, event); 250 } 251 252 /** 253 * Dispatches an {@link AccessibilityEvent} to the host {@link View} first and then 254 * to its children for adding their text content to the event. 255 * <p> 256 * The default implementation behaves as 257 * {@link View#dispatchPopulateAccessibilityEvent(AccessibilityEvent) 258 * View#dispatchPopulateAccessibilityEvent(AccessibilityEvent)} for 259 * the case of no accessibility delegate been set. 260 * </p> 261 * 262 * @param host The View hosting the delegate. 263 * @param event The event. 264 * @return True if the event population was completed. 265 * 266 * @see View#dispatchPopulateAccessibilityEvent(AccessibilityEvent) 267 * View#dispatchPopulateAccessibilityEvent(AccessibilityEvent) 268 */ 269 public boolean dispatchPopulateAccessibilityEvent(View host, AccessibilityEvent event) { 270 return DEFAULT_DELEGATE.dispatchPopulateAccessibilityEvent(host, event); 271 } 272 273 /** 274 * Gives a chance to the host View to populate the accessibility event with its 275 * text content. 276 * <p> 277 * The default implementation behaves as 278 * {@link ViewCompat#onPopulateAccessibilityEvent(View, AccessibilityEvent) 279 * ViewCompat#onPopulateAccessibilityEvent(AccessibilityEvent)} for 280 * the case of no accessibility delegate been set. 281 * </p> 282 * 283 * @param host The View hosting the delegate. 284 * @param event The accessibility event which to populate. 285 * 286 * @see ViewCompat#onPopulateAccessibilityEvent(View ,AccessibilityEvent) 287 * ViewCompat#onPopulateAccessibilityEvent(View, AccessibilityEvent) 288 */ 289 public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) { 290 DEFAULT_DELEGATE.onPopulateAccessibilityEvent(host, event); 291 } 292 293 /** 294 * Initializes an {@link AccessibilityEvent} with information about the 295 * the host View which is the event source. 296 * <p> 297 * The default implementation behaves as 298 * {@link ViewCompat#onInitializeAccessibilityEvent(View v, AccessibilityEvent event) 299 * ViewCompat#onInitalizeAccessibilityEvent(View v, AccessibilityEvent event)} for 300 * the case of no accessibility delegate been set. 301 * </p> 302 * 303 * @param host The View hosting the delegate. 304 * @param event The event to initialize. 305 * 306 * @see ViewCompat#onInitializeAccessibilityEvent(View, AccessibilityEvent) 307 * ViewCompat#onInitializeAccessibilityEvent(View, AccessibilityEvent) 308 */ 309 public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) { 310 DEFAULT_DELEGATE.onInitializeAccessibilityEvent(host, event); 311 } 312 313 /** 314 * Initializes an {@link AccessibilityNodeInfoCompat} with information about the host view. 315 * <p> 316 * The default implementation behaves as 317 * {@link ViewCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat) 318 * ViewCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat)} for 319 * the case of no accessibility delegate been set. 320 * </p> 321 * 322 * @param host The View hosting the delegate. 323 * @param info The instance to initialize. 324 * 325 * @see ViewCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat) 326 * ViewCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat) 327 */ 328 public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) { 329 DEFAULT_DELEGATE.onInitializeAccessibilityNodeInfo( 330 host, info.unwrap()); 331 } 332 333 /** 334 * Called when a child of the host View has requested sending an 335 * {@link AccessibilityEvent} and gives an opportunity to the parent (the host) 336 * to augment the event. 337 * <p> 338 * The default implementation behaves as 339 * {@link ViewGroupCompat#onRequestSendAccessibilityEvent(ViewGroup, View, AccessibilityEvent) 340 * ViewGroupCompat#onRequestSendAccessibilityEvent(ViewGroup, View, AccessibilityEvent)} for 341 * the case of no accessibility delegate been set. 342 * </p> 343 * 344 * @param host The View hosting the delegate. 345 * @param child The child which requests sending the event. 346 * @param event The event to be sent. 347 * @return True if the event should be sent 348 * 349 * @see ViewGroupCompat#onRequestSendAccessibilityEvent(ViewGroup, View, AccessibilityEvent) 350 * ViewGroupCompat#onRequestSendAccessibilityEvent(ViewGroup, View, AccessibilityEvent) 351 */ 352 public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child, 353 AccessibilityEvent event) { 354 return DEFAULT_DELEGATE.onRequestSendAccessibilityEvent(host, child, event); 355 } 356 357 /** 358 * Gets the provider for managing a virtual view hierarchy rooted at this View 359 * and reported to {@link android.accessibilityservice.AccessibilityService}s 360 * that explore the window content. 361 * <p> 362 * The default implementation behaves as 363 * {@link ViewCompat#getAccessibilityNodeProvider(View) ViewCompat#getAccessibilityNodeProvider(View)} 364 * for the case of no accessibility delegate been set. 365 * </p> 366 * 367 * @return The provider. 368 * 369 * @see AccessibilityNodeProviderCompat 370 */ 371 public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(View host) { 372 return IMPL.getAccessibilityNodeProvider(DEFAULT_DELEGATE, host); 373 } 374 375 /** 376 * Performs the specified accessibility action on the view. For 377 * possible accessibility actions look at {@link AccessibilityNodeInfoCompat}. 378 * <p> 379 * The default implementation behaves as 380 * {@link View#performAccessibilityAction(int, Bundle) 381 * View#performAccessibilityAction(int, Bundle)} for the case of 382 * no accessibility delegate been set. 383 * </p> 384 * 385 * @param action The action to perform. 386 * @return Whether the action was performed. 387 * 388 * @see View#performAccessibilityAction(int, Bundle) 389 * View#performAccessibilityAction(int, Bundle) 390 */ 391 public boolean performAccessibilityAction(View host, int action, Bundle args) { 392 return IMPL.performAccessibilityAction(DEFAULT_DELEGATE, host, action, args); 393 } 394} 395