1 /* 2 * Copyright 2018 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 androidx.core.view; 18 19 import static android.view.View.VISIBLE; 20 21 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX; 22 23 import android.annotation.SuppressLint; 24 import android.app.Activity; 25 import android.content.ClipData; 26 import android.content.ClipDescription; 27 import android.content.Context; 28 import android.content.ContextWrapper; 29 import android.content.res.ColorStateList; 30 import android.content.res.TypedArray; 31 import android.graphics.Matrix; 32 import android.graphics.Paint; 33 import android.graphics.PorterDuff; 34 import android.graphics.Rect; 35 import android.graphics.drawable.Drawable; 36 import android.os.Build; 37 import android.os.Bundle; 38 import android.text.TextUtils; 39 import android.util.AttributeSet; 40 import android.util.Log; 41 import android.util.SparseArray; 42 import android.view.ContentInfo; 43 import android.view.Display; 44 import android.view.KeyEvent; 45 import android.view.MotionEvent; 46 import android.view.PointerIcon; 47 import android.view.VelocityTracker; 48 import android.view.View; 49 import android.view.ViewConfiguration; 50 import android.view.ViewGroup; 51 import android.view.ViewParent; 52 import android.view.ViewTreeObserver; 53 import android.view.Window; 54 import android.view.WindowInsets; 55 import android.view.WindowInsetsController; 56 import android.view.accessibility.AccessibilityEvent; 57 import android.view.accessibility.AccessibilityManager; 58 import android.view.accessibility.AccessibilityNodeInfo; 59 import android.view.accessibility.AccessibilityNodeProvider; 60 import android.view.accessibility.AccessibilityRecord; 61 import android.view.autofill.AutofillId; 62 import android.view.contentcapture.ContentCaptureSession; 63 import android.view.inputmethod.InputConnection; 64 65 import androidx.annotation.FloatRange; 66 import androidx.annotation.IdRes; 67 import androidx.annotation.IntDef; 68 import androidx.annotation.Px; 69 import androidx.annotation.RequiresApi; 70 import androidx.annotation.RestrictTo; 71 import androidx.annotation.UiThread; 72 import androidx.annotation.VisibleForTesting; 73 import androidx.collection.SimpleArrayMap; 74 import androidx.core.R; 75 import androidx.core.util.Preconditions; 76 import androidx.core.view.AccessibilityDelegateCompat.AccessibilityDelegateAdapter; 77 import androidx.core.view.HapticFeedbackConstantsCompat.HapticFeedbackFlags; 78 import androidx.core.view.HapticFeedbackConstantsCompat.HapticFeedbackType; 79 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; 80 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat; 81 import androidx.core.view.accessibility.AccessibilityNodeProviderCompat; 82 import androidx.core.view.accessibility.AccessibilityViewCommand; 83 import androidx.core.view.autofill.AutofillIdCompat; 84 import androidx.core.view.contentcapture.ContentCaptureSessionCompat; 85 import androidx.core.viewtree.ViewTree; 86 87 import org.jspecify.annotations.NonNull; 88 import org.jspecify.annotations.Nullable; 89 90 import java.lang.annotation.Retention; 91 import java.lang.annotation.RetentionPolicy; 92 import java.lang.ref.WeakReference; 93 import java.lang.reflect.Field; 94 import java.lang.reflect.InvocationTargetException; 95 import java.lang.reflect.Method; 96 import java.util.ArrayList; 97 import java.util.Arrays; 98 import java.util.Collection; 99 import java.util.Collections; 100 import java.util.List; 101 import java.util.Map; 102 import java.util.WeakHashMap; 103 104 /** 105 * Helper for accessing features in {@link View}. 106 */ 107 @SuppressWarnings({"JavadocReference", "DeprecatedIsStillUsed", "JavaDoc", "RedundantSuppression"}) 108 // Unreliable warnings. 109 @SuppressLint("PrivateConstructorForUtilityClass") // deprecated non-private constructor 110 public class ViewCompat { 111 private static final String TAG = "ViewCompat"; 112 113 @RestrictTo(LIBRARY_GROUP_PREFIX) 114 @IntDef({View.FOCUS_LEFT, View.FOCUS_UP, View.FOCUS_RIGHT, View.FOCUS_DOWN, 115 View.FOCUS_FORWARD, View.FOCUS_BACKWARD}) 116 @Retention(RetentionPolicy.SOURCE) 117 public @interface FocusDirection {} 118 119 @RestrictTo(LIBRARY_GROUP_PREFIX) 120 @IntDef({View.FOCUS_LEFT, View.FOCUS_UP, View.FOCUS_RIGHT, View.FOCUS_DOWN}) 121 @Retention(RetentionPolicy.SOURCE) 122 public @interface FocusRealDirection {} 123 124 @RestrictTo(LIBRARY_GROUP_PREFIX) 125 @IntDef({View.FOCUS_FORWARD, View.FOCUS_BACKWARD}) 126 @Retention(RetentionPolicy.SOURCE) 127 public @interface FocusRelativeDirection {} 128 129 @SuppressWarnings("deprecation") 130 @IntDef({OVER_SCROLL_ALWAYS, OVER_SCROLL_IF_CONTENT_SCROLLS, OVER_SCROLL_NEVER}) 131 @Retention(RetentionPolicy.SOURCE) 132 private @interface OverScroll {} 133 134 /** 135 * Always allow a user to over-scroll this view, provided it is a 136 * view that can scroll. 137 * @deprecated Use {@link View#OVER_SCROLL_ALWAYS} directly. This constant will be removed in 138 * a future release. 139 */ 140 @Deprecated 141 public static final int OVER_SCROLL_ALWAYS = 0; 142 143 /** 144 * Allow a user to over-scroll this view only if the content is large 145 * enough to meaningfully scroll, provided it is a view that can scroll. 146 * @deprecated Use {@link View#OVER_SCROLL_IF_CONTENT_SCROLLS} directly. This constant will be 147 * removed in a future release. 148 */ 149 @Deprecated 150 public static final int OVER_SCROLL_IF_CONTENT_SCROLLS = 1; 151 152 /** 153 * Never allow a user to over-scroll this view. 154 * @deprecated Use {@link View#OVER_SCROLL_NEVER} directly. This constant will be removed in 155 * a future release. 156 */ 157 @Deprecated 158 public static final int OVER_SCROLL_NEVER = 2; 159 160 @RequiresApi(Build.VERSION_CODES.O) 161 @IntDef({ 162 View.IMPORTANT_FOR_AUTOFILL_AUTO, 163 View.IMPORTANT_FOR_AUTOFILL_YES, 164 View.IMPORTANT_FOR_AUTOFILL_NO, 165 View.IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS, 166 View.IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS 167 }) 168 @Retention(RetentionPolicy.SOURCE) 169 private @interface AutofillImportance {} 170 171 @SuppressWarnings("deprecation") 172 @IntDef({ 173 IMPORTANT_FOR_ACCESSIBILITY_AUTO, 174 IMPORTANT_FOR_ACCESSIBILITY_YES, 175 IMPORTANT_FOR_ACCESSIBILITY_NO, 176 IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS 177 }) 178 @Retention(RetentionPolicy.SOURCE) 179 private @interface ImportantForAccessibility {} 180 181 @IntDef({ 182 IMPORTANT_FOR_CONTENT_CAPTURE_AUTO, 183 IMPORTANT_FOR_CONTENT_CAPTURE_YES, 184 IMPORTANT_FOR_CONTENT_CAPTURE_NO, 185 IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS, 186 IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS, 187 }) 188 @Retention(RetentionPolicy.SOURCE) 189 private @interface ImportantForContentCapture {} 190 191 /** 192 * Automatically determine whether a view is important for content capture. 193 */ 194 public static final int IMPORTANT_FOR_CONTENT_CAPTURE_AUTO = 0x0; 195 196 /** 197 * The view is important for content capture, and its children (if any) will be traversed. 198 */ 199 public static final int IMPORTANT_FOR_CONTENT_CAPTURE_YES = 0x1; 200 201 /** 202 * The view is not important for content capture, but its children (if any) will be traversed. 203 */ 204 public static final int IMPORTANT_FOR_CONTENT_CAPTURE_NO = 0x2; 205 206 /** 207 * The view is important for content capture, but its children (if any) will not be traversed. 208 */ 209 public static final int IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS = 0x4; 210 211 /** 212 * The view is not important for content capture, and its children (if any) will not be 213 * traversed. 214 */ 215 public static final int IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS = 0x8; 216 217 /** 218 * Automatically determine whether a view is important for accessibility. 219 * 220 * @deprecated Use {@link View#IMPORTANT_FOR_ACCESSIBILITY_AUTO} directly. 221 */ 222 @Deprecated 223 public static final int IMPORTANT_FOR_ACCESSIBILITY_AUTO = 0x00000000; 224 225 /** 226 * The view is important for accessibility. 227 * 228 * @deprecated Use {@link View#IMPORTANT_FOR_ACCESSIBILITY_YES} directly. 229 */ 230 @Deprecated 231 public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 0x00000001; 232 233 /** 234 * The view is not important for accessibility. 235 * 236 * @deprecated Use {@link View#IMPORTANT_FOR_ACCESSIBILITY_NO} directly. 237 */ 238 @Deprecated 239 public static final int IMPORTANT_FOR_ACCESSIBILITY_NO = 0x00000002; 240 241 /** 242 * The view is not important for accessibility, nor are any of its 243 * descendant views. 244 * 245 * @deprecated Use {@link View#IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS} directly. 246 */ 247 @Deprecated 248 public static final int IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS = 0x00000004; 249 250 @IntDef({ 251 ACCESSIBILITY_LIVE_REGION_NONE, 252 ACCESSIBILITY_LIVE_REGION_POLITE, 253 ACCESSIBILITY_LIVE_REGION_ASSERTIVE 254 }) 255 @Retention(RetentionPolicy.SOURCE) 256 private @interface AccessibilityLiveRegion {} 257 258 /** 259 * Live region mode specifying that accessibility services should not 260 * automatically announce changes to this view. This is the default live 261 * region mode for most views. 262 * <p> 263 * Use with {@link ViewCompat#setAccessibilityLiveRegion(View, int)}. 264 */ 265 public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0x00000000; 266 267 /** 268 * Live region mode specifying that accessibility services should announce 269 * changes to this view. 270 * <p> 271 * Use with {@link ViewCompat#setAccessibilityLiveRegion(View, int)}. 272 */ 273 public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 0x00000001; 274 275 /** 276 * Live region mode specifying that accessibility services should interrupt 277 * ongoing speech to immediately announce changes to this view. 278 * <p> 279 * Use with {@link ViewCompat#setAccessibilityLiveRegion(View, int)}. 280 */ 281 public static final int ACCESSIBILITY_LIVE_REGION_ASSERTIVE = 0x00000002; 282 283 @IntDef({View.LAYER_TYPE_NONE, View.LAYER_TYPE_SOFTWARE, View.LAYER_TYPE_HARDWARE}) 284 @Retention(RetentionPolicy.SOURCE) 285 private @interface LayerType {} 286 287 /** 288 * Indicates that the view does not have a layer. 289 * 290 * @deprecated Use {@link View#LAYER_TYPE_NONE} directly. 291 */ 292 @Deprecated 293 public static final int LAYER_TYPE_NONE = 0; 294 295 /** 296 * <p>Indicates that the view has a software layer. A software layer is backed 297 * by a bitmap and causes the view to be rendered using Android's software 298 * rendering pipeline, even if hardware acceleration is enabled.</p> 299 * 300 * <p>Software layers have various usages:</p> 301 * <p>When the application is not using hardware acceleration, a software layer 302 * is useful to apply a specific color filter and/or blending mode and/or 303 * translucency to a view and all its children.</p> 304 * <p>When the application is using hardware acceleration, a software layer 305 * is useful to render drawing primitives not supported by the hardware 306 * accelerated pipeline. It can also be used to cache a complex view tree 307 * into a texture and reduce the complexity of drawing operations. For instance, 308 * when animating a complex view tree with a translation, a software layer can 309 * be used to render the view tree only once.</p> 310 * <p>Software layers should be avoided when the affected view tree updates 311 * often. Every update will require to re-render the software layer, which can 312 * potentially be slow (particularly when hardware acceleration is turned on 313 * since the layer will have to be uploaded into a hardware texture after every 314 * update.)</p> 315 * 316 * @deprecated Use {@link View#LAYER_TYPE_SOFTWARE} directly. 317 */ 318 @Deprecated 319 public static final int LAYER_TYPE_SOFTWARE = 1; 320 321 /** 322 * <p>Indicates that the view has a hardware layer. A hardware layer is backed 323 * by a hardware specific texture (generally Frame Buffer Objects or FBO on 324 * OpenGL hardware) and causes the view to be rendered using Android's hardware 325 * rendering pipeline, but only if hardware acceleration is turned on for the 326 * view hierarchy. When hardware acceleration is turned off, hardware layers 327 * behave exactly as {@link View#LAYER_TYPE_SOFTWARE software layers}.</p> 328 * 329 * <p>A hardware layer is useful to apply a specific color filter and/or 330 * blending mode and/or translucency to a view and all its children.</p> 331 * <p>A hardware layer can be used to cache a complex view tree into a 332 * texture and reduce the complexity of drawing operations. For instance, 333 * when animating a complex view tree with a translation, a hardware layer can 334 * be used to render the view tree only once.</p> 335 * <p>A hardware layer can also be used to increase the rendering quality when 336 * rotation transformations are applied on a view. It can also be used to 337 * prevent potential clipping issues when applying 3D transforms on a view.</p> 338 * 339 * @deprecated Use {@link View#LAYER_TYPE_HARDWARE} directly. 340 */ 341 @Deprecated 342 public static final int LAYER_TYPE_HARDWARE = 2; 343 344 @IntDef({ 345 LAYOUT_DIRECTION_LTR, 346 LAYOUT_DIRECTION_RTL, 347 LAYOUT_DIRECTION_INHERIT, 348 LAYOUT_DIRECTION_LOCALE}) 349 @Retention(RetentionPolicy.SOURCE) 350 private @interface LayoutDirectionMode {} 351 352 @IntDef({ 353 LAYOUT_DIRECTION_LTR, 354 LAYOUT_DIRECTION_RTL 355 }) 356 @Retention(RetentionPolicy.SOURCE) 357 private @interface ResolvedLayoutDirectionMode {} 358 359 /** 360 * Horizontal layout direction of this view is from Left to Right. 361 * 362 * @deprecated Use {@link View#LAYOUT_DIRECTION_LTR} directly. 363 */ 364 @Deprecated 365 public static final int LAYOUT_DIRECTION_LTR = 0; 366 367 /** 368 * Horizontal layout direction of this view is from Right to Left. 369 * 370 * @deprecated Use {@link View#LAYOUT_DIRECTION_RTL} directly. 371 */ 372 @Deprecated 373 public static final int LAYOUT_DIRECTION_RTL = 1; 374 375 /** 376 * Horizontal layout direction of this view is inherited from its parent. 377 * Use with {@link #setLayoutDirection}. 378 * 379 * @deprecated Use {@link View#LAYOUT_DIRECTION_INHERIT} directly. 380 */ 381 @Deprecated 382 public static final int LAYOUT_DIRECTION_INHERIT = 2; 383 384 /** 385 * Horizontal layout direction of this view is from deduced from the default language 386 * script for the locale. Use with {@link #setLayoutDirection}. 387 * 388 * @deprecated Use {@link View#LAYOUT_DIRECTION_LOCALE} directly. 389 */ 390 @Deprecated 391 public static final int LAYOUT_DIRECTION_LOCALE = 3; 392 393 /** 394 * Bits of {@link #getMeasuredWidthAndState} and 395 * {@link #getMeasuredWidthAndState} that provide the actual measured size. 396 * 397 * @deprecated Use {@link View#MEASURED_SIZE_MASK} directly. 398 */ 399 @Deprecated 400 public static final int MEASURED_SIZE_MASK = 0x00ffffff; 401 402 /** 403 * Bits of {@link #getMeasuredWidthAndState} and 404 * {@link #getMeasuredWidthAndState} that provide the additional state bits. 405 * 406 * @deprecated Use {@link View#MEASURED_STATE_MASK} directly. 407 */ 408 @Deprecated 409 public static final int MEASURED_STATE_MASK = 0xff000000; 410 411 /** 412 * Bit shift of {@link #MEASURED_STATE_MASK} to get to the height bits 413 * for functions that combine both width and height into a single int, 414 * such as {@link #getMeasuredState} and the childState argument of 415 * {@link #resolveSizeAndState(int, int, int)}. 416 * 417 * @deprecated Use {@link View#MEASURED_HEIGHT_STATE_SHIFT} directly. 418 */ 419 @Deprecated 420 public static final int MEASURED_HEIGHT_STATE_SHIFT = 16; 421 422 /** 423 * Bit of {@link #getMeasuredWidthAndState} and 424 * {@link #getMeasuredWidthAndState} that indicates the measured size 425 * is smaller that the space the view would like to have. 426 * 427 * @deprecated Use {@link View#MEASURED_STATE_TOO_SMALL} directly. 428 */ 429 @Deprecated 430 public static final int MEASURED_STATE_TOO_SMALL = 0x01000000; 431 432 /** 433 */ 434 @IntDef(value = {SCROLL_AXIS_NONE, SCROLL_AXIS_HORIZONTAL, SCROLL_AXIS_VERTICAL}, flag = true) 435 @Retention(RetentionPolicy.SOURCE) 436 @RestrictTo(LIBRARY_GROUP_PREFIX) 437 public @interface ScrollAxis {} 438 439 /** 440 * Indicates no axis of view scrolling. 441 */ 442 public static final int SCROLL_AXIS_NONE = 0; 443 444 /** 445 * Indicates scrolling along the horizontal axis. 446 */ 447 public static final int SCROLL_AXIS_HORIZONTAL = 1; 448 449 /** 450 * Indicates scrolling along the vertical axis. 451 */ 452 public static final int SCROLL_AXIS_VERTICAL = 1 << 1; 453 454 /** 455 */ 456 @IntDef({TYPE_TOUCH, TYPE_NON_TOUCH}) 457 @Retention(RetentionPolicy.SOURCE) 458 @RestrictTo(LIBRARY_GROUP_PREFIX) 459 public @interface NestedScrollType {} 460 461 /** 462 * Indicates that the input type for the gesture is from a user touching the screen. 463 */ 464 public static final int TYPE_TOUCH = 0; 465 466 /** 467 * Indicates that the input type for the gesture is caused by something which is not a user 468 * touching a screen. This is usually from a fling which is settling. 469 */ 470 public static final int TYPE_NON_TOUCH = 1; 471 472 @RestrictTo(LIBRARY_GROUP_PREFIX) 473 @Retention(RetentionPolicy.SOURCE) 474 @IntDef(flag = true, 475 value = { 476 SCROLL_INDICATOR_TOP, 477 SCROLL_INDICATOR_BOTTOM, 478 SCROLL_INDICATOR_LEFT, 479 SCROLL_INDICATOR_RIGHT, 480 SCROLL_INDICATOR_START, 481 SCROLL_INDICATOR_END, 482 }) 483 public @interface ScrollIndicators {} 484 485 /** 486 * Scroll indicator direction for the top edge of the view. 487 * 488 * @see #setScrollIndicators(View, int) 489 * @see #setScrollIndicators(View, int, int) 490 * @see #getScrollIndicators(View) 491 */ 492 public static final int SCROLL_INDICATOR_TOP = 0x1; 493 494 /** 495 * Scroll indicator direction for the bottom edge of the view. 496 * 497 * @see #setScrollIndicators(View, int) 498 * @see #setScrollIndicators(View, int, int) 499 * @see #getScrollIndicators(View) 500 */ 501 public static final int SCROLL_INDICATOR_BOTTOM = 0x2; 502 503 /** 504 * Scroll indicator direction for the left edge of the view. 505 * 506 * @see #setScrollIndicators(View, int) 507 * @see #setScrollIndicators(View, int, int) 508 * @see #getScrollIndicators(View) 509 */ 510 public static final int SCROLL_INDICATOR_LEFT = 0x4; 511 512 /** 513 * Scroll indicator direction for the right edge of the view. 514 * 515 * @see #setScrollIndicators(View, int) 516 * @see #setScrollIndicators(View, int, int) 517 * @see #getScrollIndicators(View) 518 */ 519 public static final int SCROLL_INDICATOR_RIGHT = 0x8; 520 521 /** 522 * Scroll indicator direction for the starting edge of the view. 523 * 524 * @see #setScrollIndicators(View, int) 525 * @see #setScrollIndicators(View, int, int) 526 * @see #getScrollIndicators(View) 527 */ 528 public static final int SCROLL_INDICATOR_START = 0x10; 529 530 /** 531 * Scroll indicator direction for the ending edge of the view. 532 * 533 * @see #setScrollIndicators(View, int) 534 * @see #setScrollIndicators(View, int, int) 535 * @see #getScrollIndicators(View) 536 */ 537 public static final int SCROLL_INDICATOR_END = 0x20; 538 539 private static Method sDispatchStartTemporaryDetach; 540 private static Method sDispatchFinishTemporaryDetach; 541 private static boolean sTempDetachBound; 542 543 private static WeakHashMap<View, String> sTransitionNameMap; 544 private static WeakHashMap<View, ViewPropertyAnimatorCompat> sViewPropertyAnimatorMap = null; 545 546 private static Method sChildrenDrawingOrderMethod; 547 private static Field sAccessibilityDelegateField; 548 private static boolean sAccessibilityDelegateCheckFailed = false; 549 550 private static boolean sTryHiddenViewTransformMatrixToGlobal = true; 551 552 private static ThreadLocal<Rect> sThreadLocalRect; 553 getEmptyTempRect()554 private static Rect getEmptyTempRect() { 555 if (sThreadLocalRect == null) { 556 sThreadLocalRect = new ThreadLocal<>(); 557 } 558 Rect rect = sThreadLocalRect.get(); 559 if (rect == null) { 560 rect = new Rect(); 561 sThreadLocalRect.set(rect); 562 } 563 rect.setEmpty(); 564 return rect; 565 } 566 567 /** 568 * Stores debugging information about attributes. This should be called in a constructor by 569 * every custom {@link View} that uses a custom styleable. If the custom view does not call it, 570 * then the custom attributes used by this view will not be visible in layout inspection tools. 571 * 572 * No-op before API 29. 573 * 574 * @param view view for which to save the data. 575 * @param context Context under which this view is created. 576 * @param styleable A reference to styleable array R.styleable.Foo 577 * @param attrs AttributeSet used to construct this view. 578 * @param t Resolved {@link TypedArray} returned by a call to 579 * {@link android.content.res.Resources#obtainAttributes(AttributeSet, int[])}. 580 * @param defStyleAttr Default style attribute passed into the view constructor. 581 * @param defStyleRes Default style resource passed into the view constructor. 582 */ saveAttributeDataForStyleable(@onNull View view, @SuppressLint("ContextFirst") @NonNull Context context, int @NonNull [] styleable, @Nullable AttributeSet attrs, @NonNull TypedArray t, int defStyleAttr, int defStyleRes)583 public static void saveAttributeDataForStyleable(@NonNull View view, 584 @SuppressLint("ContextFirst") @NonNull Context context, int @NonNull [] styleable, 585 @Nullable AttributeSet attrs, @NonNull TypedArray t, int defStyleAttr, 586 int defStyleRes) { 587 if (Build.VERSION.SDK_INT >= 29) { 588 Api29Impl.saveAttributeDataForStyleable( 589 view, context, styleable, attrs, t, defStyleAttr, defStyleRes); 590 } 591 } 592 593 /** 594 * Check if this view can be scrolled horizontally in a certain direction. 595 * 596 * @param view The View against which to invoke the method. 597 * @param direction Negative to check scrolling left, positive to check scrolling right. 598 * @return true if this view can be scrolled in the specified direction, false otherwise. 599 * 600 * @deprecated Use {@link View#canScrollHorizontally(int)} directly. 601 */ 602 @androidx.annotation.ReplaceWith(expression = "view.canScrollHorizontally(direction)") 603 @Deprecated canScrollHorizontally(View view, int direction)604 public static boolean canScrollHorizontally(View view, int direction) { 605 return view.canScrollHorizontally(direction); 606 } 607 608 /** 609 * Check if this view can be scrolled vertically in a certain direction. 610 * 611 * @param view The View against which to invoke the method. 612 * @param direction Negative to check scrolling up, positive to check scrolling down. 613 * @return true if this view can be scrolled in the specified direction, false otherwise. 614 * @deprecated Use {@link View#canScrollVertically(int)} directly. 615 */ 616 @androidx.annotation.ReplaceWith(expression = "view.canScrollVertically(direction)") 617 @Deprecated canScrollVertically(View view, int direction)618 public static boolean canScrollVertically(View view, int direction) { 619 return view.canScrollVertically(direction); 620 } 621 622 /** 623 * Returns the over-scroll mode for this view. The result will be 624 * one of {@link #OVER_SCROLL_ALWAYS} (default), {@link #OVER_SCROLL_IF_CONTENT_SCROLLS} 625 * (allow over-scrolling only if the view content is larger than the container), 626 * or {@link #OVER_SCROLL_NEVER}. 627 * 628 * @param view The View against which to invoke the method. 629 * @return This view's over-scroll mode. 630 * @deprecated Call {@link View#getOverScrollMode()} directly. This method will be 631 * removed in a future release. 632 */ 633 @androidx.annotation.ReplaceWith(expression = "view.getOverScrollMode()") 634 @Deprecated 635 @OverScroll getOverScrollMode(View view)636 public static int getOverScrollMode(View view) { 637 //noinspection ResourceType 638 return view.getOverScrollMode(); 639 } 640 641 /** 642 * Set the over-scroll mode for this view. Valid over-scroll modes are 643 * {@link #OVER_SCROLL_ALWAYS} (default), {@link #OVER_SCROLL_IF_CONTENT_SCROLLS} 644 * (allow over-scrolling only if the view content is larger than the container), 645 * or {@link #OVER_SCROLL_NEVER}. 646 * 647 * Setting the over-scroll mode of a view will have an effect only if the 648 * view is capable of scrolling. 649 * 650 * @param view The View against which to invoke the method. 651 * @param overScrollMode The new over-scroll mode for this view. 652 * @deprecated Call {@link View#setOverScrollMode(int)} directly. This method will be 653 * removed in a future release. 654 */ 655 @androidx.annotation.ReplaceWith(expression = "view.setOverScrollMode(overScrollMode)") 656 @Deprecated setOverScrollMode(View view, @OverScroll int overScrollMode)657 public static void setOverScrollMode(View view, @OverScroll int overScrollMode) { 658 view.setOverScrollMode(overScrollMode); 659 } 660 661 /** 662 * Called from {@link View#dispatchPopulateAccessibilityEvent(AccessibilityEvent)} 663 * giving a chance to this View to populate the accessibility event with its 664 * text content. 665 * <p> 666 * <b>Note:</b> This method should only be used with {@link AccessibilityRecord#getText()}. 667 * Avoid mutating other event state in this method. Instead, follow the practices described in 668 * {@link #dispatchPopulateAccessibilityEvent(AccessibilityEvent)}. In general, put UI 669 * metadata in the node for services to easily query, than in events. 670 * <p> 671 * Example: Adding formatted date string to an accessibility event in addition 672 * to the text added by the super implementation: 673 * <pre> public void onPopulateAccessibilityEvent(AccessibilityEvent event) { 674 * super.onPopulateAccessibilityEvent(event); 675 * final int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_WEEKDAY; 676 * String selectedDateUtterance = DateUtils.formatDateTime(mContext, 677 * mCurrentDate.getTimeInMillis(), flags); 678 * event.getText().add(selectedDateUtterance); 679 * }</pre> 680 * <p> 681 * If an {@link AccessibilityDelegateCompat} has been specified via calling 682 * {@link ViewCompat#setAccessibilityDelegate(View, AccessibilityDelegateCompat)} its 683 * {@link AccessibilityDelegateCompat#onPopulateAccessibilityEvent(View, AccessibilityEvent)} 684 * is responsible for handling this call. 685 * </p> 686 * <p class="note"><strong>Note:</strong> Always call the super implementation before adding 687 * information to the event, in case the default implementation has basic information to add. 688 * </p> 689 * 690 * @param v The View against which to invoke the method. 691 * @param event The accessibility event which to populate. 692 * 693 * @see View#sendAccessibilityEvent(int) 694 * @see View#dispatchPopulateAccessibilityEvent(AccessibilityEvent) 695 * 696 * @deprecated Call {@link View#onPopulateAccessibilityEvent(AccessibilityEvent)} directly. 697 * This method will be removed in a future release. 698 */ 699 @androidx.annotation.ReplaceWith(expression = "v.onPopulateAccessibilityEvent(event)") 700 @Deprecated onPopulateAccessibilityEvent(View v, AccessibilityEvent event)701 public static void onPopulateAccessibilityEvent(View v, AccessibilityEvent event) { 702 v.onPopulateAccessibilityEvent(event); 703 } 704 705 /** 706 * Initializes an {@link AccessibilityEvent} with information about 707 * this View which is the event source. In other words, the source of 708 * an accessibility event is the view whose state change triggered firing 709 * the event. 710 * <p> 711 * Example: Setting the password property of an event in addition 712 * to properties set by the super implementation: 713 * <pre> public void onInitializeAccessibilityEvent(AccessibilityEvent event) { 714 * super.onInitializeAccessibilityEvent(event); 715 * event.setPassword(true); 716 * }</pre> 717 * <p> 718 * If an {@link AccessibilityDelegateCompat} has been specified via calling 719 * {@link ViewCompat#setAccessibilityDelegate(View, AccessibilityDelegateCompat)}, its 720 * {@link AccessibilityDelegateCompat#onInitializeAccessibilityEvent(View, AccessibilityEvent)} 721 * is responsible for handling this call. 722 * 723 * @param v The View against which to invoke the method. 724 * @param event The event to initialize. 725 * 726 * @see View#sendAccessibilityEvent(int) 727 * @see View#dispatchPopulateAccessibilityEvent(AccessibilityEvent) 728 * 729 * @deprecated Call {@link View#onInitializeAccessibilityEvent(AccessibilityEvent)} directly. 730 * This method will be removed in a future release. 731 */ 732 @androidx.annotation.ReplaceWith(expression = "v.onInitializeAccessibilityEvent(event)") 733 @Deprecated onInitializeAccessibilityEvent(View v, AccessibilityEvent event)734 public static void onInitializeAccessibilityEvent(View v, AccessibilityEvent event) { 735 v.onInitializeAccessibilityEvent(event); 736 } 737 738 /** 739 * Initializes an {@link AccessibilityNodeInfoCompat} with information 740 * about this view. The base implementation sets: 741 * <ul> 742 * <li>{@link AccessibilityNodeInfoCompat#setParent(View)},</li> 743 * <li>{@link AccessibilityNodeInfoCompat#setBoundsInParent(Rect)},</li> 744 * <li>{@link AccessibilityNodeInfoCompat#setBoundsInScreen(Rect)},</li> 745 * <li>{@link AccessibilityNodeInfoCompat#setPackageName(CharSequence)},</li> 746 * <li>{@link AccessibilityNodeInfoCompat#setClassName(CharSequence)},</li> 747 * <li>{@link AccessibilityNodeInfoCompat#setContentDescription(CharSequence)},</li> 748 * <li>{@link AccessibilityNodeInfoCompat#setStateDescription(CharSequence)},</li> 749 * <li>{@link AccessibilityNodeInfoCompat#setEnabled(boolean)},</li> 750 * <li>{@link AccessibilityNodeInfoCompat#setClickable(boolean)},</li> 751 * <li>{@link AccessibilityNodeInfoCompat#setFocusable(boolean)},</li> 752 * <li>{@link AccessibilityNodeInfoCompat#setFocused(boolean)},</li> 753 * <li>{@link AccessibilityNodeInfoCompat#setLongClickable(boolean)},</li> 754 * <li>{@link AccessibilityNodeInfoCompat#setSelected(boolean)},</li> 755 * </ul> 756 * <p> 757 * If an {@link AccessibilityDelegateCompat} has been specified via calling 758 * {@link ViewCompat#setAccessibilityDelegate(View, AccessibilityDelegateCompat)}, its 759 * {@link AccessibilityDelegateCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat)} 760 * method is responsible for handling this call. 761 * 762 * @param v The View against which to invoke the method. 763 * @param info The instance to initialize. 764 * @deprecated Call {@link View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)} 765 * directly. 766 */ 767 @androidx.annotation.ReplaceWith(expression = "v.onInitializeAccessibilityNodeInfo(info.unwrap())") 768 @Deprecated onInitializeAccessibilityNodeInfo(@onNull View v, @NonNull AccessibilityNodeInfoCompat info)769 public static void onInitializeAccessibilityNodeInfo(@NonNull View v, 770 @NonNull AccessibilityNodeInfoCompat info) { 771 v.onInitializeAccessibilityNodeInfo(info.unwrap()); 772 } 773 774 /** 775 * Sets a delegate for implementing accessibility support via composition 776 * (as opposed to inheritance). For more details, see 777 * {@link AccessibilityDelegateCompat}. 778 * <p> 779 * <strong>Note:</strong> On platform versions prior to 780 * {@link android.os.Build.VERSION_CODES#M API 23}, delegate methods on 781 * views in the {@code android.widget.*} package are called <i>before</i> 782 * host methods. This prevents certain properties such as class name from 783 * being modified by overriding 784 * {@link AccessibilityDelegateCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat)}, 785 * as any changes will be overwritten by the host class. 786 * <p> 787 * Starting in {@link android.os.Build.VERSION_CODES#M API 23}, delegate 788 * methods are called <i>after</i> host methods, which all properties to be 789 * modified without being overwritten by the host class. 790 * <p> 791 * If an AccessibilityDelegateCompat is already attached to the view, and this method sets 792 * the delegate to null, an empty delegate will be attached to ensure that other compatibility 793 * behavior continues to work for this view. 794 * 795 * @param v view for which to set the delegate. 796 * @param delegate the object to which accessibility method calls should be 797 * delegated 798 * @see AccessibilityDelegateCompat 799 */ setAccessibilityDelegate( @onNull View v, @Nullable AccessibilityDelegateCompat delegate)800 public static void setAccessibilityDelegate( 801 @NonNull View v, @Nullable AccessibilityDelegateCompat delegate) { 802 if ((delegate == null) 803 && (getAccessibilityDelegateInternal(v) instanceof AccessibilityDelegateAdapter)) { 804 delegate = new AccessibilityDelegateCompat(); 805 } 806 setImportantForAccessibilityIfNeeded(v); 807 v.setAccessibilityDelegate(delegate == null ? null : delegate.getBridge()); 808 } 809 810 /** 811 * Sets the hints that help an {@link android.service.autofill.AutofillService} determine how 812 * to autofill the view with the user's data. 813 * 814 * <p>Typically, there is only one way to autofill a view, but there could be more than one. 815 * For example, if the application accepts either an username or email address to identify 816 * an user. 817 * 818 * <p>These hints are not validated by the Android System, but passed "as is" to the service. 819 * Hence, they can have any value, but it's recommended to use the {@code AUTOFILL_HINT_} 820 * constants such as: 821 * {@link View#AUTOFILL_HINT_USERNAME}, {@link View#AUTOFILL_HINT_PASSWORD}, 822 * {@link View#AUTOFILL_HINT_EMAIL_ADDRESS}, 823 * {@link View#AUTOFILL_HINT_NAME}, 824 * {@link View#AUTOFILL_HINT_PHONE}, 825 * {@link View#AUTOFILL_HINT_POSTAL_ADDRESS}, {@link View#AUTOFILL_HINT_POSTAL_CODE}, 826 * {@link View#AUTOFILL_HINT_CREDIT_CARD_NUMBER}, 827 * {@link View#AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE}, 828 * {@link View#AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE}, 829 * {@link View#AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY}, 830 * {@link View#AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH} or 831 * {@link View#AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR}. 832 * 833 * <p>This method is only supported on API >= 26. 834 * On API 25 and below, it is a no-op</p> 835 * 836 * @param view view for which to set the hints. 837 * @param autofillHints The autofill hints to set. If the array is emtpy, {@code null} is set. 838 * {@link android.R.attr#autofillHints} 839 */ setAutofillHints(@onNull View view, String @Nullable ... autofillHints)840 public static void setAutofillHints(@NonNull View view, String @Nullable ... autofillHints) { 841 if (Build.VERSION.SDK_INT >= 26) { 842 Api26Impl.setAutofillHints(view, autofillHints); 843 } 844 } 845 846 /** 847 * Gets the mode for determining whether this view is important for autofill. 848 * 849 * <p>See {@link #setImportantForAutofill(View, int)} and {@link #isImportantForAutofill(View)} 850 * for more info about this mode. 851 * 852 * <p>This method is only supported on API >= 26. 853 * On API 25 and below, it will always return {@link View#IMPORTANT_FOR_AUTOFILL_AUTO}.</p> 854 * 855 * @param v The View against which to invoke the method. 856 * @return {@link View#IMPORTANT_FOR_AUTOFILL_AUTO} by default, or value passed to 857 * {@link #setImportantForAutofill(View, int)}. 858 * 859 * {@link android.R.attr#importantForAutofill} 860 */ 861 @SuppressLint("InlinedApi") getImportantForAutofill(@onNull View v)862 public static @AutofillImportance int getImportantForAutofill(@NonNull View v) { 863 if (Build.VERSION.SDK_INT >= 26) { 864 return Api26Impl.getImportantForAutofill(v); 865 } 866 return View.IMPORTANT_FOR_AUTOFILL_AUTO; 867 } 868 869 /** 870 * Sets the mode for determining whether this view is considered important for autofill. 871 * 872 * <p>The platform determines the importance for autofill automatically but you 873 * can use this method to customize the behavior. For example: 874 * 875 * <ol> 876 * <li>When the view contents is irrelevant for autofill (for example, a text field used in a 877 * "Captcha" challenge), it should be {@link View#IMPORTANT_FOR_AUTOFILL_NO}.</li> 878 * <li>When both the view and its children are irrelevant for autofill (for example, the root 879 * view of an activity containing a spreadhseet editor), it should be 880 * {@link View#IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS}.</li> 881 * <li>When the view content is relevant for autofill but its children aren't (for example, 882 * a credit card expiration date represented by a custom view that overrides the proper 883 * autofill methods and has 2 children representing the month and year), it should 884 * be {@link View#IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS}.</li> 885 * </ol> 886 * 887 * <p><strong>NOTE:</strong> setting the mode as does {@link View#IMPORTANT_FOR_AUTOFILL_NO} or 888 * {@link View#IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS} does not guarantee the view (and 889 * its children) will be always be considered not important; for example, when the user 890 * explicitly makes an autofill request, all views are considered important. See 891 * {@link #isImportantForAutofill(View)} for more details about how the View's importance for 892 * autofill is used. 893 * 894 * <p>This method is only supported on API >= 26. 895 * On API 25 and below, it is a no-op</p> 896 * 897 * @param v The View against which to invoke the method. 898 * @param mode {@link View#IMPORTANT_FOR_AUTOFILL_AUTO}, 899 * {@link View#IMPORTANT_FOR_AUTOFILL_YES}, 900 * {@link View#IMPORTANT_FOR_AUTOFILL_NO}, 901 * {@link View#IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS}, 902 * or {@link View#IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS}. 903 * 904 * {@link android.R.attr#importantForAutofill} 905 */ setImportantForAutofill(@onNull View v, @AutofillImportance int mode)906 public static void setImportantForAutofill(@NonNull View v, @AutofillImportance int mode) { 907 if (Build.VERSION.SDK_INT >= 26) { 908 Api26Impl.setImportantForAutofill(v, mode); 909 } 910 } 911 912 /** 913 * Hints the Android System whether the {@link android.app.assist.AssistStructure.ViewNode} 914 * associated with this view is considered important for autofill purposes. 915 * 916 * <p>Generally speaking, a view is important for autofill if: 917 * <ol> 918 * <li>The view can be autofilled by an {@link android.service.autofill.AutofillService}.</li> 919 * <li>The view contents can help an {@link android.service.autofill.AutofillService} 920 * determine how other views can be autofilled.</li> 921 * </ol> 922 * 923 * <p>For example, view containers should typically return {@code false} for performance reasons 924 * (since the important info is provided by their children), but if its properties have relevant 925 * information (for example, a resource id called {@code credentials}, it should return 926 * {@code true}. On the other hand, views representing labels or editable fields should 927 * typically return {@code true}, but in some cases they could return {@code false} 928 * (for example, if they're part of a "Captcha" mechanism). 929 * 930 * <p>The value returned by this method depends on the value returned by 931 * {@link #getImportantForAutofill(View)}: 932 * 933 * <ol> 934 * <li>if it returns {@link View#IMPORTANT_FOR_AUTOFILL_YES} or 935 * {@link View#IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS}, 936 * then it returns {@code true}</li> 937 * <li>if it returns {@link View#IMPORTANT_FOR_AUTOFILL_NO} or 938 * {@link View#IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS}, 939 * then it returns {@code false}</li> 940 * <li>if it returns {@link View#IMPORTANT_FOR_AUTOFILL_AUTO}, 941 * then it uses some simple heuristics that can return {@code true} 942 * in some cases (like a container with a resource id), but {@code false} in most.</li> 943 * <li>otherwise, it returns {@code false}.</li> 944 * </ol> 945 * 946 * <p>When a view is considered important for autofill: 947 * <ul> 948 * <li>The view might automatically trigger an autofill request when focused on. 949 * <li>The contents of the view are included in the {@link android.view.ViewStructure} 950 * used in an autofill request. 951 * </ul> 952 * 953 * <p>On the other hand, when a view is considered not important for autofill: 954 * <ul> 955 * <li>The view never automatically triggers autofill requests, but it can trigger a manual 956 * request through {@link android.view.autofill.AutofillManager#requestAutofill(View)}. 957 * <li>The contents of the view are not included in the {@link android.view.ViewStructure} 958 * used in an autofill request, unless the request has the 959 * {@link View#AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS} flag. 960 * </ul> 961 * 962 * <p>This method is only supported on API >= 26. 963 * On API 25 and below, it will always return {@code true}.</p> 964 * 965 * @param v The View against which to invoke the method. 966 * @return whether the view is considered important for autofill. 967 * 968 * @see #setImportantForAutofill(View, int) 969 * @see View#IMPORTANT_FOR_AUTOFILL_AUTO 970 * @see View#IMPORTANT_FOR_AUTOFILL_YES 971 * @see View#IMPORTANT_FOR_AUTOFILL_NO 972 * @see View#IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS 973 * @see View#IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS 974 * @see android.view.autofill.AutofillManager#requestAutofill(View) 975 */ isImportantForAutofill(@onNull View v)976 public static boolean isImportantForAutofill(@NonNull View v) { 977 if (Build.VERSION.SDK_INT >= 26) { 978 return Api26Impl.isImportantForAutofill(v); 979 } 980 return true; 981 } 982 983 /** 984 * Gets the unique, logical identifier of this view in the activity, for autofill purposes. 985 * 986 * <p>The autofill id is created on demand, unless it is explicitly set by 987 * {@link #setAutofillId(AutofillId)}. 988 * 989 * <p>See {@link #setAutofillId(AutofillId)} for more info. 990 * 991 * Compatibility behavior: 992 * <ul> 993 * <li>SDK 26 and above, this method matches platform behavior. 994 * <li>SDK 25 and below, this method always return null. 995 * </ul> 996 * 997 * @param v The View against which to invoke the method. 998 * @return The View's autofill id. 999 */ getAutofillId(@onNull View v)1000 public static @Nullable AutofillIdCompat getAutofillId(@NonNull View v) { 1001 if (Build.VERSION.SDK_INT >= 26) { 1002 return AutofillIdCompat.toAutofillIdCompat(Api26Impl.getAutofillId(v)); 1003 } 1004 return null; 1005 } 1006 1007 /** 1008 * Sets the unique, logical identifier of this view in the activity, for autofill purposes. 1009 * 1010 * <p>The autofill id is created on demand, and this method should only be called when a view is 1011 * reused after {@link #dispatchProvideAutofillStructure(ViewStructure, int)} is called, as 1012 * that method creates a snapshot of the view that is passed along to the autofill service. 1013 * 1014 * <p>This method is typically used when view subtrees are recycled to represent different 1015 * content* —in this case, the autofill id can be saved before the view content is swapped 1016 * out, and restored later when it's swapped back in. For example: 1017 * 1018 * <pre> 1019 * EditText reusableView = ...; 1020 * ViewGroup parentView = ...; 1021 * AutofillManager afm = ...; 1022 * 1023 * // Swap out the view and change its contents 1024 * AutofillId oldId = reusableView.getAutofillId(); 1025 * CharSequence oldText = reusableView.getText(); 1026 * parentView.removeView(reusableView); 1027 * AutofillId newId = afm.getNextAutofillId(); 1028 * reusableView.setText("New I am"); 1029 * reusableView.setAutofillId(newId); 1030 * parentView.addView(reusableView); 1031 * 1032 * // Later, swap the old content back in 1033 * parentView.removeView(reusableView); 1034 * reusableView.setAutofillId(oldId); 1035 * reusableView.setText(oldText); 1036 * parentView.addView(reusableView); 1037 * </pre> 1038 * 1039 * <p>NOTE: If this view is a descendant of an {@link android.widget.AdapterView}, the system 1040 * may reset its autofill id when this view is recycled. If the autofill ids need to be stable, 1041 * they should be set again in 1042 * {@link android.widget.Adapter#getView(int, android.view.View, android.view.ViewGroup)}. 1043 * 1044 * Compatibility behavior: 1045 * <ul> 1046 * <li>SDK 28 and above, this method matches platform behavior. 1047 * <li>SDK 27 and below, this method does nothing. 1048 * </ul> 1049 * 1050 * @param v The View against which to invoke the method. 1051 * @param id an autofill ID that is unique in the {@link android.app.Activity} hosting the view, 1052 * or {@code null} to reset it. Usually it's an id previously allocated to another view (and 1053 * obtained through {@link #getAutofillId()}), or a new value obtained through 1054 * {@link AutofillManager#getNextAutofillId()}. 1055 * 1056 * @throws IllegalStateException if the view is already {@link #isAttachedToWindow() attached to 1057 * a window}. 1058 * 1059 * @throws IllegalArgumentException if the id is an autofill id associated with a virtual view. 1060 */ setAutofillId(@onNull View v, @Nullable AutofillIdCompat id)1061 public static void setAutofillId(@NonNull View v, @Nullable AutofillIdCompat id) { 1062 if (Build.VERSION.SDK_INT >= 28) { 1063 Api28Impl.setAutofillId(v, id); 1064 } 1065 } 1066 1067 /** 1068 * Sets the mode for determining whether this view is considered important for content capture. 1069 * 1070 * <p>The platform determines the importance for autofill automatically but you 1071 * can use this method to customize the behavior. Typically, a view that provides text should 1072 * be marked as {@link #IMPORTANT_FOR_CONTENT_CAPTURE_YES}. 1073 * 1074 * Compatibility behavior: 1075 * <ul> 1076 * <li>SDK 30 and above, this method matches platform behavior. 1077 * <li>SDK 29 and below, this method does nothing. 1078 * </ul> 1079 * 1080 * @param v The View against which to invoke the method. 1081 * @param mode {@link #IMPORTANT_FOR_CONTENT_CAPTURE_AUTO}, 1082 * {@link #IMPORTANT_FOR_CONTENT_CAPTURE_YES}, {@link #IMPORTANT_FOR_CONTENT_CAPTURE_NO}, 1083 * {@link #IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS}, 1084 * or {@link #IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS}. 1085 * 1086 * @attr ref android.R.styleable#View_importantForContentCapture 1087 */ setImportantForContentCapture(@onNull View v, @ImportantForContentCapture int mode)1088 public static void setImportantForContentCapture(@NonNull View v, 1089 @ImportantForContentCapture int mode) { 1090 if (Build.VERSION.SDK_INT >= 30) { 1091 Api30Impl.setImportantForContentCapture(v, mode); 1092 } 1093 } 1094 1095 /** 1096 * Gets the mode for determining whether this view is important for content capture. 1097 * 1098 * <p>See {@link #setImportantForContentCapture(int)} and 1099 * {@link #isImportantForContentCapture()} for more info about this mode. 1100 * 1101 * Compatibility behavior: 1102 * <ul> 1103 * <li>SDK 30 and above, this method matches platform behavior. 1104 * <li>SDK 29 and below, this method always return {@link #IMPORTANT_FOR_CONTENT_CAPTURE_AUTO}. 1105 * </ul> 1106 * 1107 * @param v The View against which to invoke the method. 1108 * @return {@link #IMPORTANT_FOR_CONTENT_CAPTURE_AUTO} by default, or value passed to 1109 * {@link #setImportantForContentCapture(int)}. 1110 * 1111 * @attr ref android.R.styleable#View_importantForContentCapture 1112 */ getImportantForContentCapture(@onNull View v)1113 public static int getImportantForContentCapture(@NonNull View v) { 1114 if (Build.VERSION.SDK_INT >= 30) { 1115 return Api30Impl.getImportantForContentCapture(v); 1116 } 1117 return IMPORTANT_FOR_CONTENT_CAPTURE_AUTO; 1118 } 1119 1120 /** 1121 * Hints the Android System whether this view is considered important for content capture, based 1122 * on the value explicitly set by {@link #setImportantForContentCapture(int)} and heuristics 1123 * when it's {@link #IMPORTANT_FOR_CONTENT_CAPTURE_AUTO}. 1124 * 1125 * <p>See {@link ContentCaptureManager} for more info about content capture. 1126 * 1127 * Compatibility behavior: 1128 * <ul> 1129 * <li>SDK 30 and above, this method matches platform behavior. 1130 * <li>SDK 29 and below, this method always return false. 1131 * </ul> 1132 * 1133 * @param v The View against which to invoke the method. 1134 * @return whether the view is considered important for content capture. 1135 * 1136 * @see #setImportantForContentCapture(int) 1137 * @see #IMPORTANT_FOR_CONTENT_CAPTURE_AUTO 1138 * @see #IMPORTANT_FOR_CONTENT_CAPTURE_YES 1139 * @see #IMPORTANT_FOR_CONTENT_CAPTURE_NO 1140 * @see #IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS 1141 * @see #IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS 1142 */ isImportantForContentCapture(@onNull View v)1143 public static boolean isImportantForContentCapture(@NonNull View v) { 1144 if (Build.VERSION.SDK_INT >= 30) { 1145 return Api30Impl.isImportantForContentCapture(v); 1146 } 1147 return false; 1148 } 1149 1150 /** 1151 * Gets the session used to notify content capture events. 1152 * 1153 * Compatibility behavior: 1154 * <ul> 1155 * <li>SDK 29 and above, this method matches platform behavior. 1156 * <li>SDK 28 and below, this method always return null. 1157 * </ul> 1158 * 1159 * @param v The View against which to invoke the method. 1160 * @return session explicitly set by {@link #setContentCaptureSession(ContentCaptureSession)}, 1161 * inherited by ancestors, default session or {@code null} if content capture is disabled for 1162 * this view. 1163 */ getContentCaptureSession(@onNull View v)1164 public static @Nullable ContentCaptureSessionCompat getContentCaptureSession(@NonNull View v) { 1165 if (Build.VERSION.SDK_INT >= 29) { 1166 ContentCaptureSession session = Api29Impl.getContentCaptureSession(v); 1167 if (session == null) { 1168 return null; 1169 } 1170 return ContentCaptureSessionCompat.toContentCaptureSessionCompat(session, v); 1171 } 1172 return null; 1173 } 1174 1175 /** 1176 * Sets the (optional) {@link ContentCaptureSession} associated with this view. 1177 * 1178 * <p>This method should be called when you need to associate a {@link ContentCaptureContext} to 1179 * the content capture events associated with this view or its view hierarchy (if it's a 1180 * {@link ViewGroup}). 1181 * 1182 * <p>For example, if your activity is associated with a web domain, first you would need to 1183 * set the context for the main DOM: 1184 * 1185 * <pre> 1186 * ContentCaptureSession mainSession = rootView.getContentCaptureSession(); 1187 * mainSession.setContentCaptureContext(ContentCaptureContext.forLocusId(Uri.parse(myUrl)); 1188 * </pre> 1189 * 1190 * <p>Then if the page had an {@code IFRAME}, you would create a new session for it: 1191 * 1192 * <pre> 1193 * ContentCaptureSession iframeSession = mainSession.createContentCaptureSession( 1194 * ContentCaptureContext.forLocusId(Uri.parse(iframeUrl))); 1195 * iframeView.setContentCaptureSession(iframeSession); 1196 * </pre> 1197 * 1198 * Compatibility behavior: 1199 * <ul> 1200 * <li>SDK 29 and above, this method matches platform behavior. 1201 * <li>SDK 28 and below, this method does nothing. 1202 * </ul> 1203 * 1204 * @param v The View against which to invoke the method. 1205 * @param contentCaptureSession a session created by 1206 * {@link ContentCaptureSession#createContentCaptureSession( 1207 * android.view.contentcapture.ContentCaptureContext)}. 1208 */ setContentCaptureSession(@onNull View v, @Nullable ContentCaptureSessionCompat contentCaptureSession)1209 public static void setContentCaptureSession(@NonNull View v, 1210 @Nullable ContentCaptureSessionCompat contentCaptureSession) { 1211 if (Build.VERSION.SDK_INT >= 29) { 1212 Api29Impl.setContentCaptureSession(v, contentCaptureSession); 1213 } 1214 } 1215 1216 /** 1217 * Checks whether provided View has an accessibility delegate attached to it. 1218 * 1219 * @param view The View instance to check 1220 * @return True if the View has an accessibility delegate 1221 */ hasAccessibilityDelegate(@onNull View view)1222 public static boolean hasAccessibilityDelegate(@NonNull View view) { 1223 return getAccessibilityDelegateInternal(view) != null; 1224 } 1225 1226 /** 1227 * Get the current accessibility delegate. 1228 * @see #setAccessibilityDelegate(View, AccessibilityDelegateCompat) 1229 * 1230 * @param view The view whose delegate is of interest 1231 * @return A compat wrapper for the current delegate. If no delegate is attached, you may 1232 * still get an object that is being used to provide backward compatibility. Returns 1233 * {@code null} if there is no delegate attached. 1234 */ getAccessibilityDelegate( @onNull View view)1235 public static @Nullable AccessibilityDelegateCompat getAccessibilityDelegate( 1236 @NonNull View view) { 1237 final View.AccessibilityDelegate delegate = getAccessibilityDelegateInternal(view); 1238 if (delegate == null) { 1239 return null; 1240 } 1241 if (delegate instanceof AccessibilityDelegateAdapter) { 1242 return ((AccessibilityDelegateAdapter) delegate).mCompat; 1243 } 1244 return new AccessibilityDelegateCompat(delegate); 1245 } 1246 ensureAccessibilityDelegateCompat(@onNull View v)1247 static void ensureAccessibilityDelegateCompat(@NonNull View v) { 1248 AccessibilityDelegateCompat delegateCompat = getAccessibilityDelegate(v); 1249 if (delegateCompat == null) { 1250 delegateCompat = new AccessibilityDelegateCompat(); 1251 } 1252 setAccessibilityDelegate(v, delegateCompat); 1253 } 1254 getAccessibilityDelegateInternal( @onNull View v)1255 private static View.@Nullable AccessibilityDelegate getAccessibilityDelegateInternal( 1256 @NonNull View v) { 1257 if (Build.VERSION.SDK_INT >= 29) { 1258 return Api29Impl.getAccessibilityDelegate(v); 1259 } else { 1260 return getAccessibilityDelegateThroughReflection(v); 1261 } 1262 } 1263 1264 @SuppressWarnings("JavaReflectionMemberAccess") // Private field getAccessibilityDelegateThroughReflection( @onNull View v)1265 private static View.@Nullable AccessibilityDelegate getAccessibilityDelegateThroughReflection( 1266 @NonNull View v) { 1267 if (sAccessibilityDelegateCheckFailed) { 1268 return null; // View implementation might have changed. 1269 } 1270 if (sAccessibilityDelegateField == null) { 1271 try { 1272 sAccessibilityDelegateField = View.class 1273 .getDeclaredField("mAccessibilityDelegate"); 1274 sAccessibilityDelegateField.setAccessible(true); 1275 } catch (Throwable t) { 1276 sAccessibilityDelegateCheckFailed = true; 1277 return null; 1278 } 1279 } 1280 try { 1281 Object o = sAccessibilityDelegateField.get(v); 1282 if (o instanceof View.AccessibilityDelegate) { 1283 return (View.AccessibilityDelegate) o; 1284 } 1285 return null; 1286 } catch (Throwable t) { 1287 sAccessibilityDelegateCheckFailed = true; 1288 return null; 1289 } 1290 } 1291 1292 /** 1293 * Indicates whether the view is currently tracking transient state that the 1294 * app should not need to concern itself with saving and restoring, but that 1295 * the framework should take special note to preserve when possible. 1296 * 1297 * @param view View to check for transient state 1298 * @return true if the view has transient state 1299 * @deprecated Call {@link View#hasTransientState()} directly. 1300 */ 1301 @androidx.annotation.ReplaceWith(expression = "view.hasTransientState()") 1302 @Deprecated hasTransientState(@onNull View view)1303 public static boolean hasTransientState(@NonNull View view) { 1304 return view.hasTransientState(); 1305 } 1306 1307 /** 1308 * Set whether this view is currently tracking transient state that the 1309 * framework should attempt to preserve when possible. 1310 * 1311 * @param view View tracking transient state 1312 * @param hasTransientState true if this view has transient state 1313 * @deprecated Call {@link View#setHasTransientState(boolean)} directly. 1314 */ 1315 @androidx.annotation.ReplaceWith(expression = "view.setHasTransientState(hasTransientState)") 1316 @Deprecated setHasTransientState(@onNull View view, boolean hasTransientState)1317 public static void setHasTransientState(@NonNull View view, boolean hasTransientState) { 1318 view.setHasTransientState(hasTransientState); 1319 } 1320 1321 /** 1322 * <p>Cause an invalidate to happen on the next animation time step, typically the 1323 * next display frame.</p> 1324 * 1325 * <p>This method can be invoked from outside of the UI thread 1326 * only when this View is attached to a window.</p> 1327 * 1328 * @param view View to invalidate 1329 * @deprecated Call {@link View#postInvalidateOnAnimation()} directly. 1330 */ 1331 @androidx.annotation.ReplaceWith(expression = "view.postInvalidateOnAnimation()") 1332 @Deprecated postInvalidateOnAnimation(@onNull View view)1333 public static void postInvalidateOnAnimation(@NonNull View view) { 1334 view.postInvalidateOnAnimation(); 1335 } 1336 1337 /** 1338 * <p>Cause an invalidate of the specified area to happen on the next animation 1339 * time step, typically the next display frame.</p> 1340 * 1341 * <p>This method can be invoked from outside of the UI thread 1342 * only when this View is attached to a window.</p> 1343 * 1344 * @param view View to invalidate 1345 * @param left The left coordinate of the rectangle to invalidate. 1346 * @param top The top coordinate of the rectangle to invalidate. 1347 * @param right The right coordinate of the rectangle to invalidate. 1348 * @param bottom The bottom coordinate of the rectangle to invalidate. 1349 * @deprecated Call {@link View#postInvalidateOnAnimation(int, int, int, int)} directly. 1350 */ 1351 @androidx.annotation.ReplaceWith(expression = "view.postInvalidateOnAnimation(left, top, right, bottom)") 1352 @Deprecated postInvalidateOnAnimation(@onNull View view, int left, int top, int right, int bottom)1353 public static void postInvalidateOnAnimation(@NonNull View view, int left, int top, 1354 int right, int bottom) { 1355 view.postInvalidateOnAnimation(left, top, right, bottom); 1356 } 1357 1358 /** 1359 * <p>Causes the Runnable to execute on the next animation time step. 1360 * The runnable will be run on the user interface thread.</p> 1361 * 1362 * <p>This method can be invoked from outside of the UI thread 1363 * only when this View is attached to a window.</p> 1364 * 1365 * @param view View to post this Runnable to 1366 * @param action The Runnable that will be executed. 1367 * @deprecated Call {@link View#postOnAnimation(Runnable)} directly. 1368 */ 1369 @androidx.annotation.ReplaceWith(expression = "view.postOnAnimation(action)") 1370 @Deprecated postOnAnimation(@onNull View view, @NonNull Runnable action)1371 public static void postOnAnimation(@NonNull View view, @NonNull Runnable action) { 1372 view.postOnAnimation(action); 1373 } 1374 1375 /** 1376 * <p>Causes the Runnable to execute on the next animation time step, 1377 * after the specified amount of time elapses. 1378 * The runnable will be run on the user interface thread.</p> 1379 * 1380 * <p>This method can be invoked from outside of the UI thread 1381 * only when this View is attached to a window.</p> 1382 * 1383 * @param view The view to post this Runnable to 1384 * @param action The Runnable that will be executed. 1385 * @param delayMillis The delay (in milliseconds) until the Runnable 1386 * will be executed. 1387 * @deprecated Call {@link View#postOnAnimationDelayed(Runnable, long)} directly. 1388 */ 1389 @androidx.annotation.ReplaceWith(expression = "view.postOnAnimationDelayed(action, delayMillis)") 1390 @Deprecated 1391 @SuppressLint("LambdaLast") postOnAnimationDelayed(@onNull View view, @NonNull Runnable action, long delayMillis)1392 public static void postOnAnimationDelayed(@NonNull View view, @NonNull Runnable action, 1393 long delayMillis) { 1394 view.postOnAnimationDelayed(action, delayMillis); 1395 } 1396 1397 /** 1398 * Gets the mode for determining whether this View is important for accessibility 1399 * which is if it fires accessibility events and if it is reported to 1400 * accessibility services that query the screen. 1401 * 1402 * @param view The view whose property to get. 1403 * @return The mode for determining whether a View is important for accessibility. 1404 * 1405 * @see #IMPORTANT_FOR_ACCESSIBILITY_YES 1406 * @see #IMPORTANT_FOR_ACCESSIBILITY_NO 1407 * @see #IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS 1408 * @see #IMPORTANT_FOR_ACCESSIBILITY_AUTO 1409 * @deprecated Call {@link View#getImportantForAccessibility()} directly. 1410 */ 1411 @androidx.annotation.ReplaceWith(expression = "view.getImportantForAccessibility()") 1412 @Deprecated 1413 @ImportantForAccessibility getImportantForAccessibility(@onNull View view)1414 public static int getImportantForAccessibility(@NonNull View view) { 1415 return view.getImportantForAccessibility(); 1416 } 1417 1418 /** 1419 * Sets how to determine whether this view is important for accessibility 1420 * which is if it fires accessibility events and if it is reported to 1421 * accessibility services that query the screen. 1422 * <p> 1423 * <em>Note:</em> If the current platform version does not support the 1424 * {@link #IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS} mode, then 1425 * {@link #IMPORTANT_FOR_ACCESSIBILITY_NO} will be used as it is the 1426 * closest terms of semantics. 1427 * </p> 1428 * 1429 * @param view The view whose property to set. 1430 * @param mode How to determine whether this view is important for accessibility. 1431 * 1432 * @see #IMPORTANT_FOR_ACCESSIBILITY_YES 1433 * @see #IMPORTANT_FOR_ACCESSIBILITY_NO 1434 * @see #IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS 1435 * @see #IMPORTANT_FOR_ACCESSIBILITY_AUTO 1436 * @deprecated Call {@link View#setImportantForAccessibility(int)} directly. 1437 */ 1438 @androidx.annotation.ReplaceWith(expression = "view.setImportantForAccessibility(mode)") 1439 @Deprecated 1440 @UiThread setImportantForAccessibility(@onNull View view, @ImportantForAccessibility int mode)1441 public static void setImportantForAccessibility(@NonNull View view, 1442 @ImportantForAccessibility int mode) { 1443 view.setImportantForAccessibility(mode); 1444 } 1445 1446 /** 1447 * Computes whether this view should be exposed for accessibility. In 1448 * general, views that are interactive or provide information are exposed 1449 * while views that serve only as containers are hidden. 1450 * <p> 1451 * If an ancestor of this view has importance 1452 * {@link #IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS}, this method 1453 * returns <code>false</code>. 1454 * <p> 1455 * Otherwise, the value is computed according to the view's 1456 * {@link #getImportantForAccessibility(View)} value: 1457 * <ol> 1458 * <li>{@link #IMPORTANT_FOR_ACCESSIBILITY_NO} or 1459 * {@link #IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS}, return <code>false 1460 * </code></li> 1461 * <li>{@link #IMPORTANT_FOR_ACCESSIBILITY_YES}, return <code>true</code></li> 1462 * <li>{@link #IMPORTANT_FOR_ACCESSIBILITY_AUTO}, return <code>true</code> if 1463 * view satisfies any of the following:</li> 1464 * <ul> 1465 * <li>Is actionable, e.g. {@link View#isClickable()}, 1466 * {@link View#isLongClickable()}, or {@link View#isFocusable()}</li> 1467 * <li>Has an {@link AccessibilityDelegateCompat}</li> 1468 * <li>Has an interaction listener, e.g. {@link View.OnTouchListener}, 1469 * {@link View.OnKeyListener}, etc.</li> 1470 * <li>Is an accessibility live region, e.g. 1471 * {@link #getAccessibilityLiveRegion(View)} is not 1472 * {@link #ACCESSIBILITY_LIVE_REGION_NONE}.</li> 1473 * </ul> 1474 * </ol> 1475 * <p> 1476 * <em>Note:</em> Prior to API 21, this method will always return {@code true}. 1477 * 1478 * @param view view for which to check the state. 1479 * @return Whether the view is exposed for accessibility. 1480 * @see #setImportantForAccessibility(View, int) 1481 * @see #getImportantForAccessibility(View) 1482 */ isImportantForAccessibility(@onNull View view)1483 public static boolean isImportantForAccessibility(@NonNull View view) { 1484 if (Build.VERSION.SDK_INT >= 21) { 1485 return Api21Impl.isImportantForAccessibility(view); 1486 } 1487 return true; 1488 } 1489 1490 /** 1491 * Performs the specified accessibility action on the view. For 1492 * possible accessibility actions look at {@link AccessibilityNodeInfoCompat}. 1493 * <p> 1494 * If an {@link AccessibilityDelegateCompat} has been specified via calling 1495 * {@link #setAccessibilityDelegate(View, AccessibilityDelegateCompat)} its 1496 * {@link AccessibilityDelegateCompat#performAccessibilityAction(View, int, Bundle)} 1497 * is responsible for handling this call. 1498 * </p> 1499 * <p> 1500 * <b>Note:</b> Avoid setting accessibility focus with 1501 * {@link AccessibilityNodeInfoCompat.AccessibilityActionCompat#ACTION_ACCESSIBILITY_FOCUS}. 1502 * This is intended to be controlled by screen readers. Apps changing focus can confuse 1503 * screen readers, and the resulting behavior can vary by device and screen reader version. 1504 * 1505 * @param view view on which to perform the action. 1506 * @param action The action to perform. 1507 * @param arguments Optional action arguments. 1508 * @return Whether the action was performed. 1509 * @deprecated Call {@link View#performAccessibilityAction(int, Bundle)} directly. 1510 */ 1511 @androidx.annotation.ReplaceWith(expression = "view.performAccessibilityAction(action, arguments)") 1512 @Deprecated performAccessibilityAction(@onNull View view, int action, @Nullable Bundle arguments)1513 public static boolean performAccessibilityAction(@NonNull View view, int action, 1514 @Nullable Bundle arguments) { 1515 return view.performAccessibilityAction(action, arguments); 1516 } 1517 1518 /** 1519 * Perform a haptic feedback to the user for the view. 1520 * 1521 * <p>The framework will provide haptic feedback for some built in actions, such as long 1522 * presses, but you may wish to provide feedback for your own widget. 1523 * 1524 * <p>The feedback will only be performed if {@link android.view.View#isHapticFeedbackEnabled()} 1525 * is true. 1526 * 1527 * <em>Note:</em> Check compatibility support for each feedback constant described at 1528 * {@link HapticFeedbackConstantsCompat}. 1529 * 1530 * @param view The view. 1531 * @param feedbackConstant One of the constants defined in {@link HapticFeedbackConstantsCompat} 1532 * @return Whether the feedback might be performed - generally this result should be ignored 1533 */ performHapticFeedback(@onNull View view, @HapticFeedbackType int feedbackConstant)1534 public static boolean performHapticFeedback(@NonNull View view, 1535 @HapticFeedbackType int feedbackConstant) { 1536 feedbackConstant = 1537 HapticFeedbackConstantsCompat.getFeedbackConstantOrFallback(feedbackConstant); 1538 if (feedbackConstant == HapticFeedbackConstantsCompat.NO_HAPTICS) { 1539 // This compat implementation is straightforward. 1540 return false; 1541 } 1542 return view.performHapticFeedback(feedbackConstant); 1543 } 1544 1545 /** 1546 * Perform a haptic feedback to the user for the view. 1547 * 1548 * <p>This is similar to {@link #performHapticFeedback(android.view.View, int)}, with 1549 * additional options. 1550 * 1551 * <em>Note:</em> Check compatibility support for each feedback constant described at 1552 * {@link HapticFeedbackConstantsCompat}. 1553 * 1554 * @param view The view. 1555 * @param feedbackConstant One of the constants defined in {@link HapticFeedbackConstantsCompat} 1556 * @param flags Additional flags as per {@link HapticFeedbackConstantsCompat} 1557 * @return Whether the feedback might be performed - generally this result should be ignored 1558 */ performHapticFeedback(@onNull View view, @HapticFeedbackType int feedbackConstant, @HapticFeedbackFlags int flags)1559 public static boolean performHapticFeedback(@NonNull View view, 1560 @HapticFeedbackType int feedbackConstant, @HapticFeedbackFlags int flags) { 1561 feedbackConstant = 1562 HapticFeedbackConstantsCompat.getFeedbackConstantOrFallback(feedbackConstant); 1563 if (feedbackConstant == HapticFeedbackConstantsCompat.NO_HAPTICS) { 1564 // This compat implementation is straightforward. 1565 return false; 1566 } 1567 return view.performHapticFeedback(feedbackConstant, flags); 1568 } 1569 1570 /** 1571 * Adds an accessibility action that can be performed on a node associated with a view. 1572 * A view can only have 32 actions created with this API. 1573 * 1574 * @param view The view. 1575 * @param label The user facing description of the action. If an action with the same label 1576 * already exists, it will be replaced. 1577 * @param command The command performed when the service requests the action. 1578 * @return The id associated with the action, 1579 * or {@link View#NO_ID} if the action could not be created. 1580 * This id can be used to remove the action. 1581 * <p> 1582 * Compatibility: 1583 * <ul> 1584 * <li>API < 21: No-op</li> 1585 * </ul> 1586 */ addAccessibilityAction( @onNull View view, @NonNull CharSequence label, @NonNull AccessibilityViewCommand command)1587 public static int addAccessibilityAction( 1588 @NonNull View view, @NonNull CharSequence label, 1589 @NonNull AccessibilityViewCommand command) { 1590 int actionId = getAvailableActionIdFromResources(view, label); 1591 if (actionId != View.NO_ID) { 1592 AccessibilityActionCompat action = 1593 new AccessibilityActionCompat(actionId, label, command); 1594 addAccessibilityAction(view, action); 1595 } 1596 return actionId; 1597 } 1598 1599 private static final int[] ACCESSIBILITY_ACTIONS_RESOURCE_IDS = { 1600 R.id.accessibility_custom_action_0, 1601 R.id.accessibility_custom_action_1, 1602 R.id.accessibility_custom_action_2, 1603 R.id.accessibility_custom_action_3, 1604 R.id.accessibility_custom_action_4, 1605 R.id.accessibility_custom_action_5, 1606 R.id.accessibility_custom_action_6, 1607 R.id.accessibility_custom_action_7, 1608 R.id.accessibility_custom_action_8, 1609 R.id.accessibility_custom_action_9, 1610 R.id.accessibility_custom_action_10, 1611 R.id.accessibility_custom_action_11, 1612 R.id.accessibility_custom_action_12, 1613 R.id.accessibility_custom_action_13, 1614 R.id.accessibility_custom_action_14, 1615 R.id.accessibility_custom_action_15, 1616 R.id.accessibility_custom_action_16, 1617 R.id.accessibility_custom_action_17, 1618 R.id.accessibility_custom_action_18, 1619 R.id.accessibility_custom_action_19, 1620 R.id.accessibility_custom_action_20, 1621 R.id.accessibility_custom_action_21, 1622 R.id.accessibility_custom_action_22, 1623 R.id.accessibility_custom_action_23, 1624 R.id.accessibility_custom_action_24, 1625 R.id.accessibility_custom_action_25, 1626 R.id.accessibility_custom_action_26, 1627 R.id.accessibility_custom_action_27, 1628 R.id.accessibility_custom_action_28, 1629 R.id.accessibility_custom_action_29, 1630 R.id.accessibility_custom_action_30, 1631 R.id.accessibility_custom_action_31}; 1632 getAvailableActionIdFromResources(View view, @NonNull CharSequence label)1633 private static int getAvailableActionIdFromResources(View view, @NonNull CharSequence label) { 1634 int result = View.NO_ID; 1635 // Finds the existing custom action id by label. 1636 List<AccessibilityActionCompat> actions = getActionList(view); 1637 for (int i = 0; i < actions.size(); i++) { 1638 if (TextUtils.equals(label, actions.get(i).getLabel())) { 1639 return actions.get(i).getId(); 1640 } 1641 } 1642 // Finds the first available action id from resources. 1643 for (int i = 0; i < ACCESSIBILITY_ACTIONS_RESOURCE_IDS.length && result == View.NO_ID; 1644 i++) { 1645 int id = ACCESSIBILITY_ACTIONS_RESOURCE_IDS[i]; 1646 boolean idAvailable = true; 1647 for (int j = 0; j < actions.size(); j++) { 1648 idAvailable &= actions.get(j).getId() != id; 1649 } 1650 if (idAvailable) { 1651 result = id; 1652 } 1653 } 1654 return result; 1655 } 1656 1657 /** 1658 * Replaces an action. This can be used to change the default behavior or label of the action 1659 * specified. If label and command are both {@code null}, the action will be removed. 1660 * 1661 * @param view The view. 1662 * @param replacedAction The action to be replaced. 1663 * @param label The user facing description of the action or {@code null}. 1664 * @param command The command performed when the service requests the action. 1665 * 1666 * <p> 1667 * Compatibility: 1668 * <ul> 1669 * <li>API < 21: No-op</li> 1670 * </ul> 1671 */ replaceAccessibilityAction(@onNull View view, @NonNull AccessibilityActionCompat replacedAction, @Nullable CharSequence label, @Nullable AccessibilityViewCommand command)1672 public static void replaceAccessibilityAction(@NonNull View view, 1673 @NonNull AccessibilityActionCompat replacedAction, @Nullable CharSequence label, 1674 @Nullable AccessibilityViewCommand command) { 1675 if (command == null && label == null) { 1676 ViewCompat.removeAccessibilityAction(view, replacedAction.getId()); 1677 } else { 1678 addAccessibilityAction(view, replacedAction.createReplacementAction(label, command)); 1679 } 1680 } 1681 addAccessibilityAction(@onNull View view, @NonNull AccessibilityActionCompat action)1682 private static void addAccessibilityAction(@NonNull View view, 1683 @NonNull AccessibilityActionCompat action) { 1684 if (Build.VERSION.SDK_INT >= 21) { 1685 ensureAccessibilityDelegateCompat(view); 1686 removeActionWithId(action.getId(), view); 1687 getActionList(view).add(action); 1688 notifyViewAccessibilityStateChangedIfNeeded( 1689 view, AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); 1690 } 1691 } 1692 1693 /** 1694 * Removes an accessibility action that can be performed on a node associated with a view. 1695 * If the action was not already added to the view, calling this method has no effect. 1696 * 1697 * @param view The view 1698 * @param actionId The actionId of the action to be removed. 1699 */ removeAccessibilityAction(@onNull View view, int actionId)1700 public static void removeAccessibilityAction(@NonNull View view, int actionId) { 1701 if (Build.VERSION.SDK_INT >= 21) { 1702 removeActionWithId(actionId, view); 1703 notifyViewAccessibilityStateChangedIfNeeded( 1704 view, AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); 1705 } 1706 } 1707 removeActionWithId(int actionId, View view)1708 private static void removeActionWithId(int actionId, View view) { 1709 List<AccessibilityActionCompat> actions = getActionList(view); 1710 for (int i = 0; i < actions.size(); i++) { 1711 if (actions.get(i).getId() == actionId) { 1712 actions.remove(i); 1713 break; 1714 } 1715 } 1716 } 1717 1718 @SuppressWarnings("unchecked") getActionList(View view)1719 private static List<AccessibilityActionCompat> getActionList(View view) { 1720 ArrayList<AccessibilityActionCompat> actions = 1721 (ArrayList<AccessibilityActionCompat>) view.getTag(R.id.tag_accessibility_actions); 1722 if (actions == null) { 1723 actions = new ArrayList<>(); 1724 view.setTag(R.id.tag_accessibility_actions, actions); 1725 } 1726 return actions; 1727 } 1728 1729 /** 1730 * Sets the state description of this node. 1731 * <p> 1732 * <strong>Note:</strong> Cannot be called from an 1733 * {@link android.accessibilityservice.AccessibilityService}. 1734 * This class is made immutable before being delivered to an AccessibilityService. 1735 * </p> 1736 * <p> 1737 * State refers to a frequently changing property of the View, such as an enabled/disabled 1738 * state of a button or the audio level of a volume slider. 1739 * 1740 * <p> 1741 * This should omit role or content. Role refers to the kind of user-interface element the 1742 * View is, such as a Button or Checkbox. Content is the meaningful text and graphics that 1743 * should be described by {@link View#setContentDescription(CharSequence)} or 1744 * {@code android:contentDescription}. It is expected that a content description mostly 1745 * remains constant, while a state description updates from time to time. 1746 * 1747 * @param view view for which to set the description. 1748 * @param stateDescription the state description of this node. 1749 * 1750 * @throws IllegalStateException If called from an AccessibilityService. 1751 * @see View#setStateDescription(CharSequence) 1752 * @see View#setContentDescription(CharSequence) 1753 */ 1754 @UiThread setStateDescription(@onNull View view, @Nullable CharSequence stateDescription)1755 public static void setStateDescription(@NonNull View view, 1756 @Nullable CharSequence stateDescription) { 1757 stateDescriptionProperty().set(view, stateDescription); 1758 } 1759 1760 /** 1761 * Returns the {@link View}'s state description. 1762 * <p> 1763 * <strong>Note:</strong> Do not override this method, as it will have no 1764 * effect on the state description presented to accessibility services. 1765 * You must call {@link #setStateDescription(View, CharSequence)} to modify the 1766 * state description. 1767 * 1768 * @param view view for which to get the description 1769 * @return the state description 1770 * @see #setStateDescription(View, CharSequence) 1771 */ 1772 @UiThread getStateDescription(@onNull View view)1773 public static @Nullable CharSequence getStateDescription(@NonNull View view) { 1774 return stateDescriptionProperty().get(view); 1775 } 1776 1777 1778 /** 1779 * Allow accessibility services to find and activate clickable spans in the application. 1780 * 1781 * <p> 1782 * {@link android.text.style.ClickableSpan} is automatically supported from 1783 * API 26. For compatibility back to API 19, this should be enabled. 1784 * <p> 1785 * {@link android.text.style.URLSpan}, a subclass of ClickableSpans, is 1786 * automatically supported and does not need this enabled. 1787 * <p> 1788 * Do not put ClickableSpans in {@link View#setContentDescription(CharSequence)} or 1789 * {@link View#setStateDescription(CharSequence)}. 1790 * These links are only visible to accessibility services in 1791 * {@link AccessibilityNodeInfoCompat#getText()}, which should be 1792 * modifiable using helper methods on UI elements. For example, use 1793 * {@link android.widget.TextView#setText(CharSequence)} to modify the text of TextViews. 1794 * 1795 * @param view The view 1796 * <p> 1797 * Compatibility: 1798 * <ul> 1799 * <li>API < 19: No-op 1800 * </ul> 1801 */ enableAccessibleClickableSpanSupport(@onNull View view)1802 public static void enableAccessibleClickableSpanSupport(@NonNull View view) { 1803 ensureAccessibilityDelegateCompat(view); 1804 } 1805 1806 /** 1807 * Gets the provider for managing a virtual view hierarchy rooted at this View 1808 * and reported to {@link android.accessibilityservice.AccessibilityService}s 1809 * that explore the window content. 1810 * <p> 1811 * If this method returns an instance, this instance is responsible for managing 1812 * {@link AccessibilityNodeInfoCompat}s describing the virtual sub-tree rooted at 1813 * this View including the one representing the View itself. Similarly the returned 1814 * instance is responsible for performing accessibility actions on any virtual 1815 * view or the root view itself. 1816 * </p> 1817 * <p> 1818 * If an {@link AccessibilityDelegateCompat} has been specified via calling 1819 * {@link #setAccessibilityDelegate(View, AccessibilityDelegateCompat)} its 1820 * {@link AccessibilityDelegateCompat#getAccessibilityNodeProvider(View)} 1821 * is responsible for handling this call. 1822 * </p> 1823 * 1824 * @param view The view whose property to get. 1825 * @return The provider. 1826 * 1827 * @see AccessibilityNodeProviderCompat 1828 */ getAccessibilityNodeProvider( @onNull View view)1829 public static @Nullable AccessibilityNodeProviderCompat getAccessibilityNodeProvider( 1830 @NonNull View view) { 1831 AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider(); 1832 if (provider != null) { 1833 return new AccessibilityNodeProviderCompat(provider); 1834 } 1835 return null; 1836 } 1837 1838 /** 1839 * The opacity of the view. This is a value from 0 to 1, where 0 means the view is 1840 * completely transparent and 1 means the view is completely opaque. 1841 * 1842 * <p>By default this is 1.0f. 1843 * @return The opacity of the view. 1844 * 1845 * @deprecated Use {@link View#getAlpha()} directly. 1846 */ 1847 @androidx.annotation.ReplaceWith(expression = "view.getAlpha()") 1848 @Deprecated getAlpha(View view)1849 public static float getAlpha(View view) { 1850 return view.getAlpha(); 1851 } 1852 1853 /** 1854 * <p>Specifies the type of layer backing this view. The layer can be 1855 * {@link View#LAYER_TYPE_NONE disabled}, {@link View#LAYER_TYPE_SOFTWARE software} or 1856 * {@link View#LAYER_TYPE_HARDWARE hardware}.</p> 1857 * 1858 * <p>A layer is associated with an optional {@link android.graphics.Paint} 1859 * instance that controls how the layer is composed on screen. The following 1860 * properties of the paint are taken into account when composing the layer:</p> 1861 * <ul> 1862 * <li>{@link android.graphics.Paint#getAlpha() Translucency (alpha)}</li> 1863 * <li>{@link android.graphics.Paint#getXfermode() Blending mode}</li> 1864 * <li>{@link android.graphics.Paint#getColorFilter() Color filter}</li> 1865 * </ul> 1866 * 1867 * <p>If this view has an alpha value set to < 1.0 by calling 1868 * setAlpha(float), the alpha value of the layer's paint is replaced by 1869 * this view's alpha value. Calling setAlpha(float) is therefore 1870 * equivalent to setting a hardware layer on this view and providing a paint with 1871 * the desired alpha value.<p> 1872 * 1873 * <p>Refer to the documentation of {@link View#LAYER_TYPE_NONE disabled}, 1874 * {@link View#LAYER_TYPE_SOFTWARE software} and {@link View#LAYER_TYPE_HARDWARE hardware} 1875 * for more information on when and how to use layers.</p> 1876 * 1877 * @param view View to set the layer type for 1878 * @param layerType The type of layer to use with this view, must be one of 1879 * {@link View#LAYER_TYPE_NONE}, {@link View#LAYER_TYPE_SOFTWARE} or 1880 * {@link View#LAYER_TYPE_HARDWARE} 1881 * @param paint The paint used to compose the layer. This argument is optional 1882 * and can be null. It is ignored when the layer type is 1883 * {@link View#LAYER_TYPE_NONE} 1884 * 1885 * @deprecated Use {@link View#setLayerType(int, Paint)} directly. 1886 */ 1887 @androidx.annotation.ReplaceWith(expression = "view.setLayerType(layerType, paint)") 1888 @Deprecated setLayerType(View view, @LayerType int layerType, Paint paint)1889 public static void setLayerType(View view, @LayerType int layerType, Paint paint) { 1890 view.setLayerType(layerType, paint); 1891 } 1892 1893 /** 1894 * Indicates what type of layer is currently associated with this view. By default 1895 * a view does not have a layer, and the layer type is {@link View#LAYER_TYPE_NONE}. 1896 * Refer to the documentation of 1897 * {@link #setLayerType(android.view.View, int, android.graphics.Paint)} 1898 * for more information on the different types of layers. 1899 * 1900 * @param view The view to fetch the layer type from 1901 * @return {@link View#LAYER_TYPE_NONE}, {@link View#LAYER_TYPE_SOFTWARE} or 1902 * {@link View#LAYER_TYPE_HARDWARE} 1903 * 1904 * @see #setLayerType(android.view.View, int, android.graphics.Paint) 1905 * @see View#LAYER_TYPE_NONE 1906 * @see View#LAYER_TYPE_SOFTWARE 1907 * @see View#LAYER_TYPE_HARDWARE 1908 * 1909 * @deprecated Use {@link View#getLayerType()} directly. 1910 */ 1911 @androidx.annotation.ReplaceWith(expression = "view.getLayerType()") 1912 @Deprecated 1913 @LayerType getLayerType(View view)1914 public static int getLayerType(View view) { 1915 //noinspection ResourceType 1916 return view.getLayerType(); 1917 } 1918 1919 /** 1920 * Gets the id of a view for which a given view serves as a label for 1921 * accessibility purposes. 1922 * 1923 * @param view The view on which to invoke the corresponding method. 1924 * @return The labeled view id. 1925 * @deprecated Call {@link View#getLabelFor()} directly. 1926 */ 1927 @androidx.annotation.ReplaceWith(expression = "view.getLabelFor()") 1928 @Deprecated getLabelFor(@onNull View view)1929 public static int getLabelFor(@NonNull View view) { 1930 return view.getLabelFor(); 1931 } 1932 1933 /** 1934 * Sets the id of a view for which a given view serves as a label for 1935 * accessibility purposes. 1936 * 1937 * @param view The view on which to invoke the corresponding method. 1938 * @param labeledId The labeled view id. 1939 * @deprecated Call {@link View#setLabelFor(int)} directly. 1940 */ 1941 @androidx.annotation.ReplaceWith(expression = "view.setLabelFor(labeledId)") 1942 @Deprecated setLabelFor(@onNull View view, @IdRes int labeledId)1943 public static void setLabelFor(@NonNull View view, @IdRes int labeledId) { 1944 view.setLabelFor(labeledId); 1945 } 1946 1947 /** 1948 * Updates the {@link Paint} object used with the current layer (used only if the current 1949 * layer type is not set to {@link View#LAYER_TYPE_NONE}). Changed properties of the Paint 1950 * provided to {@link #setLayerType(android.view.View, int, android.graphics.Paint)} 1951 * will be used the next time the View is redrawn, but 1952 * {@link #setLayerPaint(android.view.View, android.graphics.Paint)} 1953 * must be called to ensure that the view gets redrawn immediately. 1954 * 1955 * <p>A layer is associated with an optional {@link android.graphics.Paint} 1956 * instance that controls how the layer is composed on screen. The following 1957 * properties of the paint are taken into account when composing the layer:</p> 1958 * <ul> 1959 * <li>{@link android.graphics.Paint#getAlpha() Translucency (alpha)}</li> 1960 * <li>{@link android.graphics.Paint#getXfermode() Blending mode}</li> 1961 * <li>{@link android.graphics.Paint#getColorFilter() Color filter}</li> 1962 * </ul> 1963 * 1964 * <p>If this view has an alpha value set to < 1.0 by calling 1965 * View#setAlpha(float), the alpha value of the layer's paint is replaced by 1966 * this view's alpha value. Calling View#setAlpha(float) is therefore 1967 * equivalent to setting a hardware layer on this view and providing a paint with 1968 * the desired alpha value.</p> 1969 * 1970 * @param view View to set a layer paint for 1971 * @param paint The paint used to compose the layer. This argument is optional 1972 * and can be null. It is ignored when the layer type is 1973 * {@link View#LAYER_TYPE_NONE} 1974 * 1975 * @see #setLayerType(View, int, android.graphics.Paint) 1976 * @deprecated Call {@link View#setLayerPaint(Paint)} directly. 1977 */ 1978 @androidx.annotation.ReplaceWith(expression = "view.setLayerPaint(paint)") 1979 @Deprecated setLayerPaint(@onNull View view, @Nullable Paint paint)1980 public static void setLayerPaint(@NonNull View view, @Nullable Paint paint) { 1981 view.setLayerPaint(paint); 1982 } 1983 1984 /** 1985 * Returns the resolved layout direction for this view. 1986 * 1987 * @param view View to get layout direction for 1988 * @return {@link #LAYOUT_DIRECTION_RTL} if the layout direction is RTL or returns 1989 * {@link #LAYOUT_DIRECTION_LTR} if the layout direction is not RTL. 1990 * 1991 * For compatibility, this will return {@link #LAYOUT_DIRECTION_LTR} if API version 1992 * is lower than Jellybean MR1 (API 17) 1993 * 1994 * @deprecated Call {@link View#getLayoutDirection()} directly. 1995 */ 1996 @androidx.annotation.ReplaceWith(expression = "view.getLayoutDirection()") 1997 @Deprecated 1998 @ResolvedLayoutDirectionMode getLayoutDirection(@onNull View view)1999 public static int getLayoutDirection(@NonNull View view) { 2000 return view.getLayoutDirection(); 2001 } 2002 2003 /** 2004 * Set the layout direction for this view. This will propagate a reset of layout direction 2005 * resolution to the view's children and resolve layout direction for this view. 2006 * 2007 * @param view View to set layout direction for 2008 * @param layoutDirection the layout direction to set. Should be one of: 2009 * 2010 * {@link #LAYOUT_DIRECTION_LTR}, 2011 * {@link #LAYOUT_DIRECTION_RTL}, 2012 * {@link #LAYOUT_DIRECTION_INHERIT}, 2013 * {@link #LAYOUT_DIRECTION_LOCALE}. 2014 * 2015 * Resolution will be done if the value is set to LAYOUT_DIRECTION_INHERIT. The resolution 2016 * proceeds up the parent chain of the view to get the value. If there is no parent, then it 2017 * will return the default {@link #LAYOUT_DIRECTION_LTR}. 2018 * 2019 * @deprecated Call {@link View#setLayoutDirection(int)} directly. 2020 */ 2021 @androidx.annotation.ReplaceWith(expression = "view.setLayoutDirection(layoutDirection)") 2022 @Deprecated setLayoutDirection(@onNull View view, @LayoutDirectionMode int layoutDirection)2023 public static void setLayoutDirection(@NonNull View view, 2024 @LayoutDirectionMode int layoutDirection) { 2025 view.setLayoutDirection(layoutDirection); 2026 } 2027 2028 /** 2029 * Gets the parent for accessibility purposes. Note that the parent for 2030 * accessibility is not necessary the immediate parent. It is the first 2031 * predecessor that is important for accessibility. 2032 * 2033 * @param view View to retrieve parent for 2034 * @return The parent for use in accessibility inspection 2035 * @deprecated Call {@link View#getParentForAccessibility()} directly. 2036 */ 2037 @androidx.annotation.ReplaceWith(expression = "view.getParentForAccessibility()") 2038 @Deprecated getParentForAccessibility(@onNull View view)2039 public static @Nullable ViewParent getParentForAccessibility(@NonNull View view) { 2040 return view.getParentForAccessibility(); 2041 } 2042 2043 /** 2044 * Finds the first descendant view with the given ID, the view itself if the ID matches 2045 * {@link View#getId()}, or throws an IllegalArgumentException if the ID is invalid or there 2046 * is no matching view in the hierarchy. 2047 * <p> 2048 * <strong>Note:</strong> In most cases -- depending on compiler support -- 2049 * the resulting view is automatically cast to the target class type. If 2050 * the target class type is unconstrained, an explicit cast may be 2051 * necessary. 2052 * 2053 * @param view the view to start the search from. 2054 * @param id the ID to search for 2055 * @return a view with given ID 2056 * @see View#findViewById(int) 2057 */ 2058 @SuppressWarnings("TypeParameterUnusedInFormals") requireViewById(@onNull View view, @IdRes int id)2059 public static <T extends View> @NonNull T requireViewById(@NonNull View view, @IdRes int id) { 2060 if (Build.VERSION.SDK_INT >= 28) { 2061 return ViewCompat.Api28Impl.requireViewById(view, id); 2062 } 2063 2064 T targetView = view.findViewById(id); 2065 if (targetView == null) { 2066 throw new IllegalArgumentException("ID does not reference a View inside this View"); 2067 } 2068 return targetView; 2069 } 2070 2071 /** 2072 * Indicates whether this View is opaque. An opaque View guarantees that it will 2073 * draw all the pixels overlapping its bounds using a fully opaque color. 2074 * 2075 * @param view view for which to check the state. 2076 * @return True if this View is guaranteed to be fully opaque, false otherwise. 2077 * @deprecated Use {@link View#isOpaque()} directly. This method will be 2078 * removed in a future release. 2079 */ 2080 @androidx.annotation.ReplaceWith(expression = "view.isOpaque()") 2081 @Deprecated isOpaque(View view)2082 public static boolean isOpaque(View view) { 2083 return view.isOpaque(); 2084 } 2085 2086 /** 2087 * Utility to reconcile a desired size and state, with constraints imposed 2088 * by a MeasureSpec. Will take the desired size, unless a different size 2089 * is imposed by the constraints. The returned value is a compound integer, 2090 * with the resolved size in the {@link #MEASURED_SIZE_MASK} bits and 2091 * optionally the bit {@link #MEASURED_STATE_TOO_SMALL} set if the resulting 2092 * size is smaller than the size the view wants to be. 2093 * 2094 * @param size How big the view wants to be 2095 * @param measureSpec Constraints imposed by the parent 2096 * @param childMeasuredState Size information bit mask for the view's children. 2097 * @return Size information bit mask as defined by 2098 * {@link #MEASURED_SIZE_MASK} and {@link #MEASURED_STATE_TOO_SMALL}. 2099 * 2100 * @deprecated Use {@link View#resolveSizeAndState(int, int, int)} directly. 2101 */ 2102 @Deprecated resolveSizeAndState(int size, int measureSpec, int childMeasuredState)2103 public static int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) { 2104 return View.resolveSizeAndState(size, measureSpec, childMeasuredState); 2105 } 2106 2107 /** 2108 * Return the full width measurement information for this view as computed 2109 * by the most recent call to {@link android.view.View#measure(int, int)}. 2110 * This result is a bit mask as defined by {@link #MEASURED_SIZE_MASK} and 2111 * {@link #MEASURED_STATE_TOO_SMALL}. 2112 * This should be used during measurement and layout calculations only. Use 2113 * {@link android.view.View#getWidth()} to see how wide a view is after layout. 2114 * 2115 * @return The measured width of this view as a bit mask. 2116 * 2117 * @deprecated Use {@link View#getMeasuredWidth()} directly. 2118 */ 2119 @androidx.annotation.ReplaceWith(expression = "view.getMeasuredWidthAndState()") 2120 @Deprecated getMeasuredWidthAndState(View view)2121 public static int getMeasuredWidthAndState(View view) { 2122 return view.getMeasuredWidthAndState(); 2123 } 2124 2125 /** 2126 * Return the full height measurement information for this view as computed 2127 * by the most recent call to {@link android.view.View#measure(int, int)}. 2128 * This result is a bit mask as defined by {@link #MEASURED_SIZE_MASK} and 2129 * {@link #MEASURED_STATE_TOO_SMALL}. 2130 * This should be used during measurement and layout calculations only. Use 2131 * {@link android.view.View#getHeight()} to see how wide a view is after layout. 2132 * 2133 * @return The measured width of this view as a bit mask. 2134 * 2135 * @deprecated Use {@link View#getMeasuredHeightAndState()} directly. 2136 */ 2137 @androidx.annotation.ReplaceWith(expression = "view.getMeasuredHeightAndState()") 2138 @Deprecated getMeasuredHeightAndState(View view)2139 public static int getMeasuredHeightAndState(View view) { 2140 return view.getMeasuredHeightAndState(); 2141 } 2142 2143 /** 2144 * Return only the state bits of {@link #getMeasuredWidthAndState} 2145 * and {@link #getMeasuredHeightAndState}, combined into one integer. 2146 * The width component is in the regular bits {@link #MEASURED_STATE_MASK} 2147 * and the height component is at the shifted bits 2148 * {@link #MEASURED_HEIGHT_STATE_SHIFT}>>{@link #MEASURED_STATE_MASK}. 2149 * 2150 * @deprecated Use {@link View#getMeasuredState()} directly. 2151 */ 2152 @androidx.annotation.ReplaceWith(expression = "view.getMeasuredState()") 2153 @Deprecated getMeasuredState(View view)2154 public static int getMeasuredState(View view) { 2155 return view.getMeasuredState(); 2156 } 2157 2158 /** 2159 * Merge two states as returned by {@link #getMeasuredState(View)}. 2160 * @param curState The current state as returned from a view or the result 2161 * of combining multiple views. 2162 * @param newState The new view state to combine. 2163 * @return Returns a new integer reflecting the combination of the two 2164 * states. 2165 * 2166 * @deprecated Use {@link View#combineMeasuredStates(int, int)} directly. 2167 */ 2168 @Deprecated combineMeasuredStates(int curState, int newState)2169 public static int combineMeasuredStates(int curState, int newState) { 2170 return View.combineMeasuredStates(curState, newState); 2171 } 2172 2173 /** 2174 * Gets the live region mode for the specified View. 2175 * 2176 * @param view The view from which to obtain the live region mode 2177 * @return The live region mode for the view. 2178 * 2179 * @see ViewCompat#setAccessibilityLiveRegion(View, int) 2180 * @deprecated Call {@link View#getAccessibilityLiveRegion()} directly. 2181 */ 2182 @androidx.annotation.ReplaceWith(expression = "view.getAccessibilityLiveRegion()") 2183 @Deprecated 2184 @AccessibilityLiveRegion getAccessibilityLiveRegion(@onNull View view)2185 public static int getAccessibilityLiveRegion(@NonNull View view) { 2186 return view.getAccessibilityLiveRegion(); 2187 } 2188 2189 /** 2190 * Sets the live region mode for this view. This indicates to accessibility 2191 * services whether they should automatically notify the user about changes 2192 * to the view's content description or text, or to the content descriptions 2193 * or text of the view's children (where applicable). 2194 * <p> 2195 * To indicate that the user should be notified of changes, use 2196 * {@link #ACCESSIBILITY_LIVE_REGION_POLITE}. Announcements from this region are queued and 2197 * do not disrupt ongoing speech. 2198 * <p> 2199 * For example, selecting an option in a dropdown menu may update a panel below with the updated 2200 * content. This panel may be marked as a live region with 2201 * {@link #ACCESSIBILITY_LIVE_REGION_POLITE} to notify users of the change. 2202 * <p> 2203 * For notifying users about errors, such as in a login screen with text that displays an 2204 * "incorrect password" notification, that view should send an AccessibilityEvent of type 2205 * {@link AccessibilityEvent#CONTENT_CHANGE_TYPE_ERROR} and set 2206 * {@link AccessibilityNodeInfo#setError(CharSequence)} instead. Custom widgets should expose 2207 * error-setting methods that support accessibility automatically. For example, instead of 2208 * explicitly sending this event when using a TextView, use 2209 * {@link android.widget.TextView#setError(CharSequence)}. 2210 * <p> 2211 * To disable change notifications for this view, use 2212 * {@link #ACCESSIBILITY_LIVE_REGION_NONE}. This is the default live region 2213 * mode for most views. 2214 * <p> 2215 * If the view's changes should interrupt ongoing speech and notify the user 2216 * immediately, use {@link #ACCESSIBILITY_LIVE_REGION_ASSERTIVE}. This may result in disruptive 2217 * announcements from an accessibility service, so it should generally be used only to convey 2218 * information that is time-sensitive or critical for use of the application. Examples may 2219 * include an incoming call or an emergency alert. 2220 * 2221 * @param view The view on which to set the live region mode 2222 * @param mode The live region mode for this view, one of: 2223 * <ul> 2224 * <li>{@link #ACCESSIBILITY_LIVE_REGION_NONE} 2225 * <li>{@link #ACCESSIBILITY_LIVE_REGION_POLITE} 2226 * <li>{@link #ACCESSIBILITY_LIVE_REGION_ASSERTIVE} 2227 * </ul> 2228 * @deprecated Call {@link View#setAccessibilityLiveRegion(int)} directly. 2229 */ 2230 @androidx.annotation.ReplaceWith(expression = "view.setAccessibilityLiveRegion(mode)") 2231 @Deprecated setAccessibilityLiveRegion(@onNull View view, @AccessibilityLiveRegion int mode)2232 public static void setAccessibilityLiveRegion(@NonNull View view, 2233 @AccessibilityLiveRegion int mode) { 2234 view.setAccessibilityLiveRegion(mode); 2235 } 2236 2237 /** 2238 * Returns the start padding of the specified view depending on its resolved layout direction. 2239 * If there are inset and enabled scrollbars, this value may include the space 2240 * required to display the scrollbars as well. 2241 * 2242 * @param view The view to get padding for 2243 * @return the start padding in pixels 2244 * @deprecated Call {@link View#getPaddingStart()} directly. 2245 */ 2246 @androidx.annotation.ReplaceWith(expression = "view.getPaddingStart()") 2247 @Deprecated 2248 @Px getPaddingStart(@onNull View view)2249 public static int getPaddingStart(@NonNull View view) { 2250 return view.getPaddingStart(); 2251 } 2252 2253 /** 2254 * Returns the end padding of the specified view depending on its resolved layout direction. 2255 * If there are inset and enabled scrollbars, this value may include the space 2256 * required to display the scrollbars as well. 2257 * 2258 * @param view The view to get padding for 2259 * @return the end padding in pixels 2260 * @deprecated Call {@link View#getPaddingEnd()} directly. 2261 */ 2262 @androidx.annotation.ReplaceWith(expression = "view.getPaddingEnd()") 2263 @Deprecated 2264 @Px getPaddingEnd(@onNull View view)2265 public static int getPaddingEnd(@NonNull View view) { 2266 return view.getPaddingEnd(); 2267 } 2268 2269 /** 2270 * Sets the relative padding. The view may add on the space required to display 2271 * the scrollbars, depending on the style and visibility of the scrollbars. 2272 * So the values returned from {@link #getPaddingStart}, {@link View#getPaddingTop}, 2273 * {@link #getPaddingEnd} and {@link View#getPaddingBottom} may be different 2274 * from the values set in this call. 2275 * 2276 * @param view The view on which to set relative padding 2277 * @param start the start padding in pixels 2278 * @param top the top padding in pixels 2279 * @param end the end padding in pixels 2280 * @param bottom the bottom padding in pixels 2281 * @deprecated Call {@link View#setPaddingRelative(int, int, int, int)} directly. 2282 */ 2283 @androidx.annotation.ReplaceWith(expression = "view.setPaddingRelative(start, top, end, bottom)") 2284 @Deprecated setPaddingRelative(@onNull View view, @Px int start, @Px int top, @Px int end, @Px int bottom)2285 public static void setPaddingRelative(@NonNull View view, @Px int start, @Px int top, 2286 @Px int end, @Px int bottom) { 2287 view.setPaddingRelative(start, top, end, bottom); 2288 } 2289 bindTempDetach()2290 private static void bindTempDetach() { 2291 try { 2292 sDispatchStartTemporaryDetach = View.class.getDeclaredMethod( 2293 "dispatchStartTemporaryDetach"); 2294 sDispatchFinishTemporaryDetach = View.class.getDeclaredMethod( 2295 "dispatchFinishTemporaryDetach"); 2296 } catch (NoSuchMethodException e) { 2297 Log.e(TAG, "Couldn't find method", e); 2298 } 2299 sTempDetachBound = true; 2300 } 2301 2302 /** 2303 * Notify a view that it is being temporarily detached. 2304 */ dispatchStartTemporaryDetach(@onNull View view)2305 public static void dispatchStartTemporaryDetach(@NonNull View view) { 2306 if (Build.VERSION.SDK_INT >= 24) { 2307 Api24Impl.dispatchStartTemporaryDetach(view); 2308 } else { 2309 if (!sTempDetachBound) { 2310 bindTempDetach(); 2311 } 2312 if (sDispatchStartTemporaryDetach != null) { 2313 try { 2314 sDispatchStartTemporaryDetach.invoke(view); 2315 } catch (Exception e) { 2316 Log.d(TAG, "Error calling dispatchStartTemporaryDetach", e); 2317 } 2318 } else { 2319 // Try this instead 2320 view.onStartTemporaryDetach(); 2321 } 2322 } 2323 } 2324 2325 /** 2326 * Notify a view that its temporary detach has ended; the view is now reattached. 2327 */ dispatchFinishTemporaryDetach(@onNull View view)2328 public static void dispatchFinishTemporaryDetach(@NonNull View view) { 2329 if (Build.VERSION.SDK_INT >= 24) { 2330 Api24Impl.dispatchFinishTemporaryDetach(view); 2331 } else { 2332 if (!sTempDetachBound) { 2333 bindTempDetach(); 2334 } 2335 if (sDispatchFinishTemporaryDetach != null) { 2336 try { 2337 sDispatchFinishTemporaryDetach.invoke(view); 2338 } catch (Exception e) { 2339 Log.d(TAG, "Error calling dispatchFinishTemporaryDetach", e); 2340 } 2341 } else { 2342 // Try this instead 2343 view.onFinishTemporaryDetach(); 2344 } 2345 } 2346 } 2347 2348 /** 2349 * The horizontal location of this view relative to its {@link View#getLeft() left} position. 2350 * This position is post-layout, in addition to wherever the object's 2351 * layout placed it. 2352 * 2353 * @return The horizontal position of this view relative to its left position, in pixels. 2354 * 2355 * @deprecated Use {@link View#getTranslationX()} directly. 2356 */ 2357 @androidx.annotation.ReplaceWith(expression = "view.getTranslationX()") 2358 @Deprecated getTranslationX(View view)2359 public static float getTranslationX(View view) { 2360 return view.getTranslationX(); 2361 } 2362 2363 /** 2364 * The vertical location of this view relative to its {@link View#getTop() top} position. 2365 * This position is post-layout, in addition to wherever the object's 2366 * layout placed it. 2367 * 2368 * @return The vertical position of this view relative to its top position, in pixels. 2369 * 2370 * @deprecated Use {@link View#getTranslationY()} directly. 2371 */ 2372 @androidx.annotation.ReplaceWith(expression = "view.getTranslationY()") 2373 @Deprecated getTranslationY(View view)2374 public static float getTranslationY(View view) { 2375 return view.getTranslationY(); 2376 } 2377 2378 /** 2379 * The transform matrix of this view, which is calculated based on the current 2380 * rotation, scale, and pivot properties. 2381 * <p> 2382 * 2383 * @param view The view whose Matrix will be returned 2384 * @return The current transform matrix for the view 2385 * 2386 * @see #getRotation(View) 2387 * @see #getScaleX(View) 2388 * @see #getScaleY(View) 2389 * @see #getPivotX(View) 2390 * @see #getPivotY(View) 2391 * 2392 * @deprecated Use {@link View#getMatrix()} directly. 2393 */ 2394 @androidx.annotation.ReplaceWith(expression = "view.getMatrix()") 2395 @Deprecated getMatrix(View view)2396 public static @Nullable Matrix getMatrix(View view) { 2397 return view.getMatrix(); 2398 } 2399 2400 /** 2401 * Returns the minimum width of the view. 2402 * 2403 * <p>Prior to API 16, this method may return 0 on some platforms.</p> 2404 * 2405 * @return the minimum width the view will try to be. 2406 * @deprecated Call {@link View#getMinimumWidth()} directly. 2407 */ 2408 @androidx.annotation.ReplaceWith(expression = "view.getMinimumWidth()") 2409 @Deprecated 2410 @SuppressWarnings({"JavaReflectionMemberAccess", "ConstantConditions"}) 2411 // Reflective access to private field, unboxing result of reflective get() getMinimumWidth(@onNull View view)2412 public static int getMinimumWidth(@NonNull View view) { 2413 return view.getMinimumWidth(); 2414 } 2415 2416 /** 2417 * Returns the minimum height of the view. 2418 * 2419 * <p>Prior to API 16, this method may return 0 on some platforms.</p> 2420 * 2421 * @return the minimum height the view will try to be. 2422 * @deprecated Call {@link View#getMinimumHeight()} directly. 2423 */ 2424 @androidx.annotation.ReplaceWith(expression = "view.getMinimumHeight()") 2425 @Deprecated 2426 @SuppressWarnings({"JavaReflectionMemberAccess", "ConstantConditions"}) 2427 // Reflective access to private field, unboxing result of reflective get() getMinimumHeight(@onNull View view)2428 public static int getMinimumHeight(@NonNull View view) { 2429 return view.getMinimumHeight(); 2430 } 2431 2432 /** 2433 * This method returns a ViewPropertyAnimator object, which can be used to animate 2434 * specific properties on this View. 2435 * 2436 * @return ViewPropertyAnimator The ViewPropertyAnimator associated with this View. 2437 * @deprecated Call {@link View#animate()} directly. 2438 */ 2439 @Deprecated animate(@onNull View view)2440 public static @NonNull ViewPropertyAnimatorCompat animate(@NonNull View view) { 2441 if (sViewPropertyAnimatorMap == null) { 2442 sViewPropertyAnimatorMap = new WeakHashMap<>(); 2443 } 2444 ViewPropertyAnimatorCompat vpa = sViewPropertyAnimatorMap.get(view); 2445 if (vpa == null) { 2446 vpa = new ViewPropertyAnimatorCompat(view); 2447 sViewPropertyAnimatorMap.put(view, vpa); 2448 } 2449 return vpa; 2450 } 2451 2452 /** 2453 * Sets the horizontal location of this view relative to its left position. 2454 * This effectively positions the object post-layout, in addition to wherever the object's 2455 * layout placed it. 2456 * 2457 * @param view view for which to set the translation. 2458 * @param value The horizontal position of this view relative to its left position, 2459 * in pixels. 2460 * 2461 * @deprecated Use {@link View#setTranslationX(float)} directly. 2462 */ 2463 @androidx.annotation.ReplaceWith(expression = "view.setTranslationX(value)") 2464 @Deprecated setTranslationX(View view, float value)2465 public static void setTranslationX(View view, float value) { 2466 view.setTranslationX(value); 2467 } 2468 2469 /** 2470 * Sets the vertical location of this view relative to its top position. 2471 * This effectively positions the object post-layout, in addition to wherever the object's 2472 * layout placed it. 2473 * 2474 * @param view view for which to set the translation. 2475 * @param value The vertical position of this view relative to its top position, 2476 * in pixels. 2477 * 2478 * @attr name android:translationY 2479 * 2480 * @deprecated Use {@link View#setTranslationY(float)} directly. 2481 */ 2482 @androidx.annotation.ReplaceWith(expression = "view.setTranslationY(value)") 2483 @Deprecated setTranslationY(View view, float value)2484 public static void setTranslationY(View view, float value) { 2485 view.setTranslationY(value); 2486 } 2487 2488 /** 2489 * <p>Sets the opacity of the view. This is a value from 0 to 1, where 0 means the view is 2490 * completely transparent and 1 means the view is completely opaque.</p> 2491 * 2492 * <p> Note that setting alpha to a translucent value (0 < alpha < 1) can have significant 2493 * performance implications, especially for large views. It is best to use the alpha property 2494 * sparingly and transiently, as in the case of fading animations.</p> 2495 * 2496 * @param view view to set the alpha on. 2497 * @param value The opacity of the view. 2498 * 2499 * @deprecated Use {@link View#setAlpha(float)} directly. 2500 */ 2501 @androidx.annotation.ReplaceWith(expression = "view.setAlpha(value)") 2502 @Deprecated setAlpha(View view, @FloatRange(from = 0.0, to = 1.0) float value)2503 public static void setAlpha(View view, @FloatRange(from = 0.0, to = 1.0) float value) { 2504 view.setAlpha(value); 2505 } 2506 2507 /** 2508 * Sets the visual x position of this view, in pixels. This is equivalent to setting the 2509 * {@link #setTranslationX(View, float) translationX} property to be the difference between 2510 * the x value passed in and the current left property of the view as determined 2511 * by the layout bounds. 2512 * 2513 * @param view view to set the position on. 2514 * @param value The visual x position of this view, in pixels. 2515 * 2516 * @deprecated Use {@link View#setX(float)} directly. 2517 */ 2518 @androidx.annotation.ReplaceWith(expression = "view.setX(value)") 2519 @Deprecated setX(View view, float value)2520 public static void setX(View view, float value) { 2521 view.setX(value); 2522 } 2523 2524 /** 2525 * Sets the visual y position of this view, in pixels. This is equivalent to setting the 2526 * {@link #setTranslationY(View, float) translationY} property to be the difference between 2527 * the y value passed in and the current top property of the view as determined by the 2528 * layout bounds. 2529 * 2530 * @param view view to set the position on. 2531 * @param value The visual y position of this view, in pixels. 2532 * 2533 * @deprecated Use {@link View#setY(float)} directly. 2534 */ 2535 @androidx.annotation.ReplaceWith(expression = "view.setY(value)") 2536 @Deprecated setY(View view, float value)2537 public static void setY(View view, float value) { 2538 view.setY(value); 2539 } 2540 2541 /** 2542 * Sets the degrees that the view is rotated around the pivot point. Increasing values 2543 * result in clockwise rotation. 2544 * 2545 * @param view view to set the rotation on. 2546 * @param value The degrees of rotation. 2547 * 2548 * @deprecated Use {@link View#setRotation(float)} directly. 2549 */ 2550 @androidx.annotation.ReplaceWith(expression = "view.setRotation(value)") 2551 @Deprecated setRotation(View view, float value)2552 public static void setRotation(View view, float value) { 2553 view.setRotation(value); 2554 } 2555 2556 /** 2557 * Sets the degrees that the view is rotated around the horizontal axis through the pivot point. 2558 * Increasing values result in clockwise rotation from the viewpoint of looking down the 2559 * x axis. 2560 * 2561 * @param view view to set the rotation on. 2562 * @param value The degrees of X rotation. 2563 * 2564 * @deprecated Use {@link View#setRotationX(float)} directly. 2565 */ 2566 @androidx.annotation.ReplaceWith(expression = "view.setRotationX(value)") 2567 @Deprecated setRotationX(View view, float value)2568 public static void setRotationX(View view, float value) { 2569 view.setRotationX(value); 2570 } 2571 2572 /** 2573 * Sets the degrees that the view is rotated around the vertical axis through the pivot point. 2574 * Increasing values result in counter-clockwise rotation from the viewpoint of looking 2575 * down the y axis. 2576 * 2577 * @param view view to set the rotation on. 2578 * @param value The degrees of Y rotation. 2579 * 2580 * @deprecated Use {@link View#setRotationY(float)} directly. 2581 */ 2582 @androidx.annotation.ReplaceWith(expression = "view.setRotationY(value)") 2583 @Deprecated setRotationY(View view, float value)2584 public static void setRotationY(View view, float value) { 2585 view.setRotationY(value); 2586 } 2587 2588 /** 2589 * Sets the amount that the view is scaled in x around the pivot point, as a proportion of 2590 * the view's unscaled width. A value of 1 means that no scaling is applied. 2591 * 2592 * @param view view to set the scale on. 2593 * @param value The scaling factor. 2594 * 2595 * @deprecated Use {@link View#setScaleX(float)} directly. 2596 */ 2597 @androidx.annotation.ReplaceWith(expression = "view.setScaleX(value)") 2598 @Deprecated setScaleX(View view, float value)2599 public static void setScaleX(View view, float value) { 2600 view.setScaleX(value); 2601 } 2602 2603 /** 2604 * Sets the amount that the view is scaled in Y around the pivot point, as a proportion of 2605 * the view's unscaled width. A value of 1 means that no scaling is applied. 2606 * 2607 * @param view view to set the scale on. 2608 * @param value The scaling factor. 2609 * 2610 * @deprecated Use {@link View#setScaleY(float)} directly. 2611 */ 2612 @androidx.annotation.ReplaceWith(expression = "view.setScaleY(value)") 2613 @Deprecated setScaleY(View view, float value)2614 public static void setScaleY(View view, float value) { 2615 view.setScaleY(value); 2616 } 2617 2618 /** 2619 * The x location of the point around which the view is 2620 * {@link #setRotation(View, float) rotated} and {@link #setScaleX(View, float) scaled}. 2621 * 2622 * @param view view for which to get the pivot. 2623 * @deprecated Use {@link View#getPivotX()} directly. 2624 */ 2625 @androidx.annotation.ReplaceWith(expression = "view.getPivotX()") 2626 @Deprecated getPivotX(View view)2627 public static float getPivotX(View view) { 2628 return view.getPivotX(); 2629 } 2630 2631 /** 2632 * Sets the x location of the point around which the view is 2633 * {@link #setRotation(View, float) rotated} and {@link #setScaleX(View, float) scaled}. 2634 * By default, the pivot point is centered on the object. 2635 * Setting this property disables this behavior and causes the view to use only the 2636 * explicitly set pivotX and pivotY values. 2637 * 2638 * @param view view for which to set the pivot. 2639 * @param value The x location of the pivot point. 2640 * 2641 * @deprecated Use {@link View#setPivotX(float)} directly. 2642 */ 2643 @androidx.annotation.ReplaceWith(expression = "view.setPivotX(value)") 2644 @Deprecated setPivotX(View view, float value)2645 public static void setPivotX(View view, float value) { 2646 view.setPivotX(value); 2647 } 2648 2649 /** 2650 * The y location of the point around which the view is {@link #setRotation(View, 2651 * float) rotated} and {@link #setScaleY(View, float) scaled}. 2652 * 2653 * @param view view for which to get the pivot. 2654 * @return The y location of the pivot point. 2655 * 2656 * @deprecated Use {@link View#getPivotY()} directly. 2657 */ 2658 @androidx.annotation.ReplaceWith(expression = "view.getPivotY()") 2659 @Deprecated getPivotY(View view)2660 public static float getPivotY(View view) { 2661 return view.getPivotY(); 2662 } 2663 2664 /** 2665 * Sets the y location of the point around which the view is 2666 * {@link #setRotation(View, float) rotated} and {@link #setScaleY(View, float) scaled}. 2667 * By default, the pivot point is centered on the object. 2668 * Setting this property disables this behavior and causes the view to use only the 2669 * explicitly set pivotX and pivotY values. 2670 * 2671 * @param view view for which to set the pivot. 2672 * @param value The y location of the pivot point. 2673 * 2674 * @deprecated Use {@link View#setPivotX(float)} directly. 2675 */ 2676 @androidx.annotation.ReplaceWith(expression = "view.setPivotY(value)") 2677 @Deprecated setPivotY(View view, float value)2678 public static void setPivotY(View view, float value) { 2679 view.setPivotY(value); 2680 } 2681 2682 /** 2683 * @param view view for which to get the rotation. 2684 * @deprecated Use {@link View#getRotation()} directly. 2685 */ 2686 @androidx.annotation.ReplaceWith(expression = "view.getRotation()") 2687 @Deprecated getRotation(View view)2688 public static float getRotation(View view) { 2689 return view.getRotation(); 2690 } 2691 2692 /** 2693 * @param view view for which to get the rotation. 2694 * @deprecated Use {@link View#getRotationX()} directly. 2695 */ 2696 @androidx.annotation.ReplaceWith(expression = "view.getRotationX()") 2697 @Deprecated getRotationX(View view)2698 public static float getRotationX(View view) { 2699 return view.getRotationX(); 2700 } 2701 2702 /** 2703 * @param view view for which to get the rotation. 2704 * @deprecated Use {@link View#getRotationY()} directly. 2705 */ 2706 @androidx.annotation.ReplaceWith(expression = "view.getRotationY()") 2707 @Deprecated getRotationY(View view)2708 public static float getRotationY(View view) { 2709 return view.getRotationY(); 2710 } 2711 2712 /** 2713 * @param view view for which to get the scale. 2714 * @deprecated Use {@link View#getScaleX()} directly. 2715 */ 2716 @androidx.annotation.ReplaceWith(expression = "view.getScaleX()") 2717 @Deprecated getScaleX(View view)2718 public static float getScaleX(View view) { 2719 return view.getScaleX(); 2720 } 2721 2722 /** 2723 * @param view view for which to get the scale. 2724 * @deprecated Use {@link View#getScaleY()} directly. 2725 */ 2726 @androidx.annotation.ReplaceWith(expression = "view.getScaleY()") 2727 @Deprecated getScaleY(View view)2728 public static float getScaleY(View view) { 2729 return view.getScaleY(); 2730 } 2731 2732 /** 2733 * @param view view for which to get the X. 2734 * @deprecated Use {@link View#getX()} directly. 2735 */ 2736 @androidx.annotation.ReplaceWith(expression = "view.getX()") 2737 @Deprecated getX(View view)2738 public static float getX(View view) { 2739 return view.getX(); 2740 } 2741 2742 /** 2743 * @param view view for which to get the Y. 2744 * @deprecated Use {@link View#getY()} directly. 2745 */ 2746 @androidx.annotation.ReplaceWith(expression = "view.getY()") 2747 @Deprecated getY(View view)2748 public static float getY(View view) { 2749 return view.getY(); 2750 } 2751 2752 /** 2753 * @param view view for which to set the elevation. 2754 * @param elevation view elevation in pixels. 2755 * Sets the base elevation of this view, in pixels. 2756 */ setElevation(@onNull View view, float elevation)2757 public static void setElevation(@NonNull View view, float elevation) { 2758 if (Build.VERSION.SDK_INT >= 21) { 2759 Api21Impl.setElevation(view, elevation); 2760 } 2761 } 2762 2763 /** 2764 * The base elevation of this view relative to its parent, in pixels. 2765 * 2766 * @param view view for which to get the elevation. 2767 * @return The base depth position of the view, in pixels. 2768 */ getElevation(@onNull View view)2769 public static float getElevation(@NonNull View view) { 2770 if (Build.VERSION.SDK_INT >= 21) { 2771 return Api21Impl.getElevation(view); 2772 } 2773 return 0f; 2774 } 2775 2776 /** 2777 * Sets the depth location of this view relative to its {@link #getElevation(View) elevation}. 2778 * @param view view for which to set the translation. 2779 * @param translationZ the depth of location of this view relative its elevation. 2780 */ setTranslationZ(@onNull View view, float translationZ)2781 public static void setTranslationZ(@NonNull View view, float translationZ) { 2782 if (Build.VERSION.SDK_INT >= 21) { 2783 Api21Impl.setTranslationZ(view, translationZ); 2784 } 2785 } 2786 2787 /** 2788 * The depth location of this view relative to its {@link #getElevation(View) elevation}. 2789 * 2790 * @param view view for which to get the translation. 2791 * @return The depth of this view relative to its elevation. 2792 */ getTranslationZ(@onNull View view)2793 public static float getTranslationZ(@NonNull View view) { 2794 if (Build.VERSION.SDK_INT >= 21) { 2795 return Api21Impl.getTranslationZ(view); 2796 } 2797 return 0f; 2798 } 2799 2800 /** 2801 * Sets the name of the View to be used to identify Views in Transitions. 2802 * Names should be unique in the View hierarchy. 2803 * 2804 * @param view The View against which to invoke the method. 2805 * @param transitionName The name of the View to uniquely identify it for Transitions. 2806 */ setTransitionName(@onNull View view, @Nullable String transitionName)2807 public static void setTransitionName(@NonNull View view, @Nullable String transitionName) { 2808 if (Build.VERSION.SDK_INT >= 21) { 2809 Api21Impl.setTransitionName(view, transitionName); 2810 } else { 2811 if (sTransitionNameMap == null) { 2812 sTransitionNameMap = new WeakHashMap<>(); 2813 } 2814 sTransitionNameMap.put(view, transitionName); 2815 } 2816 } 2817 2818 /** 2819 * Returns the name of the View to be used to identify Views in Transitions. 2820 * Names should be unique in the View hierarchy. 2821 * 2822 * <p>This returns null if the View has not been given a name.</p> 2823 * 2824 * @param view The View against which to invoke the method. 2825 * @return The name used of the View to be used to identify Views in Transitions or null 2826 * if no name has been given. 2827 */ getTransitionName(@onNull View view)2828 public static @Nullable String getTransitionName(@NonNull View view) { 2829 if (Build.VERSION.SDK_INT >= 21) { 2830 return Api21Impl.getTransitionName(view); 2831 } 2832 if (sTransitionNameMap == null) { 2833 return null; 2834 } 2835 return sTransitionNameMap.get(view); 2836 } 2837 2838 /** 2839 * Convenience method to add {@code overlay} to {@code overlayHost}'s 2840 * {@link View#getOverlay() overlay} and assign the 2841 * {@link ViewTree#setViewTreeDisjointParent(View, ViewParent) disjointParent} in the 2842 * overlay hierarchy. 2843 * 2844 * @param overlayHost The view to add an overlay to 2845 * @param overlay The view to overlay onto {@code overlayHost} 2846 * @see android.view.ViewGroupOverlay#add(View) 2847 */ addOverlayView(@onNull ViewGroup overlayHost, @NonNull View overlay)2848 public static void addOverlayView(@NonNull ViewGroup overlayHost, @NonNull View overlay) { 2849 overlayHost.getOverlay().add(overlay); 2850 ViewTree.setViewTreeDisjointParent((View) overlay.getParent(), overlayHost); 2851 } 2852 2853 /** 2854 * Returns the current system UI visibility that is currently set for the entire window. 2855 * 2856 * @param view view for which to get the visibility. 2857 * 2858 * @deprecated SystemUiVisibility flags are deprecated. Use 2859 * {@link WindowInsetsController} instead. 2860 */ 2861 @androidx.annotation.ReplaceWith(expression = "view.getWindowSystemUiVisibility()") 2862 @Deprecated getWindowSystemUiVisibility(@onNull View view)2863 public static int getWindowSystemUiVisibility(@NonNull View view) { 2864 return view.getWindowSystemUiVisibility(); 2865 } 2866 2867 /** 2868 * Ask that a new dispatch of {@code View.onApplyWindowInsets(WindowInsets)} be performed. This 2869 * falls back to {@code View.requestFitSystemWindows()} where available. 2870 * 2871 * @param view view for which to send the request. 2872 */ requestApplyInsets(@onNull View view)2873 public static void requestApplyInsets(@NonNull View view) { 2874 if (Build.VERSION.SDK_INT >= 20) { 2875 Api20Impl.requestApplyInsets(view); 2876 } else { 2877 view.requestFitSystemWindows(); 2878 } 2879 } 2880 2881 /** 2882 * Tells the ViewGroup whether to draw its children in the order defined by the method 2883 * {@code ViewGroup.getChildDrawingOrder(int, int)}. 2884 * 2885 * @param viewGroup the ViewGroup for which to set the mode. 2886 * @param enabled true if the order of the children when drawing is determined by 2887 * {@link ViewGroup#getChildDrawingOrder(int, int)}, false otherwise 2888 * 2889 * <p>Prior to API 7 this will have no effect.</p> 2890 * 2891 * @deprecated Use {@link ViewGroup#setChildrenDrawingOrderEnabled(boolean)} directly. 2892 */ 2893 @SuppressLint("BanUncheckedReflection") // Reflective access to bypass Java visibility 2894 @Deprecated setChildrenDrawingOrderEnabled(ViewGroup viewGroup, boolean enabled)2895 public static void setChildrenDrawingOrderEnabled(ViewGroup viewGroup, boolean enabled) { 2896 if (sChildrenDrawingOrderMethod == null) { 2897 try { 2898 sChildrenDrawingOrderMethod = ViewGroup.class 2899 .getDeclaredMethod("setChildrenDrawingOrderEnabled", boolean.class); 2900 } catch (NoSuchMethodException e) { 2901 Log.e(TAG, "Unable to find childrenDrawingOrderEnabled", e); 2902 } 2903 sChildrenDrawingOrderMethod.setAccessible(true); 2904 } 2905 try { 2906 sChildrenDrawingOrderMethod.invoke(viewGroup, enabled); 2907 } catch (IllegalAccessException e) { 2908 Log.e(TAG, "Unable to invoke childrenDrawingOrderEnabled", e); 2909 } catch (IllegalArgumentException e) { 2910 Log.e(TAG, "Unable to invoke childrenDrawingOrderEnabled", e); 2911 } catch (InvocationTargetException e) { 2912 Log.e(TAG, "Unable to invoke childrenDrawingOrderEnabled", e); 2913 } 2914 } 2915 2916 /** 2917 * Returns true if this view should adapt to fit system window insets. This method will always 2918 * return false before API 16 (Jellybean). 2919 * 2920 * @param view view for which to get the state. 2921 * @deprecated Call {@link View#getFitsSystemWindows()} directly. 2922 */ 2923 @androidx.annotation.ReplaceWith(expression = "view.getFitsSystemWindows()") 2924 @Deprecated getFitsSystemWindows(@onNull View view)2925 public static boolean getFitsSystemWindows(@NonNull View view) { 2926 return view.getFitsSystemWindows(); 2927 } 2928 2929 /** 2930 * Sets whether or not this view should account for system screen decorations 2931 * such as the status bar and inset its content; that is, controlling whether 2932 * the default implementation of {@link View#fitSystemWindows(Rect)} will be 2933 * executed. See that method for more details. 2934 * 2935 * @param view view for which to set the state. 2936 * @param fitSystemWindows whether or not this view should account for system screen 2937 * decorations. 2938 * 2939 * @deprecated Use {@link View#setFitsSystemWindows(boolean)} directly. 2940 */ 2941 @androidx.annotation.ReplaceWith(expression = "view.setFitsSystemWindows(fitSystemWindows)") 2942 @Deprecated setFitsSystemWindows(View view, boolean fitSystemWindows)2943 public static void setFitsSystemWindows(View view, boolean fitSystemWindows) { 2944 view.setFitsSystemWindows(fitSystemWindows); 2945 } 2946 2947 /** 2948 * On API 11 devices and above, call <code>Drawable.jumpToCurrentState()</code> 2949 * on all Drawable objects associated with this view. 2950 * <p> 2951 * On API 21 and above, also calls <code>StateListAnimator#jumpToCurrentState()</code> 2952 * if there is a StateListAnimator attached to this view. 2953 * 2954 * @param view view for which to jump the drawable state. 2955 * @deprecated Use {@link View#jumpDrawablesToCurrentState()} directly. 2956 */ 2957 @androidx.annotation.ReplaceWith(expression = "view.jumpDrawablesToCurrentState()") 2958 @Deprecated jumpDrawablesToCurrentState(View view)2959 public static void jumpDrawablesToCurrentState(View view) { 2960 view.jumpDrawablesToCurrentState(); 2961 } 2962 2963 /** 2964 * Set an {@link OnApplyWindowInsetsListener} to take over the policy for applying 2965 * window insets to this view. This will only take effect on devices with API 21 or above. 2966 * 2967 * @param view view on which to the listener. 2968 * @param listener listener for the applied window insets. 2969 */ setOnApplyWindowInsetsListener(final @NonNull View view, final @Nullable OnApplyWindowInsetsListener listener)2970 public static void setOnApplyWindowInsetsListener(final @NonNull View view, 2971 final @Nullable OnApplyWindowInsetsListener listener) { 2972 if (Build.VERSION.SDK_INT >= 21) { 2973 Api21Impl.setOnApplyWindowInsetsListener(view, listener); 2974 } 2975 } 2976 2977 /** 2978 * Called when the view should apply {@link WindowInsetsCompat} according to its internal policy. 2979 * 2980 * <p>Clients may supply an {@link OnApplyWindowInsetsListener} to a view. If one is set 2981 * it will be called during dispatch instead of this method. The listener may optionally 2982 * call this method from its own implementation if it wishes to apply the view's default 2983 * insets policy in addition to its own.</p> 2984 * 2985 * @param view The View against which to invoke the method. 2986 * @param insets Insets to apply 2987 * @return The supplied insets with any applied insets consumed 2988 */ onApplyWindowInsets(@onNull View view, @NonNull WindowInsetsCompat insets)2989 public static @NonNull WindowInsetsCompat onApplyWindowInsets(@NonNull View view, 2990 @NonNull WindowInsetsCompat insets) { 2991 if (Build.VERSION.SDK_INT >= 21) { 2992 final WindowInsets unwrapped = insets.toWindowInsets(); 2993 if (unwrapped != null) { 2994 WindowInsets result = Api20Impl.onApplyWindowInsets(view, unwrapped); 2995 if (!result.equals(unwrapped)) { 2996 // If the value changed, return a newly wrapped instance 2997 return WindowInsetsCompat.toWindowInsetsCompat(result, view); 2998 } 2999 } 3000 } 3001 return insets; 3002 } 3003 3004 /** 3005 * Request to apply the given window insets to this view or another view in its subtree. 3006 * 3007 * <p>This method should be called by clients wishing to apply insets corresponding to areas 3008 * obscured by window decorations or overlays. This can include the status and navigation bars, 3009 * action bars, input methods and more. New inset categories may be added in the future. 3010 * The method returns the insets provided minus any that were applied by this view or its 3011 * children.</p> 3012 * 3013 * @param view view for which to dispatch the request. 3014 * @param insets Insets to apply 3015 * @return The provided insets minus the insets that were consumed 3016 */ dispatchApplyWindowInsets(@onNull View view, @NonNull WindowInsetsCompat insets)3017 public static @NonNull WindowInsetsCompat dispatchApplyWindowInsets(@NonNull View view, 3018 @NonNull WindowInsetsCompat insets) { 3019 if (Build.VERSION.SDK_INT >= 21) { 3020 final WindowInsets unwrapped = insets.toWindowInsets(); 3021 if (unwrapped != null) { 3022 final WindowInsets result = Build.VERSION.SDK_INT >= 30 3023 ? Api30Impl.dispatchApplyWindowInsets(view, unwrapped) 3024 : Api20Impl.dispatchApplyWindowInsets(view, unwrapped); 3025 if (!result.equals(unwrapped)) { 3026 // If the value changed, return a newly wrapped instance 3027 return WindowInsetsCompat.toWindowInsetsCompat(result, view); 3028 } 3029 } 3030 } 3031 return insets; 3032 } 3033 3034 /** 3035 * Sets a list of areas within this view's post-layout coordinate space where the system 3036 * should not intercept touch or other pointing device gestures. <em>This method should 3037 * be called by {@link View#onLayout(boolean, int, int, int, int)} or 3038 * {@link View#onDraw(Canvas)}.</em> 3039 * <p> 3040 * On devices running API 28 and below, this method has no effect. 3041 * 3042 * @param view view for which to set the exclusion rects. 3043 * @param rects A list of precision gesture regions that this view needs to function correctly 3044 * @see View#setSystemGestureExclusionRects 3045 */ setSystemGestureExclusionRects(@onNull View view, @NonNull List<Rect> rects)3046 public static void setSystemGestureExclusionRects(@NonNull View view, 3047 @NonNull List<Rect> rects) { 3048 if (Build.VERSION.SDK_INT >= 29) { 3049 Api29Impl.setSystemGestureExclusionRects(view, rects); 3050 } 3051 } 3052 3053 /** 3054 * Retrieve the list of areas within this view's post-layout coordinate space where the system 3055 * should not intercept touch or other pointing device gestures. 3056 * <p> 3057 * On devices running API 28 and below, this method always returns an empty list. 3058 * 3059 * @param view view for which to get the exclusion rects. 3060 * 3061 * @see View#getSystemGestureExclusionRects 3062 */ getSystemGestureExclusionRects(@onNull View view)3063 public static @NonNull List<Rect> getSystemGestureExclusionRects(@NonNull View view) { 3064 if (Build.VERSION.SDK_INT >= 29) { 3065 return Api29Impl.getSystemGestureExclusionRects(view); 3066 } 3067 return Collections.emptyList(); 3068 } 3069 3070 /** 3071 * Provide original {@link WindowInsetsCompat} that are dispatched to the view hierarchy. 3072 * The insets are only available if the view is attached. 3073 * <p> 3074 * On devices running API 20 and below, this method always returns null. 3075 * 3076 * @return WindowInsetsCompat from the top of the view hierarchy or null if View is detached 3077 */ getRootWindowInsets(@onNull View view)3078 public static @Nullable WindowInsetsCompat getRootWindowInsets(@NonNull View view) { 3079 if (Build.VERSION.SDK_INT >= 23) { 3080 return Api23Impl.getRootWindowInsets(view); 3081 } else if (Build.VERSION.SDK_INT >= 21) { 3082 return Api21Impl.getRootWindowInsets(view); 3083 } else { 3084 return null; 3085 } 3086 } 3087 3088 /** 3089 * Compute insets that should be consumed by this view and the ones that should propagate 3090 * to those under it. 3091 * 3092 * @param view view for which insets need to be computed. 3093 * @param insets Insets currently being processed by this View, likely received as a parameter 3094 * to {@link View#onApplyWindowInsets(WindowInsets)}. 3095 * @param outLocalInsets A Rect that will receive the insets that should be consumed 3096 * by this view 3097 * @return Insets that should be passed along to views under this one 3098 */ computeSystemWindowInsets(@onNull View view, @NonNull WindowInsetsCompat insets, @NonNull Rect outLocalInsets)3099 public static @NonNull WindowInsetsCompat computeSystemWindowInsets(@NonNull View view, 3100 @NonNull WindowInsetsCompat insets, @NonNull Rect outLocalInsets) { 3101 if (Build.VERSION.SDK_INT >= 21) { 3102 return Api21Impl.computeSystemWindowInsets(view, insets, outLocalInsets); 3103 } 3104 return insets; 3105 } 3106 3107 /** 3108 * Retrieves a {@link WindowInsetsControllerCompat} of the window this view is attached to. 3109 * 3110 * @return A {@link WindowInsetsControllerCompat} or {@code null} if the view is neither 3111 * attached to a window nor a view tree with a decor. 3112 * @see WindowCompat#getInsetsController(Window, View) 3113 * @deprecated Prefer {@link WindowCompat#getInsetsController(Window, View)} to explicitly 3114 * specify the window (such as when the view is in a dialog). 3115 */ 3116 @Deprecated getWindowInsetsController( @onNull View view)3117 public static @Nullable WindowInsetsControllerCompat getWindowInsetsController( 3118 @NonNull View view) { 3119 if (Build.VERSION.SDK_INT >= 30) { 3120 return Api30Impl.getWindowInsetsController(view); 3121 } else { 3122 Context context = view.getContext(); 3123 while (context instanceof ContextWrapper) { 3124 if (context instanceof Activity) { 3125 Window window = ((Activity) context).getWindow(); 3126 return window != null ? WindowCompat.getInsetsController(window, view) : null; 3127 } 3128 context = ((ContextWrapper) context).getBaseContext(); 3129 } 3130 return null; 3131 } 3132 } 3133 3134 /** 3135 * Sets a {@link WindowInsetsAnimationCompat.Callback} to be notified about animations of 3136 * windows that cause insets. 3137 * <p> 3138 * The callback's {@link WindowInsetsAnimationCompat.Callback#getDispatchMode() 3139 * dispatch mode} will affect whether animation callbacks are dispatched to the children of 3140 * this view. 3141 * <p> 3142 * Prior to API 30, if an {@link OnApplyWindowInsetsListener} is used on the same 3143 * view, be sure to always use the {@link ViewCompat} version of 3144 * {@link #setOnApplyWindowInsetsListener(View, OnApplyWindowInsetsListener)}, otherwise the 3145 * listener will be overridden by this method. 3146 * <p> 3147 * The insets dispatch needs to reach this view for the listener to be called. If any view 3148 * consumed the insets earlier in the dispatch, this won't be called. 3149 * <p> 3150 * Prior to API 21, this method has no effect. 3151 * 3152 * @param view view for which to set the callback. 3153 * @param callback The callback to set, or <code>null</code> to remove the currently installed 3154 * callback 3155 */ setWindowInsetsAnimationCallback(@onNull View view, final WindowInsetsAnimationCompat.@Nullable Callback callback)3156 public static void setWindowInsetsAnimationCallback(@NonNull View view, 3157 final WindowInsetsAnimationCompat.@Nullable Callback callback) { 3158 WindowInsetsAnimationCompat.setCallback(view, callback); 3159 } 3160 3161 /** 3162 * Sets the listener to be used to handle insertion of content into the given view. 3163 * 3164 * <p>Depending on the type of view, this listener may be invoked for different scenarios. For 3165 * example, for an {@code AppCompatEditText}, this listener will be invoked for the following 3166 * scenarios: 3167 * <ol> 3168 * <li>Paste from the clipboard (e.g. "Paste" or "Paste as plain text" action in the 3169 * insertion/selection menu)</li> 3170 * <li>Content insertion from the keyboard (from {@link InputConnection#commitContent})</li> 3171 * <li>Drag and drop (drop events from {@link View#onDragEvent})</li> 3172 * </ol> 3173 * 3174 * <p>When setting a listener, clients must also declare the accepted MIME types. 3175 * The listener will still be invoked even if the MIME type of the content is not one of the 3176 * declared MIME types (e.g. if the user pastes content whose type is not one of the declared 3177 * MIME types). 3178 * In that case, the listener may reject the content (defer to the default platform behavior) 3179 * or execute some other fallback logic (e.g. show an appropriate message to the user). 3180 * The declared MIME types serve as a hint to allow different features to optionally alter 3181 * their behavior. For example, a soft keyboard may optionally choose to hide its UI for 3182 * inserting GIFs for a particular input field if the MIME types set here for that field 3183 * don't include "image/gif" or "image/*". 3184 * 3185 * <p>Note: MIME type matching in the Android framework is case-sensitive, unlike formal RFC 3186 * MIME types. As a result, you should always write your MIME types with lowercase letters, 3187 * or use {@link android.content.Intent#normalizeMimeType} to ensure that it is converted to 3188 * lowercase. 3189 * 3190 * @param view The target view. 3191 * @param mimeTypes The MIME types accepted by the given listener. These may use patterns 3192 * such as "image/*", but may not start with a wildcard. This argument must 3193 * not be null or empty if a non-null listener is passed in. 3194 * @param listener The listener to use. This can be null to reset to the default behavior. 3195 */ setOnReceiveContentListener(@onNull View view, String @Nullable [] mimeTypes, @Nullable OnReceiveContentListener listener)3196 public static void setOnReceiveContentListener(@NonNull View view, 3197 String @Nullable [] mimeTypes, @Nullable OnReceiveContentListener listener) { 3198 if (Build.VERSION.SDK_INT >= 31) { 3199 Api31Impl.setOnReceiveContentListener(view, mimeTypes, listener); 3200 return; 3201 } 3202 mimeTypes = (mimeTypes == null || mimeTypes.length == 0) ? null : mimeTypes; 3203 if (listener != null) { 3204 Preconditions.checkArgument(mimeTypes != null, 3205 "When the listener is set, MIME types must also be set"); 3206 } 3207 if (mimeTypes != null) { 3208 boolean hasLeadingWildcard = false; 3209 for (String mimeType : mimeTypes) { 3210 if (mimeType.startsWith("*")) { 3211 hasLeadingWildcard = true; 3212 break; 3213 } 3214 } 3215 Preconditions.checkArgument(!hasLeadingWildcard, 3216 "A MIME type set here must not start with *: " + Arrays.toString(mimeTypes)); 3217 } 3218 view.setTag(R.id.tag_on_receive_content_mime_types, mimeTypes); 3219 view.setTag(R.id.tag_on_receive_content_listener, listener); 3220 } 3221 3222 /** 3223 * Returns the MIME types accepted by the listener configured on the given view via 3224 * {@link #setOnReceiveContentListener}. By default returns null. 3225 * 3226 * <p>Different features (e.g. pasting from the clipboard, inserting stickers from the soft 3227 * keyboard, etc) may optionally use this metadata to conditionally alter their behavior. For 3228 * example, a soft keyboard may choose to hide its UI for inserting GIFs for a particular 3229 * input field if the MIME types returned here for that field don't include "image/gif" or 3230 * "image/*". 3231 * 3232 * <p>Note: Comparisons of MIME types should be performed using utilities such as 3233 * {@link ClipDescription#compareMimeTypes} rather than simple string equality, in order to 3234 * correctly handle patterns such as "text/*", "image/*", etc. Note that MIME type matching 3235 * in the Android framework is case-sensitive, unlike formal RFC MIME types. As a result, 3236 * you should always write your MIME types with lowercase letters, or use 3237 * {@link android.content.Intent#normalizeMimeType} to ensure that it is converted to 3238 * lowercase. 3239 * 3240 * @param view The target view. 3241 * 3242 * @return The MIME types accepted by the {@link OnReceiveContentListener} for the given view 3243 * (may include patterns such as "image/*"). 3244 */ getOnReceiveContentMimeTypes(@onNull View view)3245 public static String @Nullable [] getOnReceiveContentMimeTypes(@NonNull View view) { 3246 if (Build.VERSION.SDK_INT >= 31) { 3247 return Api31Impl.getReceiveContentMimeTypes(view); 3248 } 3249 return (String[]) view.getTag(R.id.tag_on_receive_content_mime_types); 3250 } 3251 3252 /** 3253 * Receives the given content. 3254 * 3255 * <p>If a listener is set, invokes the listener. If the listener returns a non-null result, 3256 * executes the fallback handling for the portion of the content returned by the listener. 3257 * 3258 * <p>If no listener is set, executes the fallback handling. 3259 * 3260 * <p>The fallback handling is defined by the target view if the view implements 3261 * {@link OnReceiveContentViewBehavior}, or is simply a no-op. 3262 * 3263 * @param view The target view. 3264 * @param payload The content to insert and related metadata. 3265 * 3266 * @return The portion of the passed-in content that was not handled (may be all, some, or none 3267 * of the passed-in content). 3268 */ performReceiveContent(@onNull View view, @NonNull ContentInfoCompat payload)3269 public static @Nullable ContentInfoCompat performReceiveContent(@NonNull View view, 3270 @NonNull ContentInfoCompat payload) { 3271 if (Log.isLoggable(TAG, Log.DEBUG)) { 3272 Log.d(TAG, "performReceiveContent: " + payload 3273 + ", view=" + view.getClass().getSimpleName() + "[" + view.getId() + "]"); 3274 } 3275 if (Build.VERSION.SDK_INT >= 31) { 3276 return Api31Impl.performReceiveContent(view, payload); 3277 } 3278 OnReceiveContentListener listener = 3279 (OnReceiveContentListener) view.getTag(R.id.tag_on_receive_content_listener); 3280 if (listener != null) { 3281 ContentInfoCompat remaining = listener.onReceiveContent(view, payload); 3282 return (remaining == null) ? null : getFallback(view).onReceiveContent(remaining); 3283 } 3284 return getFallback(view).onReceiveContent(payload); 3285 } 3286 getFallback(@onNull View view)3287 private static OnReceiveContentViewBehavior getFallback(@NonNull View view) { 3288 if (view instanceof OnReceiveContentViewBehavior) { 3289 return ((OnReceiveContentViewBehavior) view); 3290 } 3291 return NO_OP_ON_RECEIVE_CONTENT_VIEW_BEHAVIOR; 3292 } 3293 3294 private static final OnReceiveContentViewBehavior NO_OP_ON_RECEIVE_CONTENT_VIEW_BEHAVIOR = 3295 payload -> payload; 3296 3297 @RequiresApi(31) 3298 private static final class Api31Impl { Api31Impl()3299 private Api31Impl() {} 3300 setOnReceiveContentListener(@onNull View view, String @Nullable [] mimeTypes, final @Nullable OnReceiveContentListener listener)3301 public static void setOnReceiveContentListener(@NonNull View view, 3302 String @Nullable [] mimeTypes, final @Nullable OnReceiveContentListener listener) { 3303 if (listener == null) { 3304 view.setOnReceiveContentListener(mimeTypes, null); 3305 } else { 3306 view.setOnReceiveContentListener(mimeTypes, 3307 new OnReceiveContentListenerAdapter(listener)); 3308 } 3309 } 3310 getReceiveContentMimeTypes(@onNull View view)3311 public static String @Nullable [] getReceiveContentMimeTypes(@NonNull View view) { 3312 return view.getReceiveContentMimeTypes(); 3313 } 3314 performReceiveContent(@onNull View view, @NonNull ContentInfoCompat payload)3315 public static @Nullable ContentInfoCompat performReceiveContent(@NonNull View view, 3316 @NonNull ContentInfoCompat payload) { 3317 ContentInfo platPayload = payload.toContentInfo(); 3318 ContentInfo platResult = view.performReceiveContent(platPayload); 3319 if (platResult == null) { 3320 return null; 3321 } 3322 if (platResult == platPayload) { 3323 // Avoid unnecessary conversion when returning the original payload unchanged. 3324 return payload; 3325 } 3326 return ContentInfoCompat.toContentInfoCompat(platResult); 3327 } 3328 } 3329 3330 @RequiresApi(31) 3331 private static final class OnReceiveContentListenerAdapter implements 3332 android.view.OnReceiveContentListener { 3333 3334 private final @NonNull OnReceiveContentListener mJetpackListener; 3335 OnReceiveContentListenerAdapter(@onNull OnReceiveContentListener jetpackListener)3336 OnReceiveContentListenerAdapter(@NonNull OnReceiveContentListener jetpackListener) { 3337 mJetpackListener = jetpackListener; 3338 } 3339 3340 @Override onReceiveContent(@onNull View view, @NonNull ContentInfo platPayload)3341 public @Nullable ContentInfo onReceiveContent(@NonNull View view, 3342 @NonNull ContentInfo platPayload) { 3343 ContentInfoCompat payload = ContentInfoCompat.toContentInfoCompat(platPayload); 3344 ContentInfoCompat result = mJetpackListener.onReceiveContent(view, payload); 3345 if (result == null) { 3346 return null; 3347 } 3348 if (result == payload) { 3349 // Avoid unnecessary conversion when returning the original payload unchanged. 3350 return platPayload; 3351 } 3352 return result.toContentInfo(); 3353 } 3354 } 3355 3356 /** 3357 * Controls whether the entire hierarchy under this view will save its 3358 * state when a state saving traversal occurs from its parent. 3359 * 3360 * @param view view for which to set the state. 3361 * @param enabled Set to false to <em>disable</em> state saving, or true 3362 * (the default) to allow it. 3363 * 3364 * @deprecated Use {@link View#setSaveFromParentEnabled(boolean)} directly. 3365 */ 3366 @androidx.annotation.ReplaceWith(expression = "view.setSaveFromParentEnabled(enabled)") 3367 @Deprecated setSaveFromParentEnabled(View view, boolean enabled)3368 public static void setSaveFromParentEnabled(View view, boolean enabled) { 3369 view.setSaveFromParentEnabled(enabled); 3370 } 3371 3372 /** 3373 * Changes the activated state of this view. A view can be activated or not. 3374 * Note that activation is not the same as selection. Selection is 3375 * a transient property, representing the view (hierarchy) the user is 3376 * currently interacting with. Activation is a longer-term state that the 3377 * user can move views in and out of. 3378 * 3379 * @param view view for which to set the state. 3380 * @param activated true if the view must be activated, false otherwise 3381 * 3382 * @deprecated Use {@link View#setActivated(boolean)} directly. 3383 */ 3384 @androidx.annotation.ReplaceWith(expression = "view.setActivated(activated)") 3385 @Deprecated setActivated(View view, boolean activated)3386 public static void setActivated(View view, boolean activated) { 3387 view.setActivated(activated); 3388 } 3389 3390 /** 3391 * Returns whether this View has content which overlaps. 3392 * 3393 * <p>This function, intended to be overridden by specific View types, is an optimization when 3394 * alpha is set on a view. If rendering overlaps in a view with alpha < 1, that view is drawn to 3395 * an offscreen buffer and then composited into place, which can be expensive. If the view has 3396 * no overlapping rendering, the view can draw each primitive with the appropriate alpha value 3397 * directly. An example of overlapping rendering is a TextView with a background image, such as 3398 * a Button. An example of non-overlapping rendering is a TextView with no background, or an 3399 * ImageView with only the foreground image. The default implementation returns true; subclasses 3400 * should override if they have cases which can be optimized.</p> 3401 * 3402 * @param view view for which to get the state. 3403 * @return true if the content in this view might overlap, false otherwise. 3404 * @deprecated Call {@link View#hasOverlappingRendering()} directly. 3405 */ 3406 @androidx.annotation.ReplaceWith(expression = "view.hasOverlappingRendering()") 3407 @Deprecated hasOverlappingRendering(@onNull View view)3408 public static boolean hasOverlappingRendering(@NonNull View view) { 3409 return view.hasOverlappingRendering(); 3410 } 3411 3412 /** 3413 * Return if the padding as been set through relative values 3414 * {@code View.setPaddingRelative(int, int, int, int)} or thru 3415 * 3416 * @param view view for which to get the state. 3417 * @return true if the padding is relative or false if it is not. 3418 * @deprecated Call {@link View#isPaddingRelative()} directly. 3419 */ 3420 @androidx.annotation.ReplaceWith(expression = "view.isPaddingRelative()") 3421 @Deprecated isPaddingRelative(@onNull View view)3422 public static boolean isPaddingRelative(@NonNull View view) { 3423 return view.isPaddingRelative(); 3424 } 3425 3426 /** 3427 * Set the background of the {@code view} to a given Drawable, or remove the background. If the 3428 * background has padding, {@code view}'s padding is set to the background's padding. However, 3429 * when a background is removed, this View's padding isn't touched. If setting the padding is 3430 * desired, please use {@code setPadding(int, int, int, int)}. 3431 * @param view view for which to set the background. 3432 * @param background the drawable to use as view background. 3433 * @deprecated Call {@link View#setBackground(Drawable)} directly. 3434 */ 3435 @androidx.annotation.ReplaceWith(expression = "view.setBackground(background)") 3436 @Deprecated setBackground(@onNull View view, @Nullable Drawable background)3437 public static void setBackground(@NonNull View view, @Nullable Drawable background) { 3438 view.setBackground(background); 3439 } 3440 3441 /** 3442 * Return the tint applied to the background drawable, if specified. 3443 * <p> 3444 * Only returns meaningful info when running on API v21 or newer, or if {@code view} 3445 * implements the {@code TintableBackgroundView} interface. 3446 */ getBackgroundTintList(@onNull View view)3447 public static @Nullable ColorStateList getBackgroundTintList(@NonNull View view) { 3448 if (Build.VERSION.SDK_INT >= 21) { 3449 return Api21Impl.getBackgroundTintList(view); 3450 } 3451 return (view instanceof TintableBackgroundView) 3452 ? ((TintableBackgroundView) view).getSupportBackgroundTintList() 3453 : null; 3454 } 3455 3456 /** 3457 * Applies a tint to the background drawable. 3458 * <p> 3459 * This will always take effect when running on API v21 or newer. When running on platforms 3460 * previous to API v21, it will only take effect if {@code view} implements the 3461 * {@code TintableBackgroundView} interface. 3462 */ setBackgroundTintList(@onNull View view, @Nullable ColorStateList tintList)3463 public static void setBackgroundTintList(@NonNull View view, 3464 @Nullable ColorStateList tintList) { 3465 if (Build.VERSION.SDK_INT >= 21) { 3466 Api21Impl.setBackgroundTintList(view, tintList); 3467 3468 if (Build.VERSION.SDK_INT == 21) { 3469 // Work around a bug in L that did not update the state of the background 3470 // after applying the tint 3471 Drawable background = view.getBackground(); 3472 boolean hasTint = (Api21Impl.getBackgroundTintList(view) != null) 3473 || (Api21Impl.getBackgroundTintMode(view) != null); 3474 if ((background != null) && hasTint) { 3475 if (background.isStateful()) { 3476 background.setState(view.getDrawableState()); 3477 } 3478 view.setBackground(background); 3479 } 3480 } 3481 } else if (view instanceof TintableBackgroundView) { 3482 ((TintableBackgroundView) view).setSupportBackgroundTintList(tintList); 3483 } 3484 } 3485 3486 /** 3487 * Return the blending mode used to apply the tint to the background 3488 * drawable, if specified. 3489 * <p> 3490 * Only returns meaningful info when running on API v21 or newer, or if {@code view} 3491 * implements the {@code TintableBackgroundView} interface. 3492 */ getBackgroundTintMode(@onNull View view)3493 public static PorterDuff.@Nullable Mode getBackgroundTintMode(@NonNull View view) { 3494 if (Build.VERSION.SDK_INT >= 21) { 3495 return Api21Impl.getBackgroundTintMode(view); 3496 } 3497 return (view instanceof TintableBackgroundView) 3498 ? ((TintableBackgroundView) view).getSupportBackgroundTintMode() 3499 : null; 3500 } 3501 3502 /** 3503 * Specifies the blending mode used to apply the tint specified by 3504 * {@link #setBackgroundTintList(android.view.View, android.content.res.ColorStateList)} to 3505 * the background drawable. The default mode is {@link PorterDuff.Mode#SRC_IN}. 3506 * <p> 3507 * This will always take effect when running on API v21 or newer. When running on platforms 3508 * previous to API v21, it will only take effect if {@code view} implement the 3509 * {@code TintableBackgroundView} interface. 3510 */ setBackgroundTintMode(@onNull View view, PorterDuff.@Nullable Mode mode)3511 public static void setBackgroundTintMode(@NonNull View view, PorterDuff.@Nullable Mode mode) { 3512 if (Build.VERSION.SDK_INT >= 21) { 3513 Api21Impl.setBackgroundTintMode(view, mode); 3514 3515 if (Build.VERSION.SDK_INT == 21) { 3516 // Work around a bug in L that did not update the state of the background 3517 // after applying the tint 3518 Drawable background = view.getBackground(); 3519 boolean hasTint = (Api21Impl.getBackgroundTintList(view) != null) 3520 || (Api21Impl.getBackgroundTintMode(view) != null); 3521 if ((background != null) && hasTint) { 3522 if (background.isStateful()) { 3523 background.setState(view.getDrawableState()); 3524 } 3525 view.setBackground(background); 3526 } 3527 } 3528 } else if (view instanceof TintableBackgroundView) { 3529 ((TintableBackgroundView) view).setSupportBackgroundTintMode(mode); 3530 } 3531 } 3532 3533 // TODO: getters for various view properties (rotation, etc) 3534 3535 /** 3536 * Enable or disable nested scrolling for this view. 3537 * 3538 * <p>If this property is set to true the view will be permitted to initiate nested 3539 * scrolling operations with a compatible parent view in the current hierarchy. If this 3540 * view does not implement nested scrolling this will have no effect. Disabling nested scrolling 3541 * while a nested scroll is in progress has the effect of 3542 * {@link #stopNestedScroll(View) stopping} the nested scroll.</p> 3543 * 3544 * @param view view for which to set the state. 3545 * @param enabled true to enable nested scrolling, false to disable 3546 * 3547 * @see #isNestedScrollingEnabled(View) 3548 */ 3549 @SuppressWarnings("RedundantCast") // Intentionally invoking interface method. setNestedScrollingEnabled(@onNull View view, boolean enabled)3550 public static void setNestedScrollingEnabled(@NonNull View view, boolean enabled) { 3551 if (Build.VERSION.SDK_INT >= 21) { 3552 Api21Impl.setNestedScrollingEnabled(view, enabled); 3553 } else { 3554 if (view instanceof NestedScrollingChild) { 3555 ((NestedScrollingChild) view).setNestedScrollingEnabled(enabled); 3556 } 3557 } 3558 } 3559 3560 /** 3561 * Returns true if nested scrolling is enabled for this view. 3562 * 3563 * <p>If nested scrolling is enabled and this View class implementation supports it, 3564 * this view will act as a nested scrolling child view when applicable, forwarding data 3565 * about the scroll operation in progress to a compatible and cooperating nested scrolling 3566 * parent.</p> 3567 * 3568 * @return true if nested scrolling is enabled 3569 * 3570 * @see #setNestedScrollingEnabled(View, boolean) 3571 */ 3572 @SuppressWarnings("RedundantCast") // Intentionally invoking interface method. isNestedScrollingEnabled(@onNull View view)3573 public static boolean isNestedScrollingEnabled(@NonNull View view) { 3574 if (Build.VERSION.SDK_INT >= 21) { 3575 return Api21Impl.isNestedScrollingEnabled(view); 3576 } 3577 if (view instanceof NestedScrollingChild) { 3578 return ((NestedScrollingChild) view).isNestedScrollingEnabled(); 3579 } 3580 return false; 3581 } 3582 3583 /** 3584 * Begin a nestable scroll operation along the given axes. 3585 * 3586 * <p>This version of the method just calls {@link #startNestedScroll(View, int, int)} using 3587 * the touch input type.</p> 3588 * 3589 * @param view view for which to start the scroll. 3590 * @param axes Flags consisting of a combination of {@link ViewCompat#SCROLL_AXIS_HORIZONTAL} 3591 * and/or {@link ViewCompat#SCROLL_AXIS_VERTICAL}. 3592 * @return true if a cooperative parent was found and nested scrolling has been enabled for 3593 * the current gesture. 3594 */ 3595 @SuppressWarnings("RedundantCast") // Intentionally invoking interface method. startNestedScroll(@onNull View view, @ScrollAxis int axes)3596 public static boolean startNestedScroll(@NonNull View view, @ScrollAxis int axes) { 3597 if (Build.VERSION.SDK_INT >= 21) { 3598 return Api21Impl.startNestedScroll(view, axes); 3599 } 3600 if (view instanceof NestedScrollingChild) { 3601 return ((NestedScrollingChild) view).startNestedScroll(axes); 3602 } 3603 return false; 3604 } 3605 3606 /** 3607 * Stop a nested scroll in progress. 3608 * 3609 * <p>This version of the method just calls {@link #stopNestedScroll(View, int)} using the 3610 * touch input type.</p> 3611 * 3612 * @param view view for which to stop the scroll. 3613 * 3614 * @see #startNestedScroll(View, int) 3615 */ 3616 @SuppressWarnings("RedundantCast") // Intentionally invoking interface method. stopNestedScroll(@onNull View view)3617 public static void stopNestedScroll(@NonNull View view) { 3618 if (Build.VERSION.SDK_INT >= 21) { 3619 Api21Impl.stopNestedScroll(view); 3620 } else if (view instanceof NestedScrollingChild) { 3621 ((NestedScrollingChild) view).stopNestedScroll(); 3622 } 3623 } 3624 3625 /** 3626 * Returns true if this view has a nested scrolling parent. 3627 * 3628 * <p>This version of the method just calls {@link #hasNestedScrollingParent(View, int)} 3629 * using the touch input type.</p> 3630 * 3631 * @param view view for which to check the parent. 3632 * @return whether this view has a nested scrolling parent 3633 */ 3634 @SuppressWarnings("RedundantCast") // Intentionally invoking interface method. hasNestedScrollingParent(@onNull View view)3635 public static boolean hasNestedScrollingParent(@NonNull View view) { 3636 if (Build.VERSION.SDK_INT >= 21) { 3637 return Api21Impl.hasNestedScrollingParent(view); 3638 } 3639 if (view instanceof NestedScrollingChild) { 3640 return ((NestedScrollingChild) view).hasNestedScrollingParent(); 3641 } 3642 return false; 3643 } 3644 3645 /** 3646 * Dispatch one step of a nested scroll in progress. 3647 * 3648 * <p>This version of the method just calls 3649 * {@link #dispatchNestedScroll(View, int, int, int, int, int[], int)} using the touch input 3650 * type.</p> 3651 * 3652 * @param view view for which to dispatch the scroll. 3653 * @param dxConsumed Horizontal distance in pixels consumed by this view during this scroll step 3654 * @param dyConsumed Vertical distance in pixels consumed by this view during this scroll step 3655 * @param dxUnconsumed Horizontal scroll distance in pixels not consumed by this view 3656 * @param dyUnconsumed Horizontal scroll distance in pixels not consumed by this view 3657 * @param offsetInWindow Optional. If not null, on return this will contain the offset 3658 * in local view coordinates of this view from before this operation 3659 * to after it completes. View implementations may use this to adjust 3660 * expected input coordinate tracking. 3661 * @return true if the event was dispatched, false if it could not be dispatched. 3662 */ 3663 @SuppressWarnings("RedundantCast") // Intentionally invoking interface method. dispatchNestedScroll(@onNull View view, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int @Nullable [] offsetInWindow)3664 public static boolean dispatchNestedScroll(@NonNull View view, int dxConsumed, int dyConsumed, 3665 int dxUnconsumed, int dyUnconsumed, int @Nullable [] offsetInWindow) { 3666 if (Build.VERSION.SDK_INT >= 21) { 3667 return Api21Impl.dispatchNestedScroll(view, dxConsumed, dyConsumed, dxUnconsumed, 3668 dyUnconsumed, offsetInWindow); 3669 } 3670 if (view instanceof NestedScrollingChild) { 3671 return ((NestedScrollingChild) view).dispatchNestedScroll(dxConsumed, dyConsumed, 3672 dxUnconsumed, dyUnconsumed, offsetInWindow); 3673 } 3674 return false; 3675 } 3676 3677 /** 3678 * Dispatch one step of a nested scroll in progress before this view consumes any portion of it. 3679 * 3680 * <p>This version of the method just calls 3681 * {@link #dispatchNestedPreScroll(View, int, int, int[], int[], int)} using the touch input 3682 * type.</p> 3683 * 3684 * @param view view for which to dispatch the scroll. 3685 * @param dx Horizontal scroll distance in pixels 3686 * @param dy Vertical scroll distance in pixels 3687 * @param consumed Output. If not null, consumed[0] will contain the consumed component of dx 3688 * and consumed[1] the consumed dy. 3689 * @param offsetInWindow Optional. If not null, on return this will contain the offset 3690 * in local view coordinates of this view from before this operation 3691 * to after it completes. View implementations may use this to adjust 3692 * expected input coordinate tracking. 3693 * @return true if the parent consumed some or all of the scroll delta 3694 */ 3695 @SuppressWarnings("RedundantCast") // Intentionally invoking interface method. dispatchNestedPreScroll(@onNull View view, int dx, int dy, int @Nullable [] consumed, int @Nullable [] offsetInWindow)3696 public static boolean dispatchNestedPreScroll(@NonNull View view, int dx, int dy, 3697 int @Nullable [] consumed, int @Nullable [] offsetInWindow) { 3698 if (Build.VERSION.SDK_INT >= 21) { 3699 return Api21Impl.dispatchNestedPreScroll(view, dx, dy, consumed, offsetInWindow); 3700 } 3701 if (view instanceof NestedScrollingChild) { 3702 return ((NestedScrollingChild) view).dispatchNestedPreScroll(dx, dy, consumed, 3703 offsetInWindow); 3704 } 3705 return false; 3706 } 3707 3708 /** 3709 * Begin a nestable scroll operation along the given axes. 3710 * 3711 * <p>A view starting a nested scroll promises to abide by the following contract:</p> 3712 * 3713 * <p>The view will call startNestedScroll upon initiating a scroll operation. In the case 3714 * of a touch scroll this corresponds to the initial {@link MotionEvent#ACTION_DOWN}. 3715 * In the case of touch scrolling the nested scroll will be terminated automatically in 3716 * the same manner as {@link ViewParent#requestDisallowInterceptTouchEvent(boolean)}. 3717 * In the event of programmatic scrolling the caller must explicitly call 3718 * {@link #stopNestedScroll(View)} to indicate the end of the nested scroll.</p> 3719 * 3720 * <p>If <code>startNestedScroll</code> returns true, a cooperative parent was found. 3721 * If it returns false the caller may ignore the rest of this contract until the next scroll. 3722 * Calling startNestedScroll while a nested scroll is already in progress will return true.</p> 3723 * 3724 * <p>At each incremental step of the scroll the caller should invoke 3725 * {@link #dispatchNestedPreScroll(View, int, int, int[], int[]) dispatchNestedPreScroll} 3726 * once it has calculated the requested scrolling delta. If it returns true the nested scrolling 3727 * parent at least partially consumed the scroll and the caller should adjust the amount it 3728 * scrolls by.</p> 3729 * 3730 * <p>After applying the remainder of the scroll delta the caller should invoke 3731 * {@link #dispatchNestedScroll(View, int, int, int, int, int[]) dispatchNestedScroll}, passing 3732 * both the delta consumed and the delta unconsumed. A nested scrolling parent may treat 3733 * these values differently. See 3734 * {@link NestedScrollingParent#onNestedScroll(View, int, int, int, int)}. 3735 * </p> 3736 * 3737 * @param view view on which to start the scroll. 3738 * @param axes Flags consisting of a combination of {@link ViewCompat#SCROLL_AXIS_HORIZONTAL} 3739 * and/or {@link ViewCompat#SCROLL_AXIS_VERTICAL}. 3740 * @param type the type of input which cause this scroll event 3741 * @return true if a cooperative parent was found and nested scrolling has been enabled for 3742 * the current gesture. 3743 * 3744 * @see #stopNestedScroll(View) 3745 * @see #dispatchNestedPreScroll(View, int, int, int[], int[]) 3746 * @see #dispatchNestedScroll(View, int, int, int, int, int[]) 3747 */ startNestedScroll(@onNull View view, @ScrollAxis int axes, @NestedScrollType int type)3748 public static boolean startNestedScroll(@NonNull View view, @ScrollAxis int axes, 3749 @NestedScrollType int type) { 3750 if (view instanceof NestedScrollingChild2) { 3751 return ((NestedScrollingChild2) view).startNestedScroll(axes, type); 3752 } else if (type == ViewCompat.TYPE_TOUCH) { 3753 return startNestedScroll(view, axes); 3754 } 3755 return false; 3756 } 3757 3758 /** 3759 * Stop a nested scroll in progress. 3760 * 3761 * <p>Calling this method when a nested scroll is not currently in progress is harmless.</p> 3762 * 3763 * @param view view for which to stop the scroll. 3764 * @param type the type of input which cause this scroll event 3765 * @see #startNestedScroll(View, int) 3766 */ stopNestedScroll(@onNull View view, @NestedScrollType int type)3767 public static void stopNestedScroll(@NonNull View view, @NestedScrollType int type) { 3768 if (view instanceof NestedScrollingChild2) { 3769 ((NestedScrollingChild2) view).stopNestedScroll(type); 3770 } else if (type == ViewCompat.TYPE_TOUCH) { 3771 stopNestedScroll(view); 3772 } 3773 } 3774 3775 /** 3776 * Returns true if this view has a nested scrolling parent. 3777 * 3778 * <p>The presence of a nested scrolling parent indicates that this view has initiated 3779 * a nested scroll and it was accepted by an ancestor view further up the view hierarchy.</p> 3780 * 3781 * @param view view for which to check the parent. 3782 * @param type the type of input which cause this scroll event 3783 * @return whether this view has a nested scrolling parent 3784 */ hasNestedScrollingParent(@onNull View view, @NestedScrollType int type)3785 public static boolean hasNestedScrollingParent(@NonNull View view, @NestedScrollType int type) { 3786 if (view instanceof NestedScrollingChild2) { 3787 ((NestedScrollingChild2) view).hasNestedScrollingParent(type); 3788 } else if (type == ViewCompat.TYPE_TOUCH) { 3789 return hasNestedScrollingParent(view); 3790 } 3791 return false; 3792 } 3793 3794 /** 3795 * Dispatch one step of a nested scroll in progress. 3796 * 3797 * <p>Implementations of views that support nested scrolling should call this to report 3798 * info about a scroll in progress to the current nested scrolling parent. If a nested scroll 3799 * is not currently in progress or nested scrolling is not 3800 * {@link #isNestedScrollingEnabled(View) enabled} for this view this method does nothing.</p> 3801 * 3802 * <p>Compatible View implementations should also call 3803 * {@link #dispatchNestedPreScroll(View, int, int, int[], int[], int) dispatchNestedPreScroll} 3804 * before consuming a component of the scroll event themselves. 3805 * 3806 * <p>A non-null <code>consumed</code> int array of length 2 may be passed in to enable nested 3807 * scrolling parents to report how much of the scroll distance was consumed. The original 3808 * caller (where the input event was received to start the scroll) should initialize the values 3809 * to be 0, in order to tell how much was actually consumed up the hierarchy of scrolling 3810 * parents. 3811 * 3812 * @param view view for which to dispatch the scroll. 3813 * @param dxConsumed Horizontal distance in pixels consumed by this view during this scroll step 3814 * @param dyConsumed Vertical distance in pixels consumed by this view during this scroll step 3815 * @param dxUnconsumed Horizontal scroll distance in pixels not consumed by this view 3816 * @param dyUnconsumed Horizontal scroll distance in pixels not consumed by this view 3817 * @param offsetInWindow Optional. If not null, on return this will contain the offset 3818 * in local view coordinates of this view from before this operation 3819 * to after it completes. View implementations may use this to adjust 3820 * expected input coordinate tracking. 3821 * @param type the type of input which cause this scroll event 3822 * @param consumed Output, If not null, <code>consumed[0]</code> will contain the consumed 3823 * component of dx and <code>consumed[1]</code> the consumed dy. 3824 */ dispatchNestedScroll(@onNull View view, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int @Nullable [] offsetInWindow, @NestedScrollType int type, int @NonNull [] consumed)3825 public static void dispatchNestedScroll(@NonNull View view, int dxConsumed, int dyConsumed, 3826 int dxUnconsumed, int dyUnconsumed, int @Nullable [] offsetInWindow, 3827 @NestedScrollType int type, int @NonNull [] consumed) { 3828 if (view instanceof NestedScrollingChild3) { 3829 ((NestedScrollingChild3) view).dispatchNestedScroll(dxConsumed, dyConsumed, 3830 dxUnconsumed, dyUnconsumed, offsetInWindow, type, consumed); 3831 } else { 3832 dispatchNestedScroll(view, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, 3833 offsetInWindow, type); 3834 } 3835 } 3836 3837 /** 3838 * Dispatch one step of a nested scroll in progress. 3839 * 3840 * <p>Implementations of views that support nested scrolling should call this to report 3841 * info about a scroll in progress to the current nested scrolling parent. If a nested scroll 3842 * is not currently in progress or nested scrolling is not 3843 * {@link #isNestedScrollingEnabled(View) enabled} for this view this method does nothing. 3844 * 3845 * <p>Compatible View implementations should also call 3846 * {@link #dispatchNestedPreScroll(View, int, int, int[], int[]) dispatchNestedPreScroll} before 3847 * consuming a component of the scroll event themselves. 3848 * 3849 * @param view view for which to dispatch the scroll. 3850 * @param dxConsumed Horizontal distance in pixels consumed by this view during this scroll step 3851 * @param dyConsumed Vertical distance in pixels consumed by this view during this scroll step 3852 * @param dxUnconsumed Horizontal scroll distance in pixels not consumed by this view 3853 * @param dyUnconsumed Horizontal scroll distance in pixels not consumed by this view 3854 * @param offsetInWindow Optional. If not null, on return this will contain the offset 3855 * in local view coordinates of this view from before this operation 3856 * to after it completes. View implementations may use this to adjust 3857 * expected input coordinate tracking. 3858 * @param type the type of input which cause this scroll event 3859 * @return true if the event was dispatched, and therefore the scroll distance was consumed 3860 * @see #dispatchNestedPreScroll(View, int, int, int[], int[]) 3861 */ dispatchNestedScroll(@onNull View view, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int @Nullable [] offsetInWindow, @NestedScrollType int type)3862 public static boolean dispatchNestedScroll(@NonNull View view, int dxConsumed, int dyConsumed, 3863 int dxUnconsumed, int dyUnconsumed, int @Nullable [] offsetInWindow, 3864 @NestedScrollType int type) { 3865 if (view instanceof NestedScrollingChild2) { 3866 return ((NestedScrollingChild2) view).dispatchNestedScroll(dxConsumed, dyConsumed, 3867 dxUnconsumed, dyUnconsumed, offsetInWindow, type); 3868 } else if (type == ViewCompat.TYPE_TOUCH) { 3869 return dispatchNestedScroll(view, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, 3870 offsetInWindow); 3871 } 3872 return false; 3873 } 3874 3875 /** 3876 * Dispatch one step of a nested scroll in progress before this view consumes any portion of it. 3877 * 3878 * <p>Nested pre-scroll events are to nested scroll events what touch intercept is to touch. 3879 * <code>dispatchNestedPreScroll</code> offers an opportunity for the parent view in a nested 3880 * scrolling operation to consume some or all of the scroll operation before the child view 3881 * consumes it.</p> 3882 * 3883 * @param view view for which to dispatch the scroll. 3884 * @param dx Horizontal scroll distance in pixels 3885 * @param dy Vertical scroll distance in pixels 3886 * @param consumed Output. If not null, consumed[0] will contain the consumed component of dx 3887 * and consumed[1] the consumed dy. 3888 * @param offsetInWindow Optional. If not null, on return this will contain the offset 3889 * in local view coordinates of this view from before this operation 3890 * to after it completes. View implementations may use this to adjust 3891 * expected input coordinate tracking. 3892 * @param type the type of input which cause this scroll event 3893 * @return true if the parent consumed some or all of the scroll delta 3894 * @see #dispatchNestedScroll(View, int, int, int, int, int[]) 3895 */ dispatchNestedPreScroll(@onNull View view, int dx, int dy, int @Nullable [] consumed, int @Nullable [] offsetInWindow, @NestedScrollType int type)3896 public static boolean dispatchNestedPreScroll(@NonNull View view, int dx, int dy, 3897 int @Nullable [] consumed, int @Nullable [] offsetInWindow, 3898 @NestedScrollType int type) { 3899 if (view instanceof NestedScrollingChild2) { 3900 return ((NestedScrollingChild2) view).dispatchNestedPreScroll(dx, dy, consumed, 3901 offsetInWindow, type); 3902 } else if (type == ViewCompat.TYPE_TOUCH) { 3903 return dispatchNestedPreScroll(view, dx, dy, consumed, offsetInWindow); 3904 } 3905 return false; 3906 } 3907 3908 /** 3909 * Dispatch a fling to a nested scrolling parent. 3910 * 3911 * <p>This method should be used to indicate that a nested scrolling child has detected 3912 * suitable conditions for a fling. Generally this means that a touch scroll has ended with a 3913 * {@link VelocityTracker velocity} in the direction of scrolling that meets or exceeds 3914 * the {@link ViewConfiguration#getScaledMinimumFlingVelocity() minimum fling velocity} 3915 * along a scrollable axis.</p> 3916 * 3917 * <p>If a nested scrolling child view would normally fling but it is at the edge of 3918 * its own content, it can use this method to delegate the fling to its nested scrolling 3919 * parent instead. The parent may optionally consume the fling or observe a child fling.</p> 3920 * 3921 * @param view view for which to dispatch the fling. 3922 * @param velocityX Horizontal fling velocity in pixels per second 3923 * @param velocityY Vertical fling velocity in pixels per second 3924 * @param consumed true if the child consumed the fling, false otherwise 3925 * @return true if the nested scrolling parent consumed or otherwise reacted to the fling 3926 */ 3927 @SuppressWarnings("RedundantCast") // Intentionally invoking interface method. dispatchNestedFling(@onNull View view, float velocityX, float velocityY, boolean consumed)3928 public static boolean dispatchNestedFling(@NonNull View view, float velocityX, float velocityY, 3929 boolean consumed) { 3930 if (Build.VERSION.SDK_INT >= 21) { 3931 return Api21Impl.dispatchNestedFling(view, velocityX, velocityY, consumed); 3932 } 3933 if (view instanceof NestedScrollingChild) { 3934 return ((NestedScrollingChild) view).dispatchNestedFling(velocityX, velocityY, 3935 consumed); 3936 } 3937 return false; 3938 } 3939 3940 /** 3941 * Dispatch a fling to a nested scrolling parent before it is processed by this view. 3942 * 3943 * <p>Nested pre-fling events are to nested fling events what touch intercept is to touch 3944 * and what nested pre-scroll is to nested scroll. <code>dispatchNestedPreFling</code> 3945 * offsets an opportunity for the parent view in a nested fling to fully consume the fling 3946 * before the child view consumes it. If this method returns <code>true</code>, a nested 3947 * parent view consumed the fling and this view should not scroll as a result.</p> 3948 * 3949 * <p>For a better user experience, only one view in a nested scrolling chain should consume 3950 * the fling at a time. If a parent view consumed the fling this method will return false. 3951 * Custom view implementations should account for this in two ways:</p> 3952 * 3953 * <ul> 3954 * <li>If a custom view is paged and needs to settle to a fixed page-point, do not 3955 * call <code>dispatchNestedPreFling</code>; consume the fling and settle to a valid 3956 * position regardless.</li> 3957 * <li>If a nested parent does consume the fling, this view should not scroll at all, 3958 * even to settle back to a valid idle position.</li> 3959 * </ul> 3960 * 3961 * <p>Views should also not offer fling velocities to nested parent views along an axis 3962 * where scrolling is not currently supported; a {@link android.widget.ScrollView ScrollView} 3963 * should not offer a horizontal fling velocity to its parents since scrolling along that 3964 * axis is not permitted and carrying velocity along that motion does not make sense.</p> 3965 * 3966 * @param view view for which to dispatch the fling. 3967 * @param velocityX Horizontal fling velocity in pixels per second 3968 * @param velocityY Vertical fling velocity in pixels per second 3969 * @return true if a nested scrolling parent consumed the fling 3970 */ 3971 @SuppressWarnings("RedundantCast") // Intentionally invoking interface method. dispatchNestedPreFling(@onNull View view, float velocityX, float velocityY)3972 public static boolean dispatchNestedPreFling(@NonNull View view, float velocityX, 3973 float velocityY) { 3974 if (Build.VERSION.SDK_INT >= 21) { 3975 return Api21Impl.dispatchNestedPreFling(view, velocityX, velocityY); 3976 } 3977 if (view instanceof NestedScrollingChild) { 3978 return ((NestedScrollingChild) view).dispatchNestedPreFling(velocityX, velocityY); 3979 } 3980 return false; 3981 } 3982 3983 /** 3984 * Returns whether the view hierarchy is currently undergoing a layout pass. This 3985 * information is useful to avoid situations such as calling {@link View#requestLayout()} 3986 * during a layout pass. 3987 * <p> 3988 * Compatibility: 3989 * <ul> 3990 * <li>API < 18: Always returns {@code false}</li> 3991 * </ul> 3992 * 3993 * @return whether the view hierarchy is currently undergoing a layout pass 3994 * @deprecated Call {@link View#isInLayout()} directly. 3995 */ 3996 @androidx.annotation.ReplaceWith(expression = "view.isInLayout()") 3997 @Deprecated isInLayout(@onNull View view)3998 public static boolean isInLayout(@NonNull View view) { 3999 return view.isInLayout(); 4000 } 4001 4002 /** 4003 * Returns true if {@code view} has been through at least one layout since it 4004 * was last attached to or detached from a window. 4005 * @deprecated Call {@link View#isLaidOut()} directly. 4006 */ 4007 @androidx.annotation.ReplaceWith(expression = "view.isLaidOut()") 4008 @Deprecated isLaidOut(@onNull View view)4009 public static boolean isLaidOut(@NonNull View view) { 4010 return view.isLaidOut(); 4011 } 4012 4013 /** 4014 * Returns whether layout direction has been resolved. 4015 * <p> 4016 * Compatibility: 4017 * <ul> 4018 * <li>API < 19: Always returns {@code false}</li> 4019 * </ul> 4020 * 4021 * @return true if layout direction has been resolved. 4022 * @deprecated Call {@link View#isLayoutDirectionResolved()} directly. 4023 */ 4024 @androidx.annotation.ReplaceWith(expression = "view.isLayoutDirectionResolved()") 4025 @Deprecated isLayoutDirectionResolved(@onNull View view)4026 public static boolean isLayoutDirectionResolved(@NonNull View view) { 4027 return view.isLayoutDirectionResolved(); 4028 } 4029 4030 /** 4031 * The visual z position of this view, in pixels. This is equivalent to the 4032 * {@link #setTranslationZ(View, float) translationZ} property plus the current 4033 * {@link #getElevation(View) elevation} property. 4034 * 4035 * @param view view for which to get the position. 4036 * 4037 * @return The visual z position of this view, in pixels. 4038 */ getZ(@onNull View view)4039 public static float getZ(@NonNull View view) { 4040 if (Build.VERSION.SDK_INT >= 21) { 4041 return Api21Impl.getZ(view); 4042 } 4043 return 0f; 4044 } 4045 4046 /** 4047 * Sets the visual z position of this view, in pixels. This is equivalent to setting the 4048 * {@link #setTranslationZ(View, float) translationZ} property to be the difference between 4049 * the x value passed in and the current {@link #getElevation(View) elevation} property. 4050 * <p> 4051 * Compatibility: 4052 * <ul> 4053 * <li>API < 21: No-op</li> 4054 * </ul> 4055 * 4056 * @param view view for which to set the position. 4057 * @param z The visual z position of this view, in pixels. 4058 */ setZ(@onNull View view, float z)4059 public static void setZ(@NonNull View view, float z) { 4060 if (Build.VERSION.SDK_INT >= 21) { 4061 Api21Impl.setZ(view, z); 4062 } 4063 } 4064 4065 /** 4066 * Offset this view's vertical location by the specified number of pixels. 4067 * 4068 * @param view view that needs to be offset. 4069 * @param offset the number of pixels to offset the view by 4070 */ offsetTopAndBottom(@onNull View view, int offset)4071 public static void offsetTopAndBottom(@NonNull View view, int offset) { 4072 if (Build.VERSION.SDK_INT >= 23) { 4073 view.offsetTopAndBottom(offset); 4074 } else if (Build.VERSION.SDK_INT >= 21) { 4075 final Rect parentRect = getEmptyTempRect(); 4076 boolean needInvalidateWorkaround = false; 4077 4078 final ViewParent parent = view.getParent(); 4079 if (parent instanceof View) { 4080 final View p = (View) parent; 4081 parentRect.set(p.getLeft(), p.getTop(), p.getRight(), p.getBottom()); 4082 // If the view currently does not currently intersect the parent (and is therefore 4083 // not displayed) we may need need to invalidate 4084 needInvalidateWorkaround = !parentRect.intersects(view.getLeft(), view.getTop(), 4085 view.getRight(), view.getBottom()); 4086 } 4087 4088 // Now offset, invoking the API 14+ implementation (which contains its own workarounds) 4089 compatOffsetTopAndBottom(view, offset); 4090 4091 // The view has now been offset, so let's intersect the Rect and invalidate where 4092 // the View is now displayed 4093 if (needInvalidateWorkaround && parentRect.intersect(view.getLeft(), view.getTop(), 4094 view.getRight(), view.getBottom())) { 4095 ((View) parent).invalidate(parentRect); 4096 } 4097 } else { 4098 compatOffsetTopAndBottom(view, offset); 4099 } 4100 } 4101 compatOffsetTopAndBottom(View view, int offset)4102 private static void compatOffsetTopAndBottom(View view, int offset) { 4103 view.offsetTopAndBottom(offset); 4104 if (view.getVisibility() == VISIBLE) { 4105 tickleInvalidationFlag(view); 4106 4107 ViewParent parent = view.getParent(); 4108 if (parent instanceof View) { 4109 tickleInvalidationFlag((View) parent); 4110 } 4111 } 4112 } 4113 4114 /** 4115 * Offset this view's horizontal location by the specified amount of pixels. 4116 * 4117 * @param view view which needs to be offset. 4118 * @param offset the number of pixels to offset the view by 4119 */ offsetLeftAndRight(@onNull View view, int offset)4120 public static void offsetLeftAndRight(@NonNull View view, int offset) { 4121 if (Build.VERSION.SDK_INT >= 23) { 4122 view.offsetLeftAndRight(offset); 4123 } else if (Build.VERSION.SDK_INT >= 21) { 4124 final Rect parentRect = getEmptyTempRect(); 4125 boolean needInvalidateWorkaround = false; 4126 4127 final ViewParent parent = view.getParent(); 4128 if (parent instanceof View) { 4129 final View p = (View) parent; 4130 parentRect.set(p.getLeft(), p.getTop(), p.getRight(), p.getBottom()); 4131 // If the view currently does not currently intersect the parent (and is therefore 4132 // not displayed) we may need need to invalidate 4133 needInvalidateWorkaround = !parentRect.intersects(view.getLeft(), view.getTop(), 4134 view.getRight(), view.getBottom()); 4135 } 4136 4137 // Now offset, invoking the API 14+ implementation (which contains its own workarounds) 4138 compatOffsetLeftAndRight(view, offset); 4139 4140 // The view has now been offset, so let's intersect the Rect and invalidate where 4141 // the View is now displayed 4142 if (needInvalidateWorkaround && parentRect.intersect(view.getLeft(), view.getTop(), 4143 view.getRight(), view.getBottom())) { 4144 ((View) parent).invalidate(parentRect); 4145 } 4146 } else { 4147 compatOffsetLeftAndRight(view, offset); 4148 } 4149 } 4150 compatOffsetLeftAndRight(View view, int offset)4151 private static void compatOffsetLeftAndRight(View view, int offset) { 4152 view.offsetLeftAndRight(offset); 4153 if (view.getVisibility() == VISIBLE) { 4154 tickleInvalidationFlag(view); 4155 4156 ViewParent parent = view.getParent(); 4157 if (parent instanceof View) { 4158 tickleInvalidationFlag((View) parent); 4159 } 4160 } 4161 } 4162 tickleInvalidationFlag(View view)4163 private static void tickleInvalidationFlag(View view) { 4164 final float y = view.getTranslationY(); 4165 view.setTranslationY(y + 1); 4166 view.setTranslationY(y); 4167 } 4168 4169 /** 4170 * Sets a rectangular area on this view to which the view will be clipped 4171 * when it is drawn. Setting the value to null will remove the clip bounds 4172 * and the view will draw normally, using its full bounds. 4173 * 4174 * <p>Prior to API 18 this does nothing.</p> 4175 * 4176 * @param view The view to set clipBounds. 4177 * @param clipBounds The rectangular area, in the local coordinates of 4178 * this view, to which future drawing operations will be clipped. 4179 * @deprecated Call {@link View#setClipBounds(Rect)} directly. 4180 */ 4181 @androidx.annotation.ReplaceWith(expression = "view.setClipBounds(clipBounds)") 4182 @Deprecated setClipBounds(@onNull View view, @Nullable Rect clipBounds)4183 public static void setClipBounds(@NonNull View view, @Nullable Rect clipBounds) { 4184 view.setClipBounds(clipBounds); 4185 } 4186 4187 /** 4188 * Returns a copy of the current {@link #setClipBounds(View, Rect)}. 4189 * 4190 * <p>Prior to API 18 this will return null.</p> 4191 * 4192 * @return A copy of the current clip bounds if clip bounds are set, 4193 * otherwise null. 4194 * @deprecated Call {@link View#getClipBounds()} directly. 4195 */ 4196 @androidx.annotation.ReplaceWith(expression = "view.getClipBounds()") 4197 @Deprecated getClipBounds(@onNull View view)4198 public static @Nullable Rect getClipBounds(@NonNull View view) { 4199 return view.getClipBounds(); 4200 } 4201 4202 /** 4203 * Returns true if the provided view is currently attached to a window. 4204 * @deprecated Call {@link View#isAttachedToWindow()} directly. 4205 */ 4206 @androidx.annotation.ReplaceWith(expression = "view.isAttachedToWindow()") 4207 @Deprecated isAttachedToWindow(@onNull View view)4208 public static boolean isAttachedToWindow(@NonNull View view) { 4209 return view.isAttachedToWindow(); 4210 } 4211 4212 /** 4213 * Returns whether the provided view has an attached {@link View.OnClickListener}. 4214 * 4215 * @return true if there is a listener, false if there is none. 4216 * @deprecated Call {@link View#hasOnClickListeners()} directly. 4217 */ 4218 @androidx.annotation.ReplaceWith(expression = "view.hasOnClickListeners()") 4219 @Deprecated hasOnClickListeners(@onNull View view)4220 public static boolean hasOnClickListeners(@NonNull View view) { 4221 return view.hasOnClickListeners(); 4222 } 4223 4224 /** 4225 * Sets the state of all scroll indicators. 4226 * <p> 4227 * See {@link #setScrollIndicators(View, int, int)} for usage information. 4228 * 4229 * @param view view for which to set the state. 4230 * @param indicators a bitmask of indicators that should be enabled, or 4231 * {@code 0} to disable all indicators 4232 * 4233 * @see #setScrollIndicators(View, int, int) 4234 * @see #getScrollIndicators(View) 4235 */ setScrollIndicators(@onNull View view, @ScrollIndicators int indicators)4236 public static void setScrollIndicators(@NonNull View view, @ScrollIndicators int indicators) { 4237 if (Build.VERSION.SDK_INT >= 23) { 4238 Api23Impl.setScrollIndicators(view, indicators); 4239 } 4240 } 4241 4242 /** 4243 * Sets the state of the scroll indicators specified by the mask. To change 4244 * all scroll indicators at once, see {@link #setScrollIndicators(View, int)}. 4245 * <p> 4246 * When a scroll indicator is enabled, it will be displayed if the view 4247 * can scroll in the direction of the indicator. 4248 * <p> 4249 * Multiple indicator types may be enabled or disabled by passing the 4250 * logical OR of the desired types. If multiple types are specified, they 4251 * will all be set to the same enabled state. 4252 * <p> 4253 * For example, to enable the top scroll indicatorExample: {@code setScrollIndicators} 4254 * 4255 * @param view view for which to set the state. 4256 * @param indicators the indicator direction, or the logical OR of multiple 4257 * indicator directions. One or more of: 4258 * <ul> 4259 * <li>{@link #SCROLL_INDICATOR_TOP}</li> 4260 * <li>{@link #SCROLL_INDICATOR_BOTTOM}</li> 4261 * <li>{@link #SCROLL_INDICATOR_LEFT}</li> 4262 * <li>{@link #SCROLL_INDICATOR_RIGHT}</li> 4263 * <li>{@link #SCROLL_INDICATOR_START}</li> 4264 * <li>{@link #SCROLL_INDICATOR_END}</li> 4265 * </ul> 4266 * @param mask the mask for scroll indicators. 4267 * 4268 * @see #setScrollIndicators(View, int) 4269 * @see #getScrollIndicators(View) 4270 */ setScrollIndicators(@onNull View view, @ScrollIndicators int indicators, @ScrollIndicators int mask)4271 public static void setScrollIndicators(@NonNull View view, @ScrollIndicators int indicators, 4272 @ScrollIndicators int mask) { 4273 if (Build.VERSION.SDK_INT >= 23) { 4274 Api23Impl.setScrollIndicators(view, indicators, mask); 4275 } 4276 } 4277 4278 /** 4279 * Returns a bitmask representing the enabled scroll indicators. 4280 * <p> 4281 * For example, if the top and left scroll indicators are enabled and all 4282 * other indicators are disabled, the return value will be 4283 * {@code ViewCompat.SCROLL_INDICATOR_TOP | ViewCompat.SCROLL_INDICATOR_LEFT}. 4284 * <p> 4285 * To check whether the bottom scroll indicator is enabled, use the value 4286 * of {@code (ViewCompat.getScrollIndicators(view) & ViewCompat.SCROLL_INDICATOR_BOTTOM) != 0}. 4287 * 4288 * @param view view for which to get the state. 4289 * 4290 * @return a bitmask representing the enabled scroll indicators 4291 */ getScrollIndicators(@onNull View view)4292 public static int getScrollIndicators(@NonNull View view) { 4293 if (Build.VERSION.SDK_INT >= 23) { 4294 return Api23Impl.getScrollIndicators(view); 4295 } 4296 return 0; 4297 } 4298 4299 /** 4300 * Set the pointer icon for the current view. 4301 * @param view view for which to set the pointer icon. 4302 * @param pointerIcon A PointerIconCompat instance which will be shown when the mouse hovers. 4303 */ setPointerIcon(@onNull View view, @Nullable PointerIconCompat pointerIcon)4304 public static void setPointerIcon(@NonNull View view, @Nullable PointerIconCompat pointerIcon) { 4305 if (Build.VERSION.SDK_INT >= 24) { 4306 Api24Impl.setPointerIcon(view, (PointerIcon) (pointerIcon != null 4307 ? pointerIcon.getPointerIcon() : null)); 4308 } 4309 } 4310 4311 /** 4312 * Gets the logical display to which the view's window has been attached. 4313 * <p> 4314 * Compatibility: 4315 * <ul> 4316 * <li>API < 17: Returns the default display when the view is attached. Otherwise, null. 4317 * </ul> 4318 * 4319 * @return The logical display, or null if the view is not currently attached to a window. 4320 * @deprecated Call {@link View#getDisplay()} directly. 4321 */ 4322 @androidx.annotation.ReplaceWith(expression = "view.getDisplay()") 4323 @Deprecated getDisplay(@onNull View view)4324 public static @Nullable Display getDisplay(@NonNull View view) { 4325 return view.getDisplay(); 4326 } 4327 4328 /** 4329 * Sets the tooltip for the view. 4330 * 4331 * <p>Prior to API 26 this does nothing. Use TooltipCompat class from v7 appcompat library 4332 * for a compatible tooltip implementation.</p> 4333 * 4334 * @param view view for which to set the tooltip. 4335 * @param tooltipText the tooltip text 4336 */ setTooltipText(@onNull View view, @Nullable CharSequence tooltipText)4337 public static void setTooltipText(@NonNull View view, @Nullable CharSequence tooltipText) { 4338 if (Build.VERSION.SDK_INT >= 26) { 4339 Api26Impl.setTooltipText(view, tooltipText); 4340 } 4341 } 4342 4343 /** 4344 * Start the drag and drop operation. 4345 */ 4346 @SuppressWarnings("deprecation") startDragAndDrop(@onNull View v, @Nullable ClipData data, View.@NonNull DragShadowBuilder shadowBuilder, @Nullable Object myLocalState, int flags)4347 public static boolean startDragAndDrop(@NonNull View v, @Nullable ClipData data, 4348 View.@NonNull DragShadowBuilder shadowBuilder, @Nullable Object myLocalState, 4349 int flags) { 4350 if (Build.VERSION.SDK_INT >= 24) { 4351 return Api24Impl.startDragAndDrop(v, data, shadowBuilder, myLocalState, flags); 4352 } else { 4353 return v.startDrag(data, shadowBuilder, myLocalState, flags); 4354 } 4355 } 4356 4357 /** 4358 * Cancel the drag and drop operation. 4359 */ cancelDragAndDrop(@onNull View v)4360 public static void cancelDragAndDrop(@NonNull View v) { 4361 if (Build.VERSION.SDK_INT >= 24) { 4362 Api24Impl.cancelDragAndDrop(v); 4363 } 4364 } 4365 4366 /** 4367 * Update the drag shadow while drag and drop is in progress. 4368 */ updateDragShadow(@onNull View v, View.@NonNull DragShadowBuilder shadowBuilder)4369 public static void updateDragShadow(@NonNull View v, 4370 View.@NonNull DragShadowBuilder shadowBuilder) { 4371 if (Build.VERSION.SDK_INT >= 24) { 4372 Api24Impl.updateDragShadow(v, shadowBuilder); 4373 } 4374 } 4375 4376 /** 4377 * Gets the ID of the next keyboard navigation cluster root. 4378 * 4379 * @return the next keyboard navigation cluster ID, or {@link View#NO_ID} if the framework 4380 * should decide automatically or API < 26. 4381 */ getNextClusterForwardId(@onNull View view)4382 public static int getNextClusterForwardId(@NonNull View view) { 4383 if (Build.VERSION.SDK_INT >= 26) { 4384 return Api26Impl.getNextClusterForwardId(view); 4385 } 4386 return View.NO_ID; 4387 } 4388 4389 /** 4390 * Sets the ID of the next keyboard navigation cluster root view. Does nothing if {@code view} 4391 * is not a keyboard navigation cluster or if API < 26. 4392 * 4393 * @param view view for which to set the ID. 4394 * @param nextClusterForwardId next cluster ID, or {@link View#NO_ID} if the framework 4395 * should decide automatically. 4396 */ setNextClusterForwardId(@onNull View view, int nextClusterForwardId)4397 public static void setNextClusterForwardId(@NonNull View view, int nextClusterForwardId) { 4398 if (Build.VERSION.SDK_INT >= 26) { 4399 Api26Impl.setNextClusterForwardId(view, nextClusterForwardId); 4400 } 4401 } 4402 4403 /** 4404 * Returns whether {@code view} is a root of a keyboard navigation cluster. Always returns 4405 * {@code false} on API < 26. 4406 * 4407 * @param view view for which to check the cluster. 4408 * @return {@code true} if this view is a root of a cluster, or {@code false} otherwise. 4409 */ isKeyboardNavigationCluster(@onNull View view)4410 public static boolean isKeyboardNavigationCluster(@NonNull View view) { 4411 if (Build.VERSION.SDK_INT >= 26) { 4412 return Api26Impl.isKeyboardNavigationCluster(view); 4413 } 4414 return false; 4415 } 4416 4417 /** 4418 * Set whether {@code view} is a root of a keyboard navigation cluster. Does nothing if 4419 * API < 26. 4420 * 4421 * @param view view for which to set the cluster. 4422 * @param isCluster {@code true} to mark {@code view} as the root of a cluster, {@code false} 4423 * to unmark. 4424 */ setKeyboardNavigationCluster(@onNull View view, boolean isCluster)4425 public static void setKeyboardNavigationCluster(@NonNull View view, boolean isCluster) { 4426 if (Build.VERSION.SDK_INT >= 26) { 4427 Api26Impl.setKeyboardNavigationCluster(view, isCluster); 4428 } 4429 } 4430 4431 /** 4432 * Returns whether {@code view} should receive focus when the focus is restored for the view 4433 * hierarchy containing it. Returns {@code false} on API < 26. 4434 * <p> 4435 * Focus gets restored for a view hierarchy when the root of the hierarchy gets added to a 4436 * window or serves as a target of cluster navigation. 4437 * 4438 * @param view view for which to check the state. 4439 * @return {@code true} if {@code view} is the default-focus view, {@code false} otherwise. 4440 */ isFocusedByDefault(@onNull View view)4441 public static boolean isFocusedByDefault(@NonNull View view) { 4442 if (Build.VERSION.SDK_INT >= 26) { 4443 return Api26Impl.isFocusedByDefault(view); 4444 } 4445 return false; 4446 } 4447 4448 /** 4449 * Sets whether {@code view} should receive focus when the focus is restored for the view 4450 * hierarchy containing it. 4451 * <p> 4452 * Focus gets restored for a view hierarchy when the root of the hierarchy gets added to a 4453 * window or serves as a target of cluster navigation. 4454 * <p> 4455 * Does nothing on API < 26. 4456 * 4457 * @param view view for which to set the state. 4458 * @param isFocusedByDefault {@code true} to set {@code view} as the default-focus view, 4459 * {@code false} otherwise. 4460 */ setFocusedByDefault(@onNull View view, boolean isFocusedByDefault)4461 public static void setFocusedByDefault(@NonNull View view, boolean isFocusedByDefault) { 4462 if (Build.VERSION.SDK_INT >= 26) { 4463 Api26Impl.setFocusedByDefault(view, isFocusedByDefault); 4464 } 4465 } 4466 4467 /** 4468 * Find the nearest keyboard navigation cluster in the specified direction. 4469 * This does not actually give focus to that cluster. 4470 * 4471 * @param view view on which to do the search. 4472 * @param currentCluster The starting point of the search. {@code null} means the current 4473 * cluster is not found yet. 4474 * @param direction Direction to look. 4475 * 4476 * @return the nearest keyboard navigation cluster in the specified direction, or {@code null} 4477 * if one can't be found or if API < 26. 4478 */ keyboardNavigationClusterSearch(@onNull View view, @Nullable View currentCluster, @FocusDirection int direction)4479 public static @Nullable View keyboardNavigationClusterSearch(@NonNull View view, 4480 @Nullable View currentCluster, @FocusDirection int direction) { 4481 if (Build.VERSION.SDK_INT >= 26) { 4482 return Api26Impl.keyboardNavigationClusterSearch(view, currentCluster, direction); 4483 } 4484 return null; 4485 } 4486 4487 /** 4488 * Adds any keyboard navigation cluster roots that are descendants of {@code view} ( 4489 * including {@code view} if it is a cluster root itself) to {@code views}. Does nothing 4490 * on API < 26. 4491 * 4492 * @param view view on which to make the change. 4493 * @param views collection of keyboard navigation cluster roots found so far. 4494 * @param direction direction to look. 4495 */ addKeyboardNavigationClusters(@onNull View view, @NonNull Collection<View> views, int direction)4496 public static void addKeyboardNavigationClusters(@NonNull View view, 4497 @NonNull Collection<View> views, int direction) { 4498 if (Build.VERSION.SDK_INT >= 26) { 4499 Api26Impl.addKeyboardNavigationClusters(view, views, direction); 4500 } 4501 } 4502 4503 /** 4504 * Gives focus to the default-focus view in the view hierarchy rooted at {@code view}. 4505 * If the default-focus view cannot be found or if API < 26, this falls back to calling 4506 * {@link View#requestFocus(int)}. 4507 * 4508 * @param view view on which to make the change. 4509 * @return {@code true} if {@code view} or one of its descendants took focus, {@code false} 4510 * otherwise. 4511 */ restoreDefaultFocus(@onNull View view)4512 public static boolean restoreDefaultFocus(@NonNull View view) { 4513 if (Build.VERSION.SDK_INT >= 26) { 4514 return Api26Impl.restoreDefaultFocus(view); 4515 } 4516 return view.requestFocus(); 4517 } 4518 4519 /** 4520 * Returns true if this view is focusable or if it contains a reachable View 4521 * for which {@link View#hasExplicitFocusable()} returns {@code true}. 4522 * A "reachable hasExplicitFocusable()" is a view whose parents do not block descendants focus. 4523 * Only {@link View#VISIBLE} views for which {@link View#getFocusable()} would return 4524 * {@link View#FOCUSABLE} are considered focusable. 4525 * 4526 * <p>This method preserves the pre-{@link Build.VERSION_CODES#O} behavior of 4527 * {@link View#hasFocusable()} in that only views explicitly set focusable will cause 4528 * this method to return true. A view set to {@link View#FOCUSABLE_AUTO} that resolves 4529 * to focusable will not.</p> 4530 * 4531 * @param view view on which to make the change. 4532 * @return {@code true} if the view is focusable or if the view contains a focusable 4533 * view, {@code false} otherwise 4534 */ hasExplicitFocusable(@onNull View view)4535 public static boolean hasExplicitFocusable(@NonNull View view) { 4536 if (Build.VERSION.SDK_INT >= 26) { 4537 return Api26Impl.hasExplicitFocusable(view); 4538 } 4539 return view.hasFocusable(); 4540 } 4541 4542 /** 4543 * Generate a value suitable for use in {@link View#setId(int)}. 4544 * This value will not collide with ID values generated at build time by aapt for R.id. 4545 * 4546 * @return a generated ID value 4547 * @deprecated Call {@link View#generateViewId()} directly. 4548 */ 4549 @Deprecated generateViewId()4550 public static int generateViewId() { 4551 return View.generateViewId(); 4552 } 4553 4554 /** 4555 * Adds a listener which will receive unhandled {@link KeyEvent}s. This must be called on the 4556 * UI thread. 4557 * 4558 * @param view view on which to add the listener. 4559 * @param listener a receiver of unhandled {@link KeyEvent}s. 4560 * @see #removeOnUnhandledKeyEventListener 4561 */ addOnUnhandledKeyEventListener(@onNull View view, final @NonNull OnUnhandledKeyEventListenerCompat listener)4562 public static void addOnUnhandledKeyEventListener(@NonNull View view, 4563 final @NonNull OnUnhandledKeyEventListenerCompat listener) { 4564 if (Build.VERSION.SDK_INT >= 28) { 4565 Api28Impl.addOnUnhandledKeyEventListener(view, listener); 4566 return; 4567 } 4568 @SuppressWarnings("unchecked") 4569 ArrayList<OnUnhandledKeyEventListenerCompat> viewListeners = 4570 (ArrayList<OnUnhandledKeyEventListenerCompat>) 4571 view.getTag(R.id.tag_unhandled_key_listeners); 4572 if (viewListeners == null) { 4573 viewListeners = new ArrayList<>(); 4574 view.setTag(R.id.tag_unhandled_key_listeners, viewListeners); 4575 } 4576 viewListeners.add(listener); 4577 if (viewListeners.size() == 1) { 4578 UnhandledKeyEventManager.registerListeningView(view); 4579 } 4580 } 4581 4582 /** 4583 * Removes a listener which will receive unhandled {@link KeyEvent}s. This must be called on the 4584 * UI thread. 4585 * 4586 * @param view view from which to remove the listener. 4587 * @param listener a receiver of unhandled {@link KeyEvent}s. 4588 * @see #addOnUnhandledKeyEventListener 4589 */ removeOnUnhandledKeyEventListener(@onNull View view, @NonNull OnUnhandledKeyEventListenerCompat listener)4590 public static void removeOnUnhandledKeyEventListener(@NonNull View view, 4591 @NonNull OnUnhandledKeyEventListenerCompat listener) { 4592 if (Build.VERSION.SDK_INT >= 28) { 4593 Api28Impl.removeOnUnhandledKeyEventListener(view, listener); 4594 return; 4595 } 4596 @SuppressWarnings("unchecked") 4597 ArrayList<OnUnhandledKeyEventListenerCompat> viewListeners = 4598 (ArrayList<OnUnhandledKeyEventListenerCompat>) 4599 view.getTag(R.id.tag_unhandled_key_listeners); 4600 if (viewListeners != null) { 4601 viewListeners.remove(listener); 4602 if (viewListeners.size() == 0) { 4603 UnhandledKeyEventManager.unregisterListeningView(view); 4604 } 4605 } 4606 } 4607 4608 /** 4609 * @deprecated This is a utility class and it shouldn't be instantiated. 4610 */ 4611 @Deprecated ViewCompat()4612 protected ViewCompat() { 4613 } 4614 4615 /** 4616 * Interface definition for a callback to be invoked when a hardware key event hasn't 4617 * been handled by the view hierarchy. 4618 */ 4619 @SuppressWarnings("NullableProblems") // Useless warning 4620 public interface OnUnhandledKeyEventListenerCompat { 4621 /** 4622 * Called when a hardware key is dispatched to a view after being unhandled during normal 4623 * {@link KeyEvent} dispatch. 4624 * 4625 * @param v The view the key has been dispatched to. 4626 * @param event The KeyEvent object containing information about the event. 4627 * @return {@code true} if the listener has consumed the event, {@code false} otherwise. 4628 */ onUnhandledKeyEvent(@onNull View v, @NonNull KeyEvent event)4629 boolean onUnhandledKeyEvent(@NonNull View v, @NonNull KeyEvent event); 4630 } 4631 4632 @UiThread dispatchUnhandledKeyEventBeforeHierarchy(View root, KeyEvent evt)4633 static boolean dispatchUnhandledKeyEventBeforeHierarchy(View root, KeyEvent evt) { 4634 if (Build.VERSION.SDK_INT >= 28) { 4635 return false; 4636 } 4637 return UnhandledKeyEventManager.at(root).preDispatch(evt); 4638 } 4639 4640 @UiThread dispatchUnhandledKeyEventBeforeCallback(View root, KeyEvent evt)4641 static boolean dispatchUnhandledKeyEventBeforeCallback(View root, KeyEvent evt) { 4642 if (Build.VERSION.SDK_INT >= 28) { 4643 return false; 4644 } 4645 return UnhandledKeyEventManager.at(root).dispatch(root, evt); 4646 } 4647 4648 /** 4649 * Modifies the input matrix such that it maps on-screen coordinates to 4650 * view-local coordinates for the provided view. 4651 * 4652 * @param view view to examine 4653 * @param matrix input matrix to modify 4654 */ 4655 @SuppressLint("NewApi") // Lint doesn't know about the hidden method. transformMatrixToGlobal(@onNull View view, @NonNull Matrix matrix)4656 public static void transformMatrixToGlobal(@NonNull View view, @NonNull Matrix matrix) { 4657 if (Build.VERSION.SDK_INT >= 29) { 4658 Api29Impl.transformMatrixToGlobal(view, matrix); 4659 } else { 4660 // The View method in question is available as a public (but hidden) method all the way 4661 // back to API 21, but we check that it's actually present, since conformance testing 4662 // does not assert about methods that are not in the public API. 4663 if (sTryHiddenViewTransformMatrixToGlobal) { 4664 try { 4665 Api29Impl.transformMatrixToGlobal(view, matrix); 4666 return; 4667 } catch (NoSuchMethodError e) { 4668 sTryHiddenViewTransformMatrixToGlobal = false; 4669 } 4670 } 4671 fallbackTransformMatrixToGlobal(view, matrix); 4672 } 4673 } 4674 4675 @VisibleForTesting fallbackTransformMatrixToGlobal(View view, Matrix matrix)4676 static void fallbackTransformMatrixToGlobal(View view, Matrix matrix) { 4677 ViewParent parent = view.getParent(); 4678 if (parent instanceof View) { 4679 View parentView = (View) parent; 4680 fallbackTransformMatrixToGlobal(parentView, matrix); 4681 matrix.preTranslate(-parentView.getScrollX(), -parentView.getScrollY()); 4682 } 4683 matrix.preTranslate(view.getLeft(), view.getTop()); 4684 matrix.preConcat(view.getMatrix()); 4685 } 4686 4687 /** 4688 * Sets whether this View should be a focusable element for screen readers 4689 * and include non-focusable Views from its subtree when providing feedback. 4690 * <p> 4691 * Note: this is similar to using <a href="#attr_android:focusable">{@code android:focusable}, 4692 * but does not impact input focus behavior. 4693 * <p>This can be used to 4694 * <a href="{@docRoot}guide/topics/ui/accessibility/principles#content-groups">group related 4695 * content.</a> 4696 * </p> 4697 * 4698 * @param view The view whose title should be set 4699 * @param screenReaderFocusable Whether the view should be treated as a unit by screen reader 4700 * accessibility tools. 4701 * <p> 4702 * Compatibility: 4703 * <ul> 4704 * <li>API < 19: No-op 4705 * </ul> 4706 */ 4707 @UiThread setScreenReaderFocusable(@onNull View view, boolean screenReaderFocusable)4708 public static void setScreenReaderFocusable(@NonNull View view, boolean screenReaderFocusable) { 4709 screenReaderFocusableProperty().set(view, screenReaderFocusable); 4710 } 4711 4712 /** 4713 * Returns whether the view should be treated as a focusable unit by screen reader 4714 * accessibility tools. 4715 * @see #setScreenReaderFocusable(View, boolean) 4716 * 4717 * @param view The view to check for screen reader focusability. 4718 * <p> 4719 * Compatibility: 4720 * <ul> 4721 * <li>API < 19: Always returns {@code false}</li> 4722 * </ul> 4723 * 4724 * @return Whether the view should be treated as a focusable unit by screen reader. 4725 */ 4726 @UiThread isScreenReaderFocusable(@onNull View view)4727 public static boolean isScreenReaderFocusable(@NonNull View view) { 4728 Boolean result = screenReaderFocusableProperty().get(view); 4729 return result != null && result; 4730 } 4731 screenReaderFocusableProperty()4732 private static AccessibilityViewProperty<Boolean> screenReaderFocusableProperty() { 4733 return new AccessibilityViewProperty<Boolean>( 4734 R.id.tag_screen_reader_focusable, Boolean.class, 28) { 4735 4736 @RequiresApi(28) 4737 @Override 4738 Boolean frameworkGet(@NonNull View view) { 4739 return ViewCompat.Api28Impl.isScreenReaderFocusable(view); 4740 } 4741 4742 @RequiresApi(28) 4743 @Override 4744 void frameworkSet(@NonNull View view, Boolean value) { 4745 ViewCompat.Api28Impl.setScreenReaderFocusable(view, value); 4746 } 4747 4748 @Override 4749 boolean shouldUpdate(Boolean oldValue, Boolean newValue) { 4750 return !booleanNullToFalseEquals(oldValue, newValue); 4751 } 4752 }; 4753 } 4754 4755 /** 4756 * Visually distinct portion of a window with window-like semantics are considered panes for 4757 * accessibility purposes. One example is the content view of a large fragment that is replaced. 4758 * In order for accessibility services to understand a pane's window-like behavior, panes 4759 * should have descriptive titles. Views with pane titles produce 4760 * {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED}s when they appear, disappear, or change 4761 * title. 4762 * 4763 * <p> 4764 * When transitioning from one Activity to another, instead of using 4765 * setAccessibilityPaneTitle(), set a descriptive title for your activity's window by using 4766 * {@code android:label} for the matching <activity> entry in your application’s manifest or 4767 * updating the title at runtime with {@link android.app.Activity#setTitle(CharSequence)}. 4768 * 4769 * <p> 4770 * To set the pane title in xml, use {@code android:accessibilityPaneTitle}. 4771 * @param view The view whose pane title should be set. 4772 * @param accessibilityPaneTitle The pane's title. Setting to {@code null} indicates that this 4773 * View is not a pane. 4774 * <p> 4775 * Compatibility: 4776 * <ul> 4777 * <li>API < 19: No-op 4778 * </ul> 4779 * 4780 * {@see AccessibilityNodeInfo#setPaneTitle(CharSequence)} 4781 */ 4782 @UiThread 4783 public static void setAccessibilityPaneTitle(@NonNull View view, 4784 @Nullable CharSequence accessibilityPaneTitle) { 4785 paneTitleProperty().set(view, accessibilityPaneTitle); 4786 if (accessibilityPaneTitle != null) { 4787 sAccessibilityPaneVisibilityManager.addAccessibilityPane(view); 4788 } else { 4789 sAccessibilityPaneVisibilityManager.removeAccessibilityPane(view); 4790 } 4791 } 4792 4793 /** 4794 * Get the title of the pane for purposes of accessibility. 4795 * 4796 * @param view The view queried for it's pane title. 4797 * <p> 4798 * Compatibility: 4799 * <ul> 4800 * <li>API < 19: Always returns {@code null}</li> 4801 * </ul> 4802 * 4803 * @return The current pane title. 4804 * 4805 * {@see #setAccessibilityPaneTitle}. 4806 */ 4807 @UiThread 4808 public static @Nullable CharSequence getAccessibilityPaneTitle(@NonNull View view) { 4809 return paneTitleProperty().get(view); 4810 } 4811 4812 private static AccessibilityViewProperty<CharSequence> paneTitleProperty() { 4813 return new AccessibilityViewProperty<CharSequence>(R.id.tag_accessibility_pane_title, 4814 CharSequence.class, AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_TITLE, 28) { 4815 4816 @RequiresApi(28) 4817 @Override 4818 CharSequence frameworkGet(View view) { 4819 return ViewCompat.Api28Impl.getAccessibilityPaneTitle(view); 4820 } 4821 4822 @RequiresApi(28) 4823 @Override 4824 void frameworkSet(View view, CharSequence value) { 4825 ViewCompat.Api28Impl.setAccessibilityPaneTitle(view, value); 4826 } 4827 4828 @Override 4829 boolean shouldUpdate(CharSequence oldValue, CharSequence newValue) { 4830 return !TextUtils.equals(oldValue, newValue); 4831 } 4832 }; 4833 } 4834 4835 private static AccessibilityViewProperty<CharSequence> stateDescriptionProperty() { 4836 return new AccessibilityViewProperty<CharSequence>(R.id.tag_state_description, 4837 CharSequence.class, AccessibilityEvent.CONTENT_CHANGE_TYPE_STATE_DESCRIPTION, 30) { 4838 4839 @RequiresApi(30) 4840 @Override 4841 CharSequence frameworkGet(View view) { 4842 return Api30Impl.getStateDescription(view); 4843 } 4844 4845 @RequiresApi(30) 4846 @Override 4847 void frameworkSet(View view, CharSequence value) { 4848 Api30Impl.setStateDescription(view, value); 4849 } 4850 4851 @Override 4852 boolean shouldUpdate(CharSequence oldValue, CharSequence newValue) { 4853 return !TextUtils.equals(oldValue, newValue); 4854 } 4855 }; 4856 } 4857 4858 /** 4859 * Gets whether this view is a heading for accessibility purposes. 4860 * 4861 * @param view The view checked if it is a heading. 4862 * <p> 4863 * Compatibility: 4864 * <ul> 4865 * <li>API < 28: Always returns {@code false}</li> 4866 * </ul> 4867 * 4868 * @return {@code true} if the view is a heading, {@code false} otherwise. 4869 */ 4870 @UiThread 4871 public static boolean isAccessibilityHeading(@NonNull View view) { 4872 Boolean result = accessibilityHeadingProperty().get(view); 4873 return result != null && result; 4874 } 4875 4876 /** 4877 * Set if view is a heading for a section of content for accessibility purposes. 4878 * 4879 * @param view The view to set if it is a heading. 4880 * @param isHeading {@code true} if the view is a heading, {@code false} otherwise. 4881 * <p> 4882 * Compatibility: 4883 * <ul> 4884 * <li>API < 28: No-op 4885 * </ul> 4886 */ 4887 @UiThread 4888 public static void setAccessibilityHeading(@NonNull View view, boolean isHeading) { 4889 accessibilityHeadingProperty().set(view, isHeading); 4890 } 4891 4892 private static AccessibilityViewProperty<Boolean> accessibilityHeadingProperty() { 4893 return new AccessibilityViewProperty<Boolean>( 4894 R.id.tag_accessibility_heading, Boolean.class, 28) { 4895 4896 @RequiresApi(28) 4897 @Override 4898 Boolean frameworkGet(View view) { 4899 return ViewCompat.Api28Impl.isAccessibilityHeading(view); 4900 } 4901 4902 @RequiresApi(28) 4903 @Override 4904 void frameworkSet(View view, Boolean value) { 4905 ViewCompat.Api28Impl.setAccessibilityHeading(view, value); 4906 } 4907 4908 @Override 4909 boolean shouldUpdate(Boolean oldValue, Boolean newValue) { 4910 return !booleanNullToFalseEquals(oldValue, newValue); 4911 } 4912 }; 4913 } 4914 4915 4916 abstract static class AccessibilityViewProperty<T> { 4917 private final int mTagKey; 4918 private final Class<T> mType; 4919 private final int mFrameworkMinimumSdk; 4920 private final int mContentChangeType; 4921 4922 AccessibilityViewProperty(int tagKey, Class<T> type, int frameworkMinimumSdk) { 4923 this(tagKey, type, 4924 AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED, frameworkMinimumSdk); 4925 } 4926 4927 AccessibilityViewProperty( 4928 int tagKey, Class<T> type, int contentChangeType, int frameworkMinimumSdk) { 4929 mTagKey = tagKey; 4930 mType = type; 4931 mContentChangeType = contentChangeType; 4932 mFrameworkMinimumSdk = frameworkMinimumSdk; 4933 4934 } 4935 4936 void set(View view, T value) { 4937 if (frameworkAvailable()) { 4938 frameworkSet(view, value); 4939 } else if (shouldUpdate(get(view), value)) { 4940 ensureAccessibilityDelegateCompat(view); 4941 view.setTag(mTagKey, value); 4942 // If we're here, we're guaranteed to be on v19+ (see the logic in 4943 // extrasAvailable), so we can call notifyViewAccessibilityStateChangedIfNeeded 4944 // which requires 19. 4945 notifyViewAccessibilityStateChangedIfNeeded(view, mContentChangeType); 4946 } 4947 } 4948 4949 @SuppressWarnings("unchecked") 4950 T get(View view) { 4951 if (frameworkAvailable()) { 4952 return frameworkGet(view); 4953 } else { 4954 Object value = view.getTag(mTagKey); 4955 if (mType.isInstance(value)) { 4956 return (T) value; 4957 } 4958 } 4959 return null; 4960 } 4961 4962 private boolean frameworkAvailable() { 4963 return Build.VERSION.SDK_INT >= mFrameworkMinimumSdk; 4964 } 4965 4966 boolean shouldUpdate(T oldValue, T newValue) { 4967 return !newValue.equals(oldValue); 4968 } 4969 4970 abstract T frameworkGet(View view); 4971 4972 abstract void frameworkSet(View view, T value); 4973 4974 @SuppressWarnings("BooleanMethodIsAlwaysInverted") 4975 boolean booleanNullToFalseEquals(Boolean a, Boolean b) { 4976 boolean aBool = a != null && a; 4977 boolean bBool = b != null && b; 4978 return aBool == bBool; 4979 } 4980 } 4981 4982 static void notifyViewAccessibilityStateChangedIfNeeded(View view, int changeType) { 4983 AccessibilityManager accessibilityManager = (AccessibilityManager) 4984 view.getContext().getSystemService(Context.ACCESSIBILITY_SERVICE); 4985 if (!accessibilityManager.isEnabled()) { 4986 return; 4987 } 4988 boolean isVisibleAccessibilityPane = getAccessibilityPaneTitle(view) != null 4989 && (view.isShown() && view.getWindowVisibility() == VISIBLE); 4990 // If this is a live region or accessibilityPane, we should send a subtree change event 4991 // from this view immediately. Otherwise, we can let it propagate up. 4992 if ((view.getAccessibilityLiveRegion() != ACCESSIBILITY_LIVE_REGION_NONE) 4993 || isVisibleAccessibilityPane) { 4994 final AccessibilityEvent event = AccessibilityEvent.obtain(); 4995 event.setEventType(isVisibleAccessibilityPane 4996 ? AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED 4997 : AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); 4998 event.setContentChangeTypes(changeType); 4999 if (isVisibleAccessibilityPane) { 5000 event.getText().add(getAccessibilityPaneTitle(view)); 5001 setImportantForAccessibilityIfNeeded(view); 5002 } 5003 view.sendAccessibilityEventUnchecked(event); 5004 } else if (changeType == AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED) { 5005 final AccessibilityEvent event = AccessibilityEvent.obtain(); 5006 view.onInitializeAccessibilityEvent(event); 5007 event.setEventType(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); 5008 event.setContentChangeTypes(changeType); 5009 event.setSource(view); 5010 view.onPopulateAccessibilityEvent(event); 5011 event.getText().add(getAccessibilityPaneTitle(view)); 5012 accessibilityManager.sendAccessibilityEvent(event); 5013 } else if (view.getParent() != null) { 5014 final ViewParent parent = view.getParent(); 5015 try { 5016 parent.notifySubtreeAccessibilityStateChanged(view, view, changeType); 5017 } catch (AbstractMethodError e) { 5018 Log.e(TAG, view.getParent().getClass().getSimpleName() 5019 + " does not fully implement ViewParent", e); 5020 } 5021 } 5022 } 5023 5024 private static void setImportantForAccessibilityIfNeeded(View view) { 5025 if (view.getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) { 5026 view.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); 5027 } 5028 } 5029 5030 private static final AccessibilityPaneVisibilityManager sAccessibilityPaneVisibilityManager = 5031 new AccessibilityPaneVisibilityManager(); 5032 5033 static class AccessibilityPaneVisibilityManager 5034 implements ViewTreeObserver.OnGlobalLayoutListener, View.OnAttachStateChangeListener { 5035 private final WeakHashMap<View, Boolean> mPanesToVisible = new WeakHashMap<>(); 5036 5037 @Override 5038 public void onGlobalLayout() { 5039 if (Build.VERSION.SDK_INT < 28) { 5040 for (Map.Entry<View, Boolean> entry : mPanesToVisible.entrySet()) { 5041 checkPaneVisibility(entry); 5042 } 5043 } 5044 } 5045 5046 @Override 5047 public void onViewAttachedToWindow(View view) { 5048 // When detached the view loses its viewTreeObserver. 5049 registerForLayoutCallback(view); 5050 } 5051 5052 @Override 5053 public void onViewDetachedFromWindow(View view) { 5054 // Don't do anything. 5055 } 5056 5057 void addAccessibilityPane(View pane) { 5058 mPanesToVisible.put(pane, pane.isShown() && pane.getWindowVisibility() == VISIBLE); 5059 pane.addOnAttachStateChangeListener(this); 5060 if (pane.isAttachedToWindow()) { 5061 registerForLayoutCallback(pane); 5062 } 5063 } 5064 5065 void removeAccessibilityPane(View pane) { 5066 mPanesToVisible.remove(pane); 5067 pane.removeOnAttachStateChangeListener(this); 5068 unregisterForLayoutCallback(pane); 5069 } 5070 5071 private void checkPaneVisibility(Map.Entry<View, Boolean> panesToVisibleEntry) { 5072 View pane = panesToVisibleEntry.getKey(); 5073 boolean oldVisibility = panesToVisibleEntry.getValue(); 5074 boolean newVisibility = pane.isShown() && pane.getWindowVisibility() == VISIBLE; 5075 if (oldVisibility != newVisibility) { 5076 int contentChangeType = newVisibility 5077 ? AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_APPEARED 5078 : AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED; 5079 notifyViewAccessibilityStateChangedIfNeeded(pane, contentChangeType); 5080 panesToVisibleEntry.setValue(newVisibility); 5081 } 5082 } 5083 5084 private void registerForLayoutCallback(View view) { 5085 view.getViewTreeObserver().addOnGlobalLayoutListener(this); 5086 } 5087 5088 private void unregisterForLayoutCallback(View view) { 5089 ViewTreeObserver observer = view.getViewTreeObserver(); 5090 observer.removeOnGlobalLayoutListener(this); 5091 } 5092 } 5093 5094 static class UnhandledKeyEventManager { 5095 // The number of views with listeners is usually much fewer than the number of views. 5096 // This means it should be faster to only check parent chains of views with listeners than 5097 // to check every view for listeners. 5098 private static final ArrayList<WeakReference<View>> sViewsWithListeners = new ArrayList<>(); 5099 5100 // This is a cache (per keypress) of all the views which either have listeners or 5101 // contain a view with listeners. This is only accessed on the UI thread. 5102 private @Nullable WeakHashMap<View, Boolean> mViewsContainingListeners = null; 5103 5104 // Keeps track of which Views have unhandled key focus for which keys. This doesn't 5105 // include modifiers. 5106 private SparseArray<WeakReference<View>> mCapturedKeys = null; 5107 5108 // Set to the last KeyEvent which went through preDispatch. Currently, it's difficult to 5109 // unify the "earliest" point we can handle a KeyEvent in all code-paths. However, this 5110 // de-duplicating behavior is left as an implementation detail only since things may 5111 // become cleaner as more of supportlib moves towards the component model. 5112 private WeakReference<KeyEvent> mLastDispatchedPreViewKeyEvent = null; 5113 5114 private SparseArray<WeakReference<View>> getCapturedKeys() { 5115 if (mCapturedKeys == null) { 5116 mCapturedKeys = new SparseArray<>(); 5117 } 5118 return mCapturedKeys; 5119 } 5120 5121 static UnhandledKeyEventManager at(View root) { 5122 UnhandledKeyEventManager manager = (UnhandledKeyEventManager) 5123 root.getTag(R.id.tag_unhandled_key_event_manager); 5124 if (manager == null) { 5125 manager = new UnhandledKeyEventManager(); 5126 root.setTag(R.id.tag_unhandled_key_event_manager, manager); 5127 } 5128 return manager; 5129 } 5130 5131 boolean dispatch(View root, KeyEvent event) { 5132 if (event.getAction() == KeyEvent.ACTION_DOWN) { 5133 recalcViewsWithUnhandled(); 5134 } 5135 5136 View consumer = dispatchInOrder(root, event); 5137 5138 // If an unhandled listener handles one, then keep track of it so that the consuming 5139 // view is first to receive its repeats and release as well. 5140 if (event.getAction() == KeyEvent.ACTION_DOWN) { 5141 int keycode = event.getKeyCode(); 5142 if (consumer != null && !KeyEvent.isModifierKey(keycode)) { 5143 getCapturedKeys().put(keycode, new WeakReference<>(consumer)); 5144 } 5145 } 5146 return consumer != null; 5147 } 5148 5149 private @Nullable View dispatchInOrder(View view, KeyEvent event) { 5150 if (mViewsContainingListeners == null || !mViewsContainingListeners.containsKey(view)) { 5151 return null; 5152 } 5153 if (view instanceof ViewGroup) { 5154 ViewGroup vg = (ViewGroup) view; 5155 // No access to internal ViewGroup ordering here, so just use child order. 5156 for (int i = vg.getChildCount() - 1; i >= 0; --i) { 5157 View v = vg.getChildAt(i); 5158 View consumer = dispatchInOrder(v, event); 5159 if (consumer != null) { 5160 return consumer; 5161 } 5162 } 5163 } 5164 if (onUnhandledKeyEvent(view, event)) { 5165 return view; 5166 } 5167 return null; 5168 } 5169 5170 /** 5171 * Called before the event gets dispatched to the view hierarchy 5172 * @return {@code true} if an unhandled handler has focus and consumed the event 5173 */ 5174 boolean preDispatch(KeyEvent event) { 5175 // De-duplicate calls to preDispatch. See comment on mLastDispatchedPreViewKeyEvent. 5176 if (mLastDispatchedPreViewKeyEvent != null 5177 && mLastDispatchedPreViewKeyEvent.get() == event) { 5178 return false; 5179 } 5180 mLastDispatchedPreViewKeyEvent = new WeakReference<>(event); 5181 5182 // Always clean-up 'up' events since it's possible for earlier dispatch stages to 5183 // consume them without consuming the corresponding 'down' event. 5184 WeakReference<View> currentReceiver = null; 5185 SparseArray<WeakReference<View>> capturedKeys = getCapturedKeys(); 5186 if (event.getAction() == KeyEvent.ACTION_UP) { 5187 int idx = capturedKeys.indexOfKey(event.getKeyCode()); 5188 if (idx >= 0) { 5189 currentReceiver = capturedKeys.valueAt(idx); 5190 capturedKeys.removeAt(idx); 5191 } 5192 } 5193 if (currentReceiver == null) { 5194 currentReceiver = capturedKeys.get(event.getKeyCode()); 5195 } 5196 if (currentReceiver != null) { 5197 View target = currentReceiver.get(); 5198 if (target != null && target.isAttachedToWindow()) { 5199 onUnhandledKeyEvent(target, event); 5200 } 5201 // consume anyways so that we don't feed uncaptured key events to other views 5202 return true; 5203 } 5204 return false; 5205 } 5206 5207 private boolean onUnhandledKeyEvent(@NonNull View v, @NonNull KeyEvent event) { 5208 @SuppressWarnings("unchecked") 5209 ArrayList<OnUnhandledKeyEventListenerCompat> viewListeners = 5210 (ArrayList<OnUnhandledKeyEventListenerCompat>) 5211 v.getTag(R.id.tag_unhandled_key_listeners); 5212 if (viewListeners != null) { 5213 for (int i = viewListeners.size() - 1; i >= 0; --i) { 5214 if (viewListeners.get(i).onUnhandledKeyEvent(v, event)) { 5215 return true; 5216 } 5217 } 5218 } 5219 return false; 5220 } 5221 5222 /** 5223 * Registers that a view has at least one {@link OnUnhandledKeyEventListenerCompat}. Does 5224 * nothing if the view is already registered. 5225 */ 5226 static void registerListeningView(View v) { 5227 synchronized (sViewsWithListeners) { 5228 for (WeakReference<View> wv : sViewsWithListeners) { 5229 if (wv.get() == v) { 5230 return; 5231 } 5232 } 5233 sViewsWithListeners.add(new WeakReference<>(v)); 5234 } 5235 } 5236 5237 static void unregisterListeningView(View v) { 5238 synchronized (sViewsWithListeners) { 5239 for (int i = 0; i < sViewsWithListeners.size(); ++i) { 5240 if (sViewsWithListeners.get(i).get() == v) { 5241 sViewsWithListeners.remove(i); 5242 return; 5243 } 5244 } 5245 } 5246 } 5247 5248 private void recalcViewsWithUnhandled() { 5249 if (mViewsContainingListeners != null) { 5250 mViewsContainingListeners.clear(); 5251 } 5252 if (sViewsWithListeners.isEmpty()) { 5253 return; 5254 } 5255 synchronized (sViewsWithListeners) { 5256 if (mViewsContainingListeners == null) { 5257 mViewsContainingListeners = new WeakHashMap<>(); 5258 } 5259 for (int i = sViewsWithListeners.size() - 1; i >= 0; --i) { 5260 WeakReference<View> vw = sViewsWithListeners.get(i); 5261 View v = vw.get(); 5262 if (v == null) { 5263 sViewsWithListeners.remove(i); 5264 } else { 5265 mViewsContainingListeners.put(v, Boolean.TRUE); 5266 ViewParent nxt = v.getParent(); 5267 while (nxt instanceof View) { 5268 mViewsContainingListeners.put((View) nxt, Boolean.TRUE); 5269 nxt = nxt.getParent(); 5270 } 5271 } 5272 } 5273 } 5274 } 5275 } 5276 5277 @RequiresApi(21) 5278 private static class Api21Impl { 5279 private Api21Impl() { 5280 // This class is not instantiable. 5281 } 5282 5283 // Only called on SDK 21 and 22 5284 public static @Nullable WindowInsetsCompat getRootWindowInsets(@NonNull View v) { 5285 return WindowInsetsCompat.Api21ReflectionHolder.getRootWindowInsets(v); 5286 } 5287 5288 static WindowInsetsCompat computeSystemWindowInsets(@NonNull View v, 5289 @NonNull WindowInsetsCompat insets, @NonNull Rect outLocalInsets) { 5290 WindowInsets platformInsets = insets.toWindowInsets(); 5291 if (platformInsets != null) { 5292 return WindowInsetsCompat.toWindowInsetsCompat( 5293 v.computeSystemWindowInsets(platformInsets, outLocalInsets), v); 5294 } else { 5295 outLocalInsets.setEmpty(); 5296 return insets; 5297 } 5298 } 5299 5300 static void setOnApplyWindowInsetsListener(final @NonNull View v, 5301 final @Nullable OnApplyWindowInsetsListener listener) { 5302 final View.OnApplyWindowInsetsListener wrappedUserListener = listener != null 5303 ? new View.OnApplyWindowInsetsListener() { 5304 WindowInsetsCompat mLastInsets = null; 5305 5306 @Override 5307 public WindowInsets onApplyWindowInsets(final View view, 5308 final WindowInsets insets) { 5309 WindowInsetsCompat compatInsets = 5310 WindowInsetsCompat.toWindowInsetsCompat(insets, view); 5311 if (Build.VERSION.SDK_INT < 30) { 5312 callCompatInsetAnimationCallback(insets, v); 5313 5314 if (compatInsets.equals(mLastInsets)) { 5315 // We got the same insets we just return the previously computed 5316 // insets. 5317 return listener.onApplyWindowInsets(view, compatInsets) 5318 .toWindowInsets(); 5319 } 5320 } 5321 mLastInsets = compatInsets; 5322 compatInsets = listener.onApplyWindowInsets(view, compatInsets); 5323 5324 if (Build.VERSION.SDK_INT >= 30) { 5325 return compatInsets.toWindowInsets(); 5326 } 5327 5328 // On API < 30, the visibleInsets, used to built WindowInsetsCompat, are 5329 // updated after the insets dispatch so we don't have the updated 5330 // visible insets at that point. As a workaround, we re-apply the insets 5331 // so we know that we'll have the right value the next time it's called. 5332 requestApplyInsets(view); 5333 // Keep a copy in case the insets haven't changed on the next call so we 5334 // don't need to call the listener again. 5335 5336 return compatInsets.toWindowInsets(); 5337 } 5338 } 5339 : null; 5340 5341 // For backward compatibility of WindowInsetsAnimation, we use an 5342 // OnApplyWindowInsetsListener. We use the view tags to keep track of both listeners 5343 if (Build.VERSION.SDK_INT < 30) { 5344 v.setTag(R.id.tag_on_apply_window_listener, wrappedUserListener); 5345 } 5346 5347 final Object compatInsetsDispatch = v.getTag(R.id.tag_compat_insets_dispatch); 5348 if (compatInsetsDispatch != null) { 5349 // Don't call `v.setOnApplyWindowInsetsListener`. Otherwise, it will overwrite the 5350 // compat-dispatch listener. The compat-dispatch listener will make sure listeners 5351 // stored with `tag_on_apply_window_listener` and 5352 // `tag_window_insets_animation_callback` are get called. 5353 return; 5354 } 5355 5356 if (wrappedUserListener != null) { 5357 v.setOnApplyWindowInsetsListener(wrappedUserListener); 5358 } else { 5359 // If the listener is null, we need to make sure our compat listener, if any, is 5360 // set in-lieu of the listener being removed. 5361 final View.OnApplyWindowInsetsListener compatInsetsAnimationCallback = 5362 (View.OnApplyWindowInsetsListener) v.getTag( 5363 R.id.tag_window_insets_animation_callback); 5364 v.setOnApplyWindowInsetsListener(compatInsetsAnimationCallback); 5365 } 5366 } 5367 5368 /** 5369 * The backport of {@link WindowInsetsAnimationCompat.Callback} on API < 30 relies on 5370 * onApplyWindowInsetsListener, so if this callback is set, we'll call it in this method 5371 */ 5372 static void callCompatInsetAnimationCallback(final @NonNull WindowInsets insets, 5373 final @NonNull View v) { 5374 // In case a WindowInsetsAnimationCompat.Callback is set, make sure to 5375 // call its compat listener. 5376 View.OnApplyWindowInsetsListener insetsAnimationCallback = 5377 (View.OnApplyWindowInsetsListener) v.getTag( 5378 R.id.tag_window_insets_animation_callback); 5379 if (insetsAnimationCallback != null) { 5380 insetsAnimationCallback.onApplyWindowInsets(v, insets); 5381 } 5382 } 5383 5384 static boolean dispatchNestedFling(@NonNull View view, float velocityX, float velocityY, 5385 boolean consumed) { 5386 return view.dispatchNestedFling(velocityX, velocityY, consumed); 5387 } 5388 5389 static boolean dispatchNestedPreFling(@NonNull View view, float velocityX, 5390 float velocityY) { 5391 return view.dispatchNestedPreFling(velocityX, velocityY); 5392 } 5393 5394 static float getZ(@NonNull View view) { 5395 return view.getZ(); 5396 } 5397 5398 static void setZ(@NonNull View view, float z) { 5399 view.setZ(z); 5400 } 5401 5402 static void setElevation(View view, float elevation) { 5403 view.setElevation(elevation); 5404 } 5405 5406 static void setTranslationZ(View view, float translationZ) { 5407 view.setTranslationZ(translationZ); 5408 } 5409 5410 static float getTranslationZ(View view) { 5411 return view.getTranslationZ(); 5412 } 5413 5414 static void setTransitionName(View view, String transitionName) { 5415 view.setTransitionName(transitionName); 5416 } 5417 5418 static boolean isImportantForAccessibility(View view) { 5419 return view.isImportantForAccessibility(); 5420 } 5421 5422 static float getElevation(View view) { 5423 return view.getElevation(); 5424 } 5425 5426 static String getTransitionName(View view) { 5427 return view.getTransitionName(); 5428 } 5429 5430 static void setBackgroundTintList(View view, ColorStateList tint) { 5431 view.setBackgroundTintList(tint); 5432 } 5433 5434 static ColorStateList getBackgroundTintList(View view) { 5435 return view.getBackgroundTintList(); 5436 } 5437 5438 static PorterDuff.Mode getBackgroundTintMode(View view) { 5439 return view.getBackgroundTintMode(); 5440 } 5441 5442 static void setBackgroundTintMode(View view, PorterDuff.Mode tintMode) { 5443 view.setBackgroundTintMode(tintMode); 5444 } 5445 5446 static void setNestedScrollingEnabled(View view, boolean enabled) { 5447 view.setNestedScrollingEnabled(enabled); 5448 } 5449 5450 static boolean isNestedScrollingEnabled(View view) { 5451 return view.isNestedScrollingEnabled(); 5452 } 5453 5454 static boolean startNestedScroll(View view, int axes) { 5455 return view.startNestedScroll(axes); 5456 } 5457 5458 static void stopNestedScroll(View view) { 5459 view.stopNestedScroll(); 5460 } 5461 5462 static boolean hasNestedScrollingParent(View view) { 5463 return view.hasNestedScrollingParent(); 5464 } 5465 5466 static boolean dispatchNestedScroll(View view, int dxConsumed, int dyConsumed, 5467 int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) { 5468 return view.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, 5469 offsetInWindow); 5470 } 5471 5472 static boolean dispatchNestedPreScroll(View view, int dx, int dy, int[] consumed, 5473 int[] offsetInWindow) { 5474 return view.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow); 5475 } 5476 } 5477 5478 @RequiresApi(23) 5479 private static class Api23Impl { 5480 private Api23Impl() { 5481 // This class is not instantiable. 5482 } 5483 5484 public static @Nullable WindowInsetsCompat getRootWindowInsets(@NonNull View v) { 5485 final WindowInsets wi = v.getRootWindowInsets(); 5486 if (wi == null) return null; 5487 5488 final WindowInsetsCompat insets = WindowInsetsCompat.toWindowInsetsCompat(wi); 5489 // This looks strange, but the WindowInsetsCompat instance still needs to know about 5490 // what the root window insets, and the root view visible bounds are 5491 insets.setRootWindowInsets(insets); 5492 insets.copyRootViewBounds(v.getRootView()); 5493 return insets; 5494 } 5495 5496 static void setScrollIndicators(@NonNull View view, int indicators) { 5497 view.setScrollIndicators(indicators); 5498 } 5499 5500 static void setScrollIndicators(@NonNull View view, int indicators, int mask) { 5501 view.setScrollIndicators(indicators, mask); 5502 } 5503 5504 static int getScrollIndicators(@NonNull View view) { 5505 return view.getScrollIndicators(); 5506 } 5507 } 5508 5509 @RequiresApi(29) 5510 private static class Api29Impl { 5511 private Api29Impl() { 5512 // This class is not instantiable. 5513 } 5514 5515 static void saveAttributeDataForStyleable(@NonNull View view, 5516 @NonNull Context context, int @NonNull [] styleable, @Nullable AttributeSet attrs, 5517 @NonNull TypedArray t, int defStyleAttr, int defStyleRes) { 5518 view.saveAttributeDataForStyleable( 5519 context, styleable, attrs, t, defStyleAttr, defStyleRes); 5520 } 5521 5522 static View.AccessibilityDelegate getAccessibilityDelegate(View view) { 5523 return view.getAccessibilityDelegate(); 5524 } 5525 5526 static void setSystemGestureExclusionRects(View view, List<Rect> rects) { 5527 view.setSystemGestureExclusionRects(rects); 5528 } 5529 5530 static List<Rect> getSystemGestureExclusionRects(View view) { 5531 return view.getSystemGestureExclusionRects(); 5532 } 5533 5534 static ContentCaptureSession getContentCaptureSession(View view) { 5535 return view.getContentCaptureSession(); 5536 } 5537 5538 static void setContentCaptureSession(View view, 5539 ContentCaptureSessionCompat contentCaptureSession) { 5540 view.setContentCaptureSession(contentCaptureSession == null 5541 ? null : contentCaptureSession.toContentCaptureSession()); 5542 } 5543 5544 static void transformMatrixToGlobal(View view, Matrix matrix) { 5545 view.transformMatrixToGlobal(matrix); 5546 } 5547 } 5548 5549 @RequiresApi(30) 5550 private static class Api30Impl { 5551 private Api30Impl() { 5552 // This class is not instantiable. 5553 } 5554 5555 public static @Nullable WindowInsetsControllerCompat getWindowInsetsController( 5556 @NonNull View view) { 5557 WindowInsetsController windowInsetsController = view.getWindowInsetsController(); 5558 return windowInsetsController != null 5559 ? WindowInsetsControllerCompat.toWindowInsetsControllerCompat( 5560 windowInsetsController) : null; 5561 } 5562 5563 static void setStateDescription(View view, CharSequence stateDescription) { 5564 view.setStateDescription(stateDescription); 5565 } 5566 5567 static CharSequence getStateDescription(View view) { 5568 return view.getStateDescription(); 5569 } 5570 5571 static void setImportantForContentCapture(View view, int mode) { 5572 view.setImportantForContentCapture(mode); 5573 } 5574 5575 static boolean isImportantForContentCapture(View view) { 5576 return view.isImportantForContentCapture(); 5577 } 5578 5579 static int getImportantForContentCapture(View view) { 5580 return view.getImportantForContentCapture(); 5581 } 5582 5583 static WindowInsets dispatchApplyWindowInsets(View view, WindowInsets insets) { 5584 return view.dispatchApplyWindowInsets(insets); 5585 } 5586 } 5587 5588 @RequiresApi(26) 5589 static class Api26Impl { 5590 private Api26Impl() { 5591 // This class is not instantiable. 5592 } 5593 5594 static void setAutofillHints(@NonNull View view, String... autofillHints) { 5595 view.setAutofillHints(autofillHints); 5596 } 5597 5598 static void setTooltipText(@NonNull View view, CharSequence tooltipText) { 5599 view.setTooltipText(tooltipText); 5600 } 5601 5602 static int getNextClusterForwardId(@NonNull View view) { 5603 return view.getNextClusterForwardId(); 5604 } 5605 5606 static void setNextClusterForwardId(View view, int nextClusterForwardId) { 5607 view.setNextClusterForwardId(nextClusterForwardId); 5608 } 5609 5610 static boolean isKeyboardNavigationCluster(@NonNull View view) { 5611 return view.isKeyboardNavigationCluster(); 5612 } 5613 5614 static void setKeyboardNavigationCluster(@NonNull View view, boolean isCluster) { 5615 view.setKeyboardNavigationCluster(isCluster); 5616 } 5617 5618 static boolean isFocusedByDefault(@NonNull View view) { 5619 return view.isFocusedByDefault(); 5620 } 5621 5622 static void setFocusedByDefault(@NonNull View view, boolean isFocusedByDefault) { 5623 view.setFocusedByDefault(isFocusedByDefault); 5624 } 5625 5626 static View keyboardNavigationClusterSearch(@NonNull View view, View currentCluster, 5627 int direction) { 5628 return view.keyboardNavigationClusterSearch(currentCluster, direction); 5629 } 5630 5631 static void addKeyboardNavigationClusters(@NonNull View view, Collection<View> views, 5632 int direction) { 5633 view.addKeyboardNavigationClusters(views, direction); 5634 } 5635 5636 static boolean restoreDefaultFocus(@NonNull View view) { 5637 return view.restoreDefaultFocus(); 5638 } 5639 5640 static boolean hasExplicitFocusable(@NonNull View view) { 5641 return view.hasExplicitFocusable(); 5642 } 5643 5644 static int getImportantForAutofill(View view) { 5645 return view.getImportantForAutofill(); 5646 } 5647 5648 static void setImportantForAutofill(View view, int mode) { 5649 view.setImportantForAutofill(mode); 5650 } 5651 5652 static boolean isImportantForAutofill(View view) { 5653 return view.isImportantForAutofill(); 5654 } 5655 5656 public static AutofillId getAutofillId(View view) { 5657 return view.getAutofillId(); 5658 } 5659 } 5660 5661 @RequiresApi(24) 5662 static class Api24Impl { 5663 private Api24Impl() { 5664 // This class is not instantiable. 5665 } 5666 5667 static void setPointerIcon(@NonNull View view, PointerIcon pointerIcon) { 5668 view.setPointerIcon(pointerIcon); 5669 } 5670 5671 static boolean startDragAndDrop(@NonNull View view, @Nullable ClipData data, 5672 View.@NonNull DragShadowBuilder shadowBuilder, @Nullable Object myLocalState, 5673 int flags) { 5674 return view.startDragAndDrop(data, shadowBuilder, myLocalState, flags); 5675 } 5676 5677 static void cancelDragAndDrop(@NonNull View view) { 5678 view.cancelDragAndDrop(); 5679 } 5680 5681 static void updateDragShadow(@NonNull View view, 5682 View.@NonNull DragShadowBuilder shadowBuilder) { 5683 view.updateDragShadow(shadowBuilder); 5684 } 5685 5686 static void dispatchStartTemporaryDetach(View view) { 5687 view.dispatchStartTemporaryDetach(); 5688 } 5689 5690 static void dispatchFinishTemporaryDetach(View view) { 5691 view.dispatchFinishTemporaryDetach(); 5692 } 5693 } 5694 5695 @RequiresApi(28) 5696 static class Api28Impl { 5697 private Api28Impl() { 5698 // This class is not instantiable. 5699 } 5700 5701 @SuppressWarnings({"unchecked", "TypeParameterUnusedInFormals"}) 5702 static <T> T requireViewById(View view, int id) { 5703 return (T) view.requireViewById(id); 5704 } 5705 5706 static CharSequence getAccessibilityPaneTitle(View view) { 5707 return view.getAccessibilityPaneTitle(); 5708 } 5709 5710 static void setAccessibilityPaneTitle(View view, 5711 CharSequence accessibilityPaneTitle) { 5712 view.setAccessibilityPaneTitle(accessibilityPaneTitle); 5713 } 5714 5715 static void setAccessibilityHeading(View view, boolean isHeading) { 5716 view.setAccessibilityHeading(isHeading); 5717 } 5718 5719 static boolean isAccessibilityHeading(View view) { 5720 return view.isAccessibilityHeading(); 5721 } 5722 5723 static boolean isScreenReaderFocusable(View view) { 5724 return view.isScreenReaderFocusable(); 5725 } 5726 5727 static void setScreenReaderFocusable(View view, boolean screenReaderFocusable) { 5728 view.setScreenReaderFocusable(screenReaderFocusable); 5729 } 5730 5731 @SuppressWarnings("unchecked") 5732 static void addOnUnhandledKeyEventListener(@NonNull View v, 5733 final @NonNull OnUnhandledKeyEventListenerCompat listener) { 5734 SimpleArrayMap<OnUnhandledKeyEventListenerCompat, View.OnUnhandledKeyEventListener> 5735 viewListeners = (SimpleArrayMap<OnUnhandledKeyEventListenerCompat, 5736 View.OnUnhandledKeyEventListener>) 5737 v.getTag(R.id.tag_unhandled_key_listeners); 5738 if (viewListeners == null) { 5739 viewListeners = new SimpleArrayMap<>(); 5740 v.setTag(R.id.tag_unhandled_key_listeners, viewListeners); 5741 } 5742 5743 View.OnUnhandledKeyEventListener fwListener = listener::onUnhandledKeyEvent; 5744 5745 viewListeners.put(listener, fwListener); 5746 v.addOnUnhandledKeyEventListener(fwListener); 5747 } 5748 5749 @SuppressWarnings("unchecked") 5750 static void removeOnUnhandledKeyEventListener(@NonNull View v, 5751 @NonNull OnUnhandledKeyEventListenerCompat listener) { 5752 SimpleArrayMap<OnUnhandledKeyEventListenerCompat, View.OnUnhandledKeyEventListener> 5753 viewListeners = (SimpleArrayMap<OnUnhandledKeyEventListenerCompat, 5754 View.OnUnhandledKeyEventListener>) 5755 v.getTag(R.id.tag_unhandled_key_listeners); 5756 if (viewListeners == null) { 5757 return; 5758 } 5759 View.OnUnhandledKeyEventListener fwListener = viewListeners.get(listener); 5760 if (fwListener != null) { 5761 v.removeOnUnhandledKeyEventListener(fwListener); 5762 } 5763 } 5764 5765 public static void setAutofillId(View view, AutofillIdCompat id) { 5766 view.setAutofillId(id == null ? null : id.toAutofillId()); 5767 } 5768 } 5769 5770 @RequiresApi(20) 5771 static class Api20Impl { 5772 private Api20Impl() { 5773 // This class is not instantiable. 5774 } 5775 5776 static void requestApplyInsets(View view) { 5777 view.requestApplyInsets(); 5778 } 5779 5780 static WindowInsets onApplyWindowInsets(View view, WindowInsets insets) { 5781 return view.onApplyWindowInsets(insets); 5782 } 5783 5784 static WindowInsets dispatchApplyWindowInsets(View view, WindowInsets insets) { 5785 return ViewGroupCompat.sCompatInsetsDispatchInstalled 5786 // Dispatches insets in a way compatible with API 30+, but ignores 5787 // View.OnApplyWindowInsetsListener set by the app. They should use 5788 // ViewCompat.OnApplyWindowInsetsListener instead. 5789 ? ViewGroupCompat.dispatchApplyWindowInsets(view, insets) 5790 // Dispatches insets in the legacy way that a view can consume or modify insets 5791 // to be dispatched to its siblings, but View.OnApplyWindowInsetsListener set 5792 // by the app will be respected. 5793 : view.dispatchApplyWindowInsets(insets); 5794 } 5795 } 5796 } 5797