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