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