ViewCompat.java revision 25121559b53b9f6c7ef7159203d42e11b9aee281
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.graphics.Rect; 20import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; 21import android.support.v4.view.accessibility.AccessibilityNodeProviderCompat; 22import android.view.View; 23import android.view.accessibility.AccessibilityEvent; 24 25/** 26 * Helper for accessing features in {@link View} introduced after API 27 * level 4 in a backwards compatible fashion. 28 */ 29public class ViewCompat { 30 /** 31 * Always allow a user to over-scroll this view, provided it is a 32 * view that can scroll. 33 */ 34 public static final int OVER_SCROLL_ALWAYS = 0; 35 36 /** 37 * Allow a user to over-scroll this view only if the content is large 38 * enough to meaningfully scroll, provided it is a view that can scroll. 39 */ 40 public static final int OVER_SCROLL_IF_CONTENT_SCROLLS = 1; 41 42 /** 43 * Never allow a user to over-scroll this view. 44 */ 45 public static final int OVER_SCROLL_NEVER = 2; 46 47 private static final long FAKE_FRAME_TIME = 10; 48 49 /** 50 * Automatically determine whether a view is important for accessibility. 51 */ 52 public static final int IMPORTANT_FOR_ACCESSIBILITY_AUTO = 0x00000000; 53 54 /** 55 * The view is important for accessibility. 56 */ 57 public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 0x00000001; 58 59 /** 60 * The view is not important for accessibility. 61 */ 62 public static final int IMPORTANT_FOR_ACCESSIBILITY_NO = 0x00000002; 63 64 interface ViewCompatImpl { 65 public boolean canScrollHorizontally(View v, int direction); 66 public boolean canScrollVertically(View v, int direction); 67 public int getOverScrollMode(View v); 68 public void setOverScrollMode(View v, int mode); 69 public void onInitializeAccessibilityEvent(View v, AccessibilityEvent event); 70 public void onPopulateAccessibilityEvent(View v, AccessibilityEvent event); 71 public void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfoCompat info); 72 public void setAccessibilityDelegate(View v, AccessibilityDelegateCompat delegate); 73 public boolean hasTransientState(View view); 74 public void setHasTransientState(View view, boolean hasTransientState); 75 public void postInvalidateOnAnimation(View view); 76 public void postInvalidateOnAnimation(View view, int left, int top, int right, int bottom); 77 public void postOnAnimation(View view, Runnable action); 78 public void postOnAnimationDelayed(View view, Runnable action, long delayMillis); 79 public int getImportantForAccessibility(View view); 80 public void setImportantForAccessibility(View view, int mode); 81 public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(View view); 82 } 83 84 static class BaseViewCompatImpl implements ViewCompatImpl { 85 public boolean canScrollHorizontally(View v, int direction) { 86 return false; 87 } 88 public boolean canScrollVertically(View v, int direction) { 89 return false; 90 } 91 public int getOverScrollMode(View v) { 92 return OVER_SCROLL_NEVER; 93 } 94 public void setOverScrollMode(View v, int mode) { 95 // Do nothing; API doesn't exist 96 } 97 public void setAccessibilityDelegate(View v, AccessibilityDelegateCompat delegate) { 98 // Do nothing; API doesn't exist 99 } 100 public void onPopulateAccessibilityEvent(View v, AccessibilityEvent event) { 101 // Do nothing; API doesn't exist 102 } 103 public void onInitializeAccessibilityEvent(View v, AccessibilityEvent event) { 104 // Do nothing; API doesn't exist 105 } 106 public void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfoCompat info) { 107 // Do nothing; API doesn't exist 108 } 109 public boolean hasTransientState(View view) { 110 // A view can't have transient state if transient state wasn't supported. 111 return false; 112 } 113 public void setHasTransientState(View view, boolean hasTransientState) { 114 // Do nothing; API doesn't exist 115 } 116 public void postInvalidateOnAnimation(View view) { 117 view.postInvalidateDelayed(getFrameTime()); 118 } 119 public void postInvalidateOnAnimation(View view, int left, int top, int right, int bottom) { 120 view.postInvalidateDelayed(getFrameTime(), left, top, right, bottom); 121 } 122 public void postOnAnimation(View view, Runnable action) { 123 view.postDelayed(action, getFrameTime()); 124 } 125 public void postOnAnimationDelayed(View view, Runnable action, long delayMillis) { 126 view.postDelayed(action, getFrameTime() + delayMillis); 127 } 128 long getFrameTime() { 129 return FAKE_FRAME_TIME; 130 } 131 public int getImportantForAccessibility(View view) { 132 return 0; 133 } 134 public void setImportantForAccessibility(View view, int mode) { 135 136 } 137 public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(View view) { 138 return null; 139 } 140 } 141 142 static class GBViewCompatImpl extends BaseViewCompatImpl { 143 @Override 144 public int getOverScrollMode(View v) { 145 return ViewCompatGingerbread.getOverScrollMode(v); 146 } 147 @Override 148 public void setOverScrollMode(View v, int mode) { 149 ViewCompatGingerbread.setOverScrollMode(v, mode); 150 } 151 } 152 153 static class HCViewCompatImpl extends GBViewCompatImpl { 154 long getFrameTime() { 155 return ViewCompatHC.getFrameTime(); 156 } 157 } 158 159 static class ICSViewCompatImpl extends HCViewCompatImpl { 160 @Override 161 public boolean canScrollHorizontally(View v, int direction) { 162 return ViewCompatICS.canScrollHorizontally(v, direction); 163 } 164 @Override 165 public boolean canScrollVertically(View v, int direction) { 166 return ViewCompatICS.canScrollVertically(v, direction); 167 } 168 @Override 169 public void onPopulateAccessibilityEvent(View v, AccessibilityEvent event) { 170 ViewCompatICS.onPopulateAccessibilityEvent(v, event); 171 } 172 @Override 173 public void onInitializeAccessibilityEvent(View v, AccessibilityEvent event) { 174 ViewCompatICS.onInitializeAccessibilityEvent(v, event); 175 } 176 @Override 177 public void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfoCompat info) { 178 ViewCompatICS.onInitializeAccessibilityNodeInfo(v, info.getInfo()); 179 } 180 @Override 181 public void setAccessibilityDelegate(View v, AccessibilityDelegateCompat delegate) { 182 ViewCompatICS.setAccessibilityDelegate(v, delegate.getBridge()); 183 } 184 } 185 186 static class JBViewCompatImpl extends ICSViewCompatImpl { 187 @Override 188 public boolean hasTransientState(View view) { 189 return ViewCompatJB.hasTransientState(view); 190 } 191 @Override 192 public void setHasTransientState(View view, boolean hasTransientState) { 193 ViewCompatJB.setHasTransientState(view, hasTransientState); 194 } 195 @Override 196 public void postInvalidateOnAnimation(View view) { 197 ViewCompatJB.postInvalidateOnAnimation(view); 198 } 199 @Override 200 public void postInvalidateOnAnimation(View view, int left, int top, int right, int bottom) { 201 ViewCompatJB.postInvalidateOnAnimation(view, left, top, right, bottom); 202 } 203 @Override 204 public void postOnAnimation(View view, Runnable action) { 205 ViewCompatJB.postOnAnimation(view, action); 206 } 207 @Override 208 public void postOnAnimationDelayed(View view, Runnable action, long delayMillis) { 209 ViewCompatJB.postOnAnimationDelayed(view, action, delayMillis); 210 } 211 @Override 212 public int getImportantForAccessibility(View view) { 213 return ViewCompatJB.getImportantForAccessibility(view); 214 } 215 @Override 216 public void setImportantForAccessibility(View view, int mode) { 217 ViewCompatJB.setImportantForAccessibility(view, mode); 218 } 219 @Override 220 public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(View view) { 221 Object compat = ViewCompatJB.getAccessibilityNodeProvider(view); 222 if (compat != null) { 223 return new AccessibilityNodeProviderCompat(compat); 224 } 225 return null; 226 } 227 } 228 229 static final ViewCompatImpl IMPL; 230 static { 231 final int version = android.os.Build.VERSION.SDK_INT; 232 if (version >= 16 || android.os.Build.VERSION.CODENAME.equals("JellyBean")) { 233 IMPL = new JBViewCompatImpl(); 234 } else if (version >= 14) { 235 IMPL = new ICSViewCompatImpl(); 236 } else if (version >= 11) { 237 IMPL = new HCViewCompatImpl(); 238 } else if (version >= 9) { 239 IMPL = new GBViewCompatImpl(); 240 } else { 241 IMPL = new BaseViewCompatImpl(); 242 } 243 } 244 245 /** 246 * Check if this view can be scrolled horizontally in a certain direction. 247 * 248 * @param v The View against which to invoke the method. 249 * @param direction Negative to check scrolling left, positive to check scrolling right. 250 * @return true if this view can be scrolled in the specified direction, false otherwise. 251 */ 252 public static boolean canScrollHorizontally(View v, int direction) { 253 return IMPL.canScrollHorizontally(v, direction); 254 } 255 256 /** 257 * Check if this view can be scrolled vertically in a certain direction. 258 * 259 * @param v The View against which to invoke the method. 260 * @param direction Negative to check scrolling up, positive to check scrolling down. 261 * @return true if this view can be scrolled in the specified direction, false otherwise. 262 */ 263 public static boolean canScrollVertically(View v, int direction) { 264 return IMPL.canScrollVertically(v, direction); 265 } 266 267 /** 268 * Returns the over-scroll mode for this view. The result will be 269 * one of {@link #OVER_SCROLL_ALWAYS} (default), {@link #OVER_SCROLL_IF_CONTENT_SCROLLS} 270 * (allow over-scrolling only if the view content is larger than the container), 271 * or {@link #OVER_SCROLL_NEVER}. 272 * 273 * @param v The View against which to invoke the method. 274 * @return This view's over-scroll mode. 275 */ 276 public static int getOverScrollMode(View v) { 277 return IMPL.getOverScrollMode(v); 278 } 279 280 /** 281 * Set the over-scroll mode for this view. Valid over-scroll modes are 282 * {@link #OVER_SCROLL_ALWAYS} (default), {@link #OVER_SCROLL_IF_CONTENT_SCROLLS} 283 * (allow over-scrolling only if the view content is larger than the container), 284 * or {@link #OVER_SCROLL_NEVER}. 285 * 286 * Setting the over-scroll mode of a view will have an effect only if the 287 * view is capable of scrolling. 288 * 289 * @param v The View against which to invoke the method. 290 * @param overScrollMode The new over-scroll mode for this view. 291 */ 292 public static void setOverScrollMode(View v, int overScrollMode) { 293 IMPL.setOverScrollMode(v, overScrollMode); 294 } 295 296 /** 297 * Called from {@link View#dispatchPopulateAccessibilityEvent(AccessibilityEvent)} 298 * giving a chance to this View to populate the accessibility event with its 299 * text content. While this method is free to modify event 300 * attributes other than text content, doing so should normally be performed in 301 * {@link View#onInitializeAccessibilityEvent(AccessibilityEvent)}. 302 * <p> 303 * Example: Adding formatted date string to an accessibility event in addition 304 * to the text added by the super implementation: 305 * <pre> public void onPopulateAccessibilityEvent(AccessibilityEvent event) { 306 * super.onPopulateAccessibilityEvent(event); 307 * final int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_WEEKDAY; 308 * String selectedDateUtterance = DateUtils.formatDateTime(mContext, 309 * mCurrentDate.getTimeInMillis(), flags); 310 * event.getText().add(selectedDateUtterance); 311 * }</pre> 312 * <p> 313 * If an {@link android.view.View.AccessibilityDelegate} has been specified via calling 314 * {@link View#setAccessibilityDelegate(android.view.View.AccessibilityDelegate)} its 315 * {@link android.view.View.AccessibilityDelegate#onPopulateAccessibilityEvent(View, 316 * AccessibilityEvent)} 317 * is responsible for handling this call. 318 * </p> 319 * <p class="note"><strong>Note:</strong> Always call the super implementation before adding 320 * information to the event, in case the default implementation has basic information to add. 321 * </p> 322 * 323 * @param v The View against which to invoke the method. 324 * @param event The accessibility event which to populate. 325 * 326 * @see View#sendAccessibilityEvent(int) 327 * @see View#dispatchPopulateAccessibilityEvent(AccessibilityEvent) 328 */ 329 public static void onPopulateAccessibilityEvent(View v, AccessibilityEvent event) { 330 IMPL.onPopulateAccessibilityEvent(v, event); 331 } 332 333 /** 334 * Initializes an {@link AccessibilityEvent} with information about 335 * this View which is the event source. In other words, the source of 336 * an accessibility event is the view whose state change triggered firing 337 * the event. 338 * <p> 339 * Example: Setting the password property of an event in addition 340 * to properties set by the super implementation: 341 * <pre> public void onInitializeAccessibilityEvent(AccessibilityEvent event) { 342 * super.onInitializeAccessibilityEvent(event); 343 * event.setPassword(true); 344 * }</pre> 345 * <p> 346 * If an {@link android.view.View.AccessibilityDelegate} has been specified via calling 347 * {@link View#setAccessibilityDelegate(android.view.View.AccessibilityDelegate)} its 348 * {@link android.view.View.AccessibilityDelegate#onInitializeAccessibilityEvent(View, 349 * AccessibilityEvent)} 350 * is responsible for handling this call. 351 * </p> 352 * <p class="note"><strong>Note:</strong> Always call the super implementation before adding 353 * information to the event, in case the default implementation has basic information to add. 354 * </p> 355 * 356 * @param v The View against which to invoke the method. 357 * @param event The event to initialize. 358 * 359 * @see View#sendAccessibilityEvent(int) 360 * @see View#dispatchPopulateAccessibilityEvent(AccessibilityEvent) 361 */ 362 public static void onInitializeAccessibilityEvent(View v, AccessibilityEvent event) { 363 IMPL.onInitializeAccessibilityEvent(v, event); 364 } 365 366 /** 367 * Initializes an {@link android.view.accessibility.AccessibilityNodeInfo} with information 368 * about this view. The base implementation sets: 369 * <ul> 370 * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setParent(View)},</li> 371 * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setBoundsInParent(Rect)},</li> 372 * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setBoundsInScreen(Rect)},</li> 373 * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setPackageName(CharSequence)},</li> 374 * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setClassName(CharSequence)},</li> 375 * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setContentDescription(CharSequence)},</li> 376 * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setEnabled(boolean)},</li> 377 * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setClickable(boolean)},</li> 378 * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setFocusable(boolean)},</li> 379 * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setFocused(boolean)},</li> 380 * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setLongClickable(boolean)},</li> 381 * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setSelected(boolean)},</li> 382 * </ul> 383 * <p> 384 * Subclasses should override this method, call the super implementation, 385 * and set additional attributes. 386 * </p> 387 * <p> 388 * If an {@link android.view.View.AccessibilityDelegate} has been specified via calling 389 * {@link View#setAccessibilityDelegate(android.view.View.AccessibilityDelegate)} its 390 * {@link android.view.View.AccessibilityDelegate#onInitializeAccessibilityNodeInfo(View, 391 * android.view.accessibility.AccessibilityNodeInfo)} 392 * is responsible for handling this call. 393 * </p> 394 * 395 * @param v The View against which to invoke the method. 396 * @param info The instance to initialize. 397 */ 398 public static void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfoCompat info) { 399 IMPL.onInitializeAccessibilityNodeInfo(v, info); 400 } 401 402 /** 403 * Sets a delegate for implementing accessibility support via compositon as 404 * opposed to inheritance. The delegate's primary use is for implementing 405 * backwards compatible widgets. For more details see 406 * {@link android.view.View.AccessibilityDelegate}. 407 * 408 * @param v The View against which to invoke the method. 409 * @param delegate The delegate instance. 410 * 411 * @see android.view.View.AccessibilityDelegate 412 */ 413 public static void setAccessibilityDelegate(View v, AccessibilityDelegateCompat delegate) { 414 IMPL.setAccessibilityDelegate(v, delegate); 415 } 416 417 /** 418 * Indicates whether the view is currently tracking transient state that the 419 * app should not need to concern itself with saving and restoring, but that 420 * the framework should take special note to preserve when possible. 421 * 422 * @param view View to check for transient state 423 * @return true if the view has transient state 424 */ 425 public static boolean hasTransientState(View view) { 426 return IMPL.hasTransientState(view); 427 } 428 429 /** 430 * Set whether this view is currently tracking transient state that the 431 * framework should attempt to preserve when possible. 432 * 433 * @param view View tracking transient state 434 * @param hasTransientState true if this view has transient state 435 */ 436 public static void setHasTransientState(View view, boolean hasTransientState) { 437 IMPL.setHasTransientState(view, hasTransientState); 438 } 439 440 /** 441 * <p>Cause an invalidate to happen on the next animation time step, typically the 442 * next display frame.</p> 443 * 444 * <p>This method can be invoked from outside of the UI thread 445 * only when this View is attached to a window.</p> 446 * 447 * @param view View to invalidate 448 */ 449 public static void postInvalidateOnAnimation(View view) { 450 IMPL.postInvalidateOnAnimation(view); 451 } 452 453 /** 454 * <p>Cause an invalidate of the specified area to happen on the next animation 455 * time step, typically the next display frame.</p> 456 * 457 * <p>This method can be invoked from outside of the UI thread 458 * only when this View is attached to a window.</p> 459 * 460 * @param view View to invalidate 461 * @param left The left coordinate of the rectangle to invalidate. 462 * @param top The top coordinate of the rectangle to invalidate. 463 * @param right The right coordinate of the rectangle to invalidate. 464 * @param bottom The bottom coordinate of the rectangle to invalidate. 465 */ 466 public static void postInvalidateOnAnimation(View view, int left, int top, 467 int right, int bottom) { 468 IMPL.postInvalidateOnAnimation(view, left, top, right, bottom); 469 } 470 471 /** 472 * <p>Causes the Runnable to execute on the next animation time step. 473 * The runnable will be run on the user interface thread.</p> 474 * 475 * <p>This method can be invoked from outside of the UI thread 476 * only when this View is attached to a window.</p> 477 * 478 * @param view View to post this Runnable to 479 * @param action The Runnable that will be executed. 480 */ 481 public static void postOnAnimation(View view, Runnable action) { 482 IMPL.postOnAnimation(view, action); 483 } 484 485 /** 486 * <p>Causes the Runnable to execute on the next animation time step, 487 * after the specified amount of time elapses. 488 * The runnable will be run on the user interface thread.</p> 489 * 490 * <p>This method can be invoked from outside of the UI thread 491 * only when this View is attached to a window.</p> 492 * 493 * @param view The view to post this Runnable to 494 * @param action The Runnable that will be executed. 495 * @param delayMillis The delay (in milliseconds) until the Runnable 496 * will be executed. 497 */ 498 public static void postOnAnimationDelayed(View view, Runnable action, long delayMillis) { 499 IMPL.postOnAnimationDelayed(view, action, delayMillis); 500 } 501 502 /** 503 * Gets the mode for determining whether this View is important for accessibility 504 * which is if it fires accessibility events and if it is reported to 505 * accessibility services that query the screen. 506 * 507 * @param view The view whose property to get. 508 * @return The mode for determining whether a View is important for accessibility. 509 * 510 * @see #IMPORTANT_FOR_ACCESSIBILITY_YES 511 * @see #IMPORTANT_FOR_ACCESSIBILITY_NO 512 * @see #IMPORTANT_FOR_ACCESSIBILITY_AUTO 513 */ 514 public static int getImportantForAccessibility(View view) { 515 return IMPL.getImportantForAccessibility(view); 516 } 517 518 /** 519 * Sets how to determine whether this view is important for accessibility 520 * which is if it fires accessibility events and if it is reported to 521 * accessibility services that query the screen. 522 * 523 * @param view The view whose property to set. 524 * @param mode How to determine whether this view is important for accessibility. 525 * 526 * @see #IMPORTANT_FOR_ACCESSIBILITY_YES 527 * @see #IMPORTANT_FOR_ACCESSIBILITY_NO 528 * @see #IMPORTANT_FOR_ACCESSIBILITY_AUTO 529 */ 530 public static void setImportantForAccessibility(View view, int mode) { 531 IMPL.setImportantForAccessibility(view, mode); 532 } 533 534 /** 535 * Gets the provider for managing a virtual view hierarchy rooted at this View 536 * and reported to {@link android.accessibilityservice.AccessibilityServiceCompat}s 537 * that explore the window content. 538 * <p> 539 * If this method returns an instance, this instance is responsible for managing 540 * {@link AccessibilityNodeInfoComapt}s describing the virtual sub-tree rooted at 541 * this View including the one representing the View itself. Similarly the returned 542 * instance is responsible for performing accessibility actions on any virtual 543 * view or the root view itself. 544 * </p> 545 * <p> 546 * If an {@link AccessibilityDelegateCompat} has been specified via calling 547 * {@link #setAccessibilityDelegate(AccessibilityDelegate)} its 548 * {@link AccessibilityDelegateCompat#getAccessibilityNodeProvider(View)} 549 * is responsible for handling this call. 550 * </p> 551 * 552 * @param view The view whose property to get. 553 * @return The provider. 554 * 555 * @see AccessibilityNodeProviderCompat 556 */ 557 public static AccessibilityNodeProviderCompat getAccessibilityNodeProvider(View view) { 558 return IMPL.getAccessibilityNodeProvider(view); 559 } 560} 561