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* &mdash;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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt; 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