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 androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
20 
21 import android.animation.ValueAnimator;
22 import android.annotation.SuppressLint;
23 import android.annotation.TargetApi;
24 import android.content.ClipData;
25 import android.content.Context;
26 import android.content.res.ColorStateList;
27 import android.graphics.Matrix;
28 import android.graphics.Paint;
29 import android.graphics.PorterDuff;
30 import android.graphics.Rect;
31 import android.graphics.drawable.Drawable;
32 import android.os.Build;
33 import android.os.Bundle;
34 import android.util.Log;
35 import android.view.Display;
36 import android.view.MotionEvent;
37 import android.view.PointerIcon;
38 import android.view.VelocityTracker;
39 import android.view.View;
40 import android.view.ViewConfiguration;
41 import android.view.ViewGroup;
42 import android.view.ViewParent;
43 import android.view.WindowInsets;
44 import android.view.WindowManager;
45 import android.view.accessibility.AccessibilityEvent;
46 import android.view.accessibility.AccessibilityNodeProvider;
47 
48 import androidx.annotation.FloatRange;
49 import androidx.annotation.IdRes;
50 import androidx.annotation.IntDef;
51 import androidx.annotation.NonNull;
52 import androidx.annotation.Nullable;
53 import androidx.annotation.Px;
54 import androidx.annotation.RequiresApi;
55 import androidx.annotation.RestrictTo;
56 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
57 import androidx.core.view.accessibility.AccessibilityNodeProviderCompat;
58 
59 import java.lang.annotation.Retention;
60 import java.lang.annotation.RetentionPolicy;
61 import java.lang.reflect.Field;
62 import java.lang.reflect.InvocationTargetException;
63 import java.lang.reflect.Method;
64 import java.util.Collection;
65 import java.util.WeakHashMap;
66 import java.util.concurrent.atomic.AtomicInteger;
67 
68 /**
69  * Helper for accessing features in {@link View}.
70  */
71 public class ViewCompat {
72     private static final String TAG = "ViewCompat";
73 
74     /** @hide */
75     @RestrictTo(LIBRARY_GROUP)
76     @IntDef({View.FOCUS_LEFT, View.FOCUS_UP, View.FOCUS_RIGHT, View.FOCUS_DOWN,
77             View.FOCUS_FORWARD, View.FOCUS_BACKWARD})
78     @Retention(RetentionPolicy.SOURCE)
79     public @interface FocusDirection {}
80 
81     /** @hide */
82     @RestrictTo(LIBRARY_GROUP)
83     @IntDef({View.FOCUS_LEFT, View.FOCUS_UP, View.FOCUS_RIGHT, View.FOCUS_DOWN})
84     @Retention(RetentionPolicy.SOURCE)
85     public @interface FocusRealDirection {}
86 
87     /** @hide */
88     @RestrictTo(LIBRARY_GROUP)
89     @IntDef({View.FOCUS_FORWARD, View.FOCUS_BACKWARD})
90     @Retention(RetentionPolicy.SOURCE)
91     public @interface FocusRelativeDirection {}
92 
93     @IntDef({OVER_SCROLL_ALWAYS, OVER_SCROLL_IF_CONTENT_SCROLLS, OVER_SCROLL_NEVER})
94     @Retention(RetentionPolicy.SOURCE)
95     private @interface OverScroll {}
96 
97     /**
98      * Always allow a user to over-scroll this view, provided it is a
99      * view that can scroll.
100      * @deprecated Use {@link View#OVER_SCROLL_ALWAYS} directly. This constant will be removed in
101      * a future release.
102      */
103     @Deprecated
104     public static final int OVER_SCROLL_ALWAYS = 0;
105 
106     /**
107      * Allow a user to over-scroll this view only if the content is large
108      * enough to meaningfully scroll, provided it is a view that can scroll.
109      * @deprecated Use {@link View#OVER_SCROLL_IF_CONTENT_SCROLLS} directly. This constant will be
110      * removed in a future release.
111      */
112     @Deprecated
113     public static final int OVER_SCROLL_IF_CONTENT_SCROLLS = 1;
114 
115     /**
116      * Never allow a user to over-scroll this view.
117      * @deprecated Use {@link View#OVER_SCROLL_NEVER} directly. This constant will be removed in
118      * a future release.
119      */
120     @Deprecated
121     public static final int OVER_SCROLL_NEVER = 2;
122 
123     @TargetApi(Build.VERSION_CODES.O)
124     @IntDef({
125             View.IMPORTANT_FOR_AUTOFILL_AUTO,
126             View.IMPORTANT_FOR_AUTOFILL_YES,
127             View.IMPORTANT_FOR_AUTOFILL_NO,
128             View.IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS,
129             View.IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS
130     })
131     @Retention(RetentionPolicy.SOURCE)
132     private @interface AutofillImportance {}
133 
134     @IntDef({
135             IMPORTANT_FOR_ACCESSIBILITY_AUTO,
136             IMPORTANT_FOR_ACCESSIBILITY_YES,
137             IMPORTANT_FOR_ACCESSIBILITY_NO,
138             IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
139     })
140     @Retention(RetentionPolicy.SOURCE)
141     private @interface ImportantForAccessibility {}
142 
143     /**
144      * Automatically determine whether a view is important for accessibility.
145      */
146     public static final int IMPORTANT_FOR_ACCESSIBILITY_AUTO = 0x00000000;
147 
148     /**
149      * The view is important for accessibility.
150      */
151     public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 0x00000001;
152 
153     /**
154      * The view is not important for accessibility.
155      */
156     public static final int IMPORTANT_FOR_ACCESSIBILITY_NO = 0x00000002;
157 
158     /**
159      * The view is not important for accessibility, nor are any of its
160      * descendant views.
161      */
162     public static final int IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS = 0x00000004;
163 
164     @IntDef({
165             ACCESSIBILITY_LIVE_REGION_NONE,
166             ACCESSIBILITY_LIVE_REGION_POLITE,
167             ACCESSIBILITY_LIVE_REGION_ASSERTIVE
168     })
169     @Retention(RetentionPolicy.SOURCE)
170     private @interface AccessibilityLiveRegion {}
171 
172     /**
173      * Live region mode specifying that accessibility services should not
174      * automatically announce changes to this view. This is the default live
175      * region mode for most views.
176      * <p>
177      * Use with {@link ViewCompat#setAccessibilityLiveRegion(View, int)}.
178      */
179     public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0x00000000;
180 
181     /**
182      * Live region mode specifying that accessibility services should announce
183      * changes to this view.
184      * <p>
185      * Use with {@link ViewCompat#setAccessibilityLiveRegion(View, int)}.
186      */
187     public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 0x00000001;
188 
189     /**
190      * Live region mode specifying that accessibility services should interrupt
191      * ongoing speech to immediately announce changes to this view.
192      * <p>
193      * Use with {@link ViewCompat#setAccessibilityLiveRegion(View, int)}.
194      */
195     public static final int ACCESSIBILITY_LIVE_REGION_ASSERTIVE = 0x00000002;
196 
197     @IntDef({View.LAYER_TYPE_NONE, View.LAYER_TYPE_SOFTWARE, View.LAYER_TYPE_HARDWARE})
198     @Retention(RetentionPolicy.SOURCE)
199     private @interface LayerType {}
200 
201     /**
202      * Indicates that the view does not have a layer.
203      *
204      * @deprecated Use {@link View#LAYER_TYPE_NONE} directly.
205      */
206     @Deprecated
207     public static final int LAYER_TYPE_NONE = 0;
208 
209     /**
210      * <p>Indicates that the view has a software layer. A software layer is backed
211      * by a bitmap and causes the view to be rendered using Android's software
212      * rendering pipeline, even if hardware acceleration is enabled.</p>
213      *
214      * <p>Software layers have various usages:</p>
215      * <p>When the application is not using hardware acceleration, a software layer
216      * is useful to apply a specific color filter and/or blending mode and/or
217      * translucency to a view and all its children.</p>
218      * <p>When the application is using hardware acceleration, a software layer
219      * is useful to render drawing primitives not supported by the hardware
220      * accelerated pipeline. It can also be used to cache a complex view tree
221      * into a texture and reduce the complexity of drawing operations. For instance,
222      * when animating a complex view tree with a translation, a software layer can
223      * be used to render the view tree only once.</p>
224      * <p>Software layers should be avoided when the affected view tree updates
225      * often. Every update will require to re-render the software layer, which can
226      * potentially be slow (particularly when hardware acceleration is turned on
227      * since the layer will have to be uploaded into a hardware texture after every
228      * update.)</p>
229      *
230      * @deprecated Use {@link View#LAYER_TYPE_SOFTWARE} directly.
231      */
232     @Deprecated
233     public static final int LAYER_TYPE_SOFTWARE = 1;
234 
235     /**
236      * <p>Indicates that the view has a hardware layer. A hardware layer is backed
237      * by a hardware specific texture (generally Frame Buffer Objects or FBO on
238      * OpenGL hardware) and causes the view to be rendered using Android's hardware
239      * rendering pipeline, but only if hardware acceleration is turned on for the
240      * view hierarchy. When hardware acceleration is turned off, hardware layers
241      * behave exactly as {@link View#LAYER_TYPE_SOFTWARE software layers}.</p>
242      *
243      * <p>A hardware layer is useful to apply a specific color filter and/or
244      * blending mode and/or translucency to a view and all its children.</p>
245      * <p>A hardware layer can be used to cache a complex view tree into a
246      * texture and reduce the complexity of drawing operations. For instance,
247      * when animating a complex view tree with a translation, a hardware layer can
248      * be used to render the view tree only once.</p>
249      * <p>A hardware layer can also be used to increase the rendering quality when
250      * rotation transformations are applied on a view. It can also be used to
251      * prevent potential clipping issues when applying 3D transforms on a view.</p>
252      *
253      * @deprecated Use {@link View#LAYER_TYPE_HARDWARE} directly.
254      */
255     @Deprecated
256     public static final int LAYER_TYPE_HARDWARE = 2;
257 
258     @IntDef({
259             LAYOUT_DIRECTION_LTR,
260             LAYOUT_DIRECTION_RTL,
261             LAYOUT_DIRECTION_INHERIT,
262             LAYOUT_DIRECTION_LOCALE})
263     @Retention(RetentionPolicy.SOURCE)
264     private @interface LayoutDirectionMode {}
265 
266     @IntDef({
267             LAYOUT_DIRECTION_LTR,
268             LAYOUT_DIRECTION_RTL
269     })
270     @Retention(RetentionPolicy.SOURCE)
271     private @interface ResolvedLayoutDirectionMode {}
272 
273     /**
274      * Horizontal layout direction of this view is from Left to Right.
275      */
276     public static final int LAYOUT_DIRECTION_LTR = 0;
277 
278     /**
279      * Horizontal layout direction of this view is from Right to Left.
280      */
281     public static final int LAYOUT_DIRECTION_RTL = 1;
282 
283     /**
284      * Horizontal layout direction of this view is inherited from its parent.
285      * Use with {@link #setLayoutDirection}.
286      */
287     public static final int LAYOUT_DIRECTION_INHERIT = 2;
288 
289     /**
290      * Horizontal layout direction of this view is from deduced from the default language
291      * script for the locale. Use with {@link #setLayoutDirection}.
292      */
293     public static final int LAYOUT_DIRECTION_LOCALE = 3;
294 
295     /**
296      * Bits of {@link #getMeasuredWidthAndState} and
297      * {@link #getMeasuredWidthAndState} that provide the actual measured size.
298      *
299      * @deprecated Use {@link View#MEASURED_SIZE_MASK} directly.
300      */
301     @Deprecated
302     public static final int MEASURED_SIZE_MASK = 0x00ffffff;
303 
304     /**
305      * Bits of {@link #getMeasuredWidthAndState} and
306      * {@link #getMeasuredWidthAndState} that provide the additional state bits.
307      *
308      * @deprecated Use {@link View#MEASURED_STATE_MASK} directly.
309      */
310     @Deprecated
311     public static final int MEASURED_STATE_MASK = 0xff000000;
312 
313     /**
314      * Bit shift of {@link #MEASURED_STATE_MASK} to get to the height bits
315      * for functions that combine both width and height into a single int,
316      * such as {@link #getMeasuredState} and the childState argument of
317      * {@link #resolveSizeAndState(int, int, int)}.
318      *
319      * @deprecated Use {@link View#MEASURED_HEIGHT_STATE_SHIFT} directly.
320      */
321     @Deprecated
322     public static final int MEASURED_HEIGHT_STATE_SHIFT = 16;
323 
324     /**
325      * Bit of {@link #getMeasuredWidthAndState} and
326      * {@link #getMeasuredWidthAndState} that indicates the measured size
327      * is smaller that the space the view would like to have.
328      *
329      * @deprecated Use {@link View#MEASURED_STATE_TOO_SMALL} directly.
330      */
331     @Deprecated
332     public static final int MEASURED_STATE_TOO_SMALL = 0x01000000;
333 
334     /**
335      * @hide
336      */
337     @IntDef(value = {SCROLL_AXIS_NONE, SCROLL_AXIS_HORIZONTAL, SCROLL_AXIS_VERTICAL}, flag = true)
338     @Retention(RetentionPolicy.SOURCE)
339     @RestrictTo(LIBRARY_GROUP)
340     public @interface ScrollAxis {}
341 
342     /**
343      * Indicates no axis of view scrolling.
344      */
345     public static final int SCROLL_AXIS_NONE = 0;
346 
347     /**
348      * Indicates scrolling along the horizontal axis.
349      */
350     public static final int SCROLL_AXIS_HORIZONTAL = 1 << 0;
351 
352     /**
353      * Indicates scrolling along the vertical axis.
354      */
355     public static final int SCROLL_AXIS_VERTICAL = 1 << 1;
356 
357     /**
358      * @hide
359      */
360     @IntDef({TYPE_TOUCH, TYPE_NON_TOUCH})
361     @Retention(RetentionPolicy.SOURCE)
362     @RestrictTo(LIBRARY_GROUP)
363     public @interface NestedScrollType {}
364 
365     /**
366      * Indicates that the input type for the gesture is from a user touching the screen.
367      */
368     public static final int TYPE_TOUCH = 0;
369 
370     /**
371      * Indicates that the input type for the gesture is caused by something which is not a user
372      * touching a screen. This is usually from a fling which is settling.
373      */
374     public static final int TYPE_NON_TOUCH = 1;
375 
376     /** @hide */
377     @RestrictTo(LIBRARY_GROUP)
378     @Retention(RetentionPolicy.SOURCE)
379     @IntDef(flag = true,
380             value = {
381                     SCROLL_INDICATOR_TOP,
382                     SCROLL_INDICATOR_BOTTOM,
383                     SCROLL_INDICATOR_LEFT,
384                     SCROLL_INDICATOR_RIGHT,
385                     SCROLL_INDICATOR_START,
386                     SCROLL_INDICATOR_END,
387             })
388     public @interface ScrollIndicators {}
389 
390     /**
391      * Scroll indicator direction for the top edge of the view.
392      *
393      * @see #setScrollIndicators(View, int)
394      * @see #setScrollIndicators(View, int, int)
395      * @see #getScrollIndicators(View)
396      */
397     public static final int SCROLL_INDICATOR_TOP = 0x1;
398 
399     /**
400      * Scroll indicator direction for the bottom edge of the view.
401      *
402      * @see #setScrollIndicators(View, int)
403      * @see #setScrollIndicators(View, int, int)
404      * @see #getScrollIndicators(View)
405      */
406     public static final int SCROLL_INDICATOR_BOTTOM = 0x2;
407 
408     /**
409      * Scroll indicator direction for the left edge of the view.
410      *
411      * @see #setScrollIndicators(View, int)
412      * @see #setScrollIndicators(View, int, int)
413      * @see #getScrollIndicators(View)
414      */
415     public static final int SCROLL_INDICATOR_LEFT = 0x4;
416 
417     /**
418      * Scroll indicator direction for the right edge of the view.
419      *
420      * @see #setScrollIndicators(View, int)
421      * @see #setScrollIndicators(View, int, int)
422      * @see #getScrollIndicators(View)
423      */
424     public static final int SCROLL_INDICATOR_RIGHT = 0x8;
425 
426     /**
427      * Scroll indicator direction for the starting edge of the view.
428      *
429      * @see #setScrollIndicators(View, int)
430      * @see #setScrollIndicators(View, int, int)
431      * @see #getScrollIndicators(View)
432      */
433     public static final int SCROLL_INDICATOR_START = 0x10;
434 
435     /**
436      * Scroll indicator direction for the ending edge of the view.
437      *
438      * @see #setScrollIndicators(View, int)
439      * @see #setScrollIndicators(View, int, int)
440      * @see #getScrollIndicators(View)
441      */
442     public static final int SCROLL_INDICATOR_END = 0x20;
443 
444     private static final AtomicInteger sNextGeneratedId = new AtomicInteger(1);
445 
446     private static Field sMinWidthField;
447     private static boolean sMinWidthFieldFetched;
448     private static Field sMinHeightField;
449     private static boolean sMinHeightFieldFetched;
450 
451     private static Method sDispatchStartTemporaryDetach;
452     private static Method sDispatchFinishTemporaryDetach;
453     private static boolean sTempDetachBound;
454 
455     private static WeakHashMap<View, String> sTransitionNameMap;
456     private static WeakHashMap<View, ViewPropertyAnimatorCompat> sViewPropertyAnimatorMap = null;
457 
458     private static Method sChildrenDrawingOrderMethod;
459     private static Field sAccessibilityDelegateField;
460     private static boolean sAccessibilityDelegateCheckFailed = false;
461 
462     private static ThreadLocal<Rect> sThreadLocalRect;
463 
getEmptyTempRect()464     private static Rect getEmptyTempRect() {
465         if (sThreadLocalRect == null) {
466             sThreadLocalRect = new ThreadLocal<>();
467         }
468         Rect rect = sThreadLocalRect.get();
469         if (rect == null) {
470             rect = new Rect();
471             sThreadLocalRect.set(rect);
472         }
473         rect.setEmpty();
474         return rect;
475     }
476 
477     /**
478      * Check if this view can be scrolled horizontally in a certain direction.
479      *
480      * @param view The View against which to invoke the method.
481      * @param direction Negative to check scrolling left, positive to check scrolling right.
482      * @return true if this view can be scrolled in the specified direction, false otherwise.
483      *
484      * @deprecated Use {@link View#canScrollHorizontally(int)} directly.
485      */
486     @Deprecated
canScrollHorizontally(View view, int direction)487     public static boolean canScrollHorizontally(View view, int direction) {
488         return view.canScrollHorizontally(direction);
489     }
490 
491     /**
492      * Check if this view can be scrolled vertically in a certain direction.
493      *
494      * @param view The View against which to invoke the method.
495      * @param direction Negative to check scrolling up, positive to check scrolling down.
496      * @return true if this view can be scrolled in the specified direction, false otherwise.
497      *
498      * @deprecated Use {@link View#canScrollVertically(int)} directly.
499      */
500     @Deprecated
canScrollVertically(View view, int direction)501     public static boolean canScrollVertically(View view, int direction) {
502         return view.canScrollVertically(direction);
503     }
504 
505     /**
506      * Returns the over-scroll mode for this view. The result will be
507      * one of {@link #OVER_SCROLL_ALWAYS} (default), {@link #OVER_SCROLL_IF_CONTENT_SCROLLS}
508      * (allow over-scrolling only if the view content is larger than the container),
509      * or {@link #OVER_SCROLL_NEVER}.
510      *
511      * @param v The View against which to invoke the method.
512      * @return This view's over-scroll mode.
513      * @deprecated Call {@link View#getOverScrollMode()} directly. This method will be
514      * removed in a future release.
515      */
516     @Deprecated
517     @OverScroll
getOverScrollMode(View v)518     public static int getOverScrollMode(View v) {
519         //noinspection ResourceType
520         return v.getOverScrollMode();
521     }
522 
523     /**
524      * Set the over-scroll mode for this view. Valid over-scroll modes are
525      * {@link #OVER_SCROLL_ALWAYS} (default), {@link #OVER_SCROLL_IF_CONTENT_SCROLLS}
526      * (allow over-scrolling only if the view content is larger than the container),
527      * or {@link #OVER_SCROLL_NEVER}.
528      *
529      * Setting the over-scroll mode of a view will have an effect only if the
530      * view is capable of scrolling.
531      *
532      * @param v The View against which to invoke the method.
533      * @param overScrollMode The new over-scroll mode for this view.
534      * @deprecated Call {@link View#setOverScrollMode(int)} directly. This method will be
535      * removed in a future release.
536      */
537     @Deprecated
setOverScrollMode(View v, @OverScroll int overScrollMode)538     public static void setOverScrollMode(View v, @OverScroll int overScrollMode) {
539         v.setOverScrollMode(overScrollMode);
540     }
541 
542     /**
543      * Called from {@link View#dispatchPopulateAccessibilityEvent(AccessibilityEvent)}
544      * giving a chance to this View to populate the accessibility event with its
545      * text content. While this method is free to modify event
546      * attributes other than text content, doing so should normally be performed in
547      * {@link View#onInitializeAccessibilityEvent(AccessibilityEvent)}.
548      * <p>
549      * Example: Adding formatted date string to an accessibility event in addition
550      *          to the text added by the super implementation:
551      * <pre> public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
552      *     super.onPopulateAccessibilityEvent(event);
553      *     final int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_WEEKDAY;
554      *     String selectedDateUtterance = DateUtils.formatDateTime(mContext,
555      *         mCurrentDate.getTimeInMillis(), flags);
556      *     event.getText().add(selectedDateUtterance);
557      * }</pre>
558      * <p>
559      * If an {@link AccessibilityDelegateCompat} has been specified via calling
560      * {@link ViewCompat#setAccessibilityDelegate(View, AccessibilityDelegateCompat)} its
561      * {@link AccessibilityDelegateCompat#onPopulateAccessibilityEvent(View, AccessibilityEvent)}
562      * is responsible for handling this call.
563      * </p>
564      * <p class="note"><strong>Note:</strong> Always call the super implementation before adding
565      * information to the event, in case the default implementation has basic information to add.
566      * </p>
567      *
568      * @param v The View against which to invoke the method.
569      * @param event The accessibility event which to populate.
570      *
571      * @see View#sendAccessibilityEvent(int)
572      * @see View#dispatchPopulateAccessibilityEvent(AccessibilityEvent)
573      *
574      * @deprecated Call {@link View#onPopulateAccessibilityEvent(AccessibilityEvent)} directly.
575      * This method will be removed in a future release.
576      */
577     @Deprecated
onPopulateAccessibilityEvent(View v, AccessibilityEvent event)578     public static void onPopulateAccessibilityEvent(View v, AccessibilityEvent event) {
579         v.onPopulateAccessibilityEvent(event);
580     }
581 
582     /**
583      * Initializes an {@link AccessibilityEvent} with information about
584      * this View which is the event source. In other words, the source of
585      * an accessibility event is the view whose state change triggered firing
586      * the event.
587      * <p>
588      * Example: Setting the password property of an event in addition
589      *          to properties set by the super implementation:
590      * <pre> public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
591      *     super.onInitializeAccessibilityEvent(event);
592      *     event.setPassword(true);
593      * }</pre>
594      * <p>
595      * If an {@link AccessibilityDelegateCompat} has been specified via calling
596      * {@link ViewCompat#setAccessibilityDelegate(View, AccessibilityDelegateCompat)}, its
597      * {@link AccessibilityDelegateCompat#onInitializeAccessibilityEvent(View, AccessibilityEvent)}
598      * is responsible for handling this call.
599      *
600      * @param v The View against which to invoke the method.
601      * @param event The event to initialize.
602      *
603      * @see View#sendAccessibilityEvent(int)
604      * @see View#dispatchPopulateAccessibilityEvent(AccessibilityEvent)
605      *
606      * @deprecated Call {@link View#onInitializeAccessibilityEvent(AccessibilityEvent)} directly.
607      * This method will be removed in a future release.
608      */
609     @Deprecated
onInitializeAccessibilityEvent(View v, AccessibilityEvent event)610     public static void onInitializeAccessibilityEvent(View v, AccessibilityEvent event) {
611         v.onInitializeAccessibilityEvent(event);
612     }
613 
614     /**
615      * Initializes an {@link AccessibilityNodeInfoCompat} with information
616      * about this view. The base implementation sets:
617      * <ul>
618      * <li>{@link AccessibilityNodeInfoCompat#setParent(View)},</li>
619      * <li>{@link AccessibilityNodeInfoCompat#setBoundsInParent(Rect)},</li>
620      * <li>{@link AccessibilityNodeInfoCompat#setBoundsInScreen(Rect)},</li>
621      * <li>{@link AccessibilityNodeInfoCompat#setPackageName(CharSequence)},</li>
622      * <li>{@link AccessibilityNodeInfoCompat#setClassName(CharSequence)},</li>
623      * <li>{@link AccessibilityNodeInfoCompat#setContentDescription(CharSequence)},</li>
624      * <li>{@link AccessibilityNodeInfoCompat#setEnabled(boolean)},</li>
625      * <li>{@link AccessibilityNodeInfoCompat#setClickable(boolean)},</li>
626      * <li>{@link AccessibilityNodeInfoCompat#setFocusable(boolean)},</li>
627      * <li>{@link AccessibilityNodeInfoCompat#setFocused(boolean)},</li>
628      * <li>{@link AccessibilityNodeInfoCompat#setLongClickable(boolean)},</li>
629      * <li>{@link AccessibilityNodeInfoCompat#setSelected(boolean)},</li>
630      * </ul>
631      * <p>
632      * If an {@link AccessibilityDelegateCompat} has been specified via calling
633      * {@link ViewCompat#setAccessibilityDelegate(View, AccessibilityDelegateCompat)}, its
634      * {@link AccessibilityDelegateCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat)}
635      * method is responsible for handling this call.
636      *
637      * @param v The View against which to invoke the method.
638      * @param info The instance to initialize.
639      */
onInitializeAccessibilityNodeInfo(@onNull View v, AccessibilityNodeInfoCompat info)640     public static void onInitializeAccessibilityNodeInfo(@NonNull View v,
641             AccessibilityNodeInfoCompat info) {
642         v.onInitializeAccessibilityNodeInfo(info.unwrap());
643     }
644 
645     /**
646      * Sets a delegate for implementing accessibility support via composition
647      * (as opposed to inheritance). For more details, see
648      * {@link AccessibilityDelegateCompat}.
649      * <p>
650      * <strong>Note:</strong> On platform versions prior to
651      * {@link android.os.Build.VERSION_CODES#M API 23}, delegate methods on
652      * views in the {@code android.widget.*} package are called <i>before</i>
653      * host methods. This prevents certain properties such as class name from
654      * being modified by overriding
655      * {@link AccessibilityDelegateCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat)},
656      * as any changes will be overwritten by the host class.
657      * <p>
658      * Starting in {@link android.os.Build.VERSION_CODES#M API 23}, delegate
659      * methods are called <i>after</i> host methods, which all properties to be
660      * modified without being overwritten by the host class.
661      *
662      * @param delegate the object to which accessibility method calls should be
663      *                 delegated
664      * @see AccessibilityDelegateCompat
665      */
setAccessibilityDelegate(@onNull View v, AccessibilityDelegateCompat delegate)666     public static void setAccessibilityDelegate(@NonNull View v,
667             AccessibilityDelegateCompat delegate) {
668         v.setAccessibilityDelegate(delegate == null ? null : delegate.getBridge());
669     }
670 
671     /**
672      * Sets the hints that help an {@link android.service.autofill.AutofillService} determine how
673      * to autofill the view with the user's data.
674      *
675      * <p>Typically, there is only one way to autofill a view, but there could be more than one.
676      * For example, if the application accepts either an username or email address to identify
677      * an user.
678      *
679      * <p>These hints are not validated by the Android System, but passed "as is" to the service.
680      * Hence, they can have any value, but it's recommended to use the {@code AUTOFILL_HINT_}
681      * constants such as:
682      * {@link View#AUTOFILL_HINT_USERNAME}, {@link View#AUTOFILL_HINT_PASSWORD},
683      * {@link View#AUTOFILL_HINT_EMAIL_ADDRESS},
684      * {@link View#AUTOFILL_HINT_NAME},
685      * {@link View#AUTOFILL_HINT_PHONE},
686      * {@link View#AUTOFILL_HINT_POSTAL_ADDRESS}, {@link View#AUTOFILL_HINT_POSTAL_CODE},
687      * {@link View#AUTOFILL_HINT_CREDIT_CARD_NUMBER},
688      * {@link View#AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE},
689      * {@link View#AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE},
690      * {@link View#AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY},
691      * {@link View#AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH} or
692      * {@link View#AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR}.
693      *
694      * <p>This method is only supported on API >= 26.
695      * On API 25 and below, it is a no-op</p>
696      *
697      * @param autofillHints The autofill hints to set. If the array is emtpy, {@code null} is set.
698      * @attr ref android.R.styleable#View_autofillHints
699      */
setAutofillHints(@onNull View v, @Nullable String... autofillHints)700     public static void setAutofillHints(@NonNull View v, @Nullable String... autofillHints) {
701         if (Build.VERSION.SDK_INT >= 26) {
702             v.setAutofillHints(autofillHints);
703         }
704     }
705 
706     /**
707      * Gets the mode for determining whether this view is important for autofill.
708      *
709      * <p>See {@link #setImportantForAutofill(View, int)} and {@link #isImportantForAutofill(View)}
710      * for more info about this mode.
711      *
712      * <p>This method is only supported on API >= 26.
713      * On API 25 and below, it will always return {@link View#IMPORTANT_FOR_AUTOFILL_AUTO}.</p>
714      *
715      * @return {@link View#IMPORTANT_FOR_AUTOFILL_AUTO} by default, or value passed to
716      * {@link #setImportantForAutofill(View, int)}.
717      *
718      * @attr ref android.R.styleable#View_importantForAutofill
719      */
720     @SuppressLint("InlinedApi")
getImportantForAutofill(@onNull View v)721     public static @AutofillImportance int getImportantForAutofill(@NonNull View v) {
722         if (Build.VERSION.SDK_INT >= 26) {
723             return v.getImportantForAutofill();
724         }
725         return View.IMPORTANT_FOR_AUTOFILL_AUTO;
726     }
727 
728     /**
729      * Sets the mode for determining whether this view is considered important for autofill.
730      *
731      * <p>The platform determines the importance for autofill automatically but you
732      * can use this method to customize the behavior. For example:
733      *
734      * <ol>
735      *   <li>When the view contents is irrelevant for autofill (for example, a text field used in a
736      *       "Captcha" challenge), it should be {@link View#IMPORTANT_FOR_AUTOFILL_NO}.
737      *   <li>When both the view and its children are irrelevant for autofill (for example, the root
738      *       view of an activity containing a spreadhseet editor), it should be
739      *       {@link View#IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS}.
740      *   <li>When the view content is relevant for autofill but its children aren't (for example,
741      *       a credit card expiration date represented by a custom view that overrides the proper
742      *       autofill methods and has 2 children representing the month and year), it should
743      *       be {@link View#IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS}.
744      * </ol>
745      *
746      * <p><b>NOTE:</strong> setting the mode as does {@link View#IMPORTANT_FOR_AUTOFILL_NO} or
747      * {@link View#IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS} does not guarantee the view (and
748      * its children) will be always be considered not important; for example, when the user
749      * explicitly makes an autofill request, all views are considered important. See
750      * {@link #isImportantForAutofill(View)} for more details about how the View's importance for
751      * autofill is used.
752      *
753      * <p>This method is only supported on API >= 26.
754      * On API 25 and below, it is a no-op</p>
755      *
756      *
757      * @param mode {@link View#IMPORTANT_FOR_AUTOFILL_AUTO},
758      * {@link View#IMPORTANT_FOR_AUTOFILL_YES},
759      * {@link View#IMPORTANT_FOR_AUTOFILL_NO},
760      * {@link View#IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS},
761      * or {@link View#IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS}.
762      *
763      * @attr ref android.R.styleable#View_importantForAutofill
764      */
setImportantForAutofill(@onNull View v, @AutofillImportance int mode)765     public static void setImportantForAutofill(@NonNull View v, @AutofillImportance int mode) {
766         if (Build.VERSION.SDK_INT >= 26) {
767             v.setImportantForAutofill(mode);
768         }
769     }
770 
771     /**
772      * Hints the Android System whether the {@link android.app.assist.AssistStructure.ViewNode}
773      * associated with this view is considered important for autofill purposes.
774      *
775      * <p>Generally speaking, a view is important for autofill if:
776      * <ol>
777      * <li>The view can be autofilled by an {@link android.service.autofill.AutofillService}.
778      * <li>The view contents can help an {@link android.service.autofill.AutofillService}
779      *     determine how other views can be autofilled.
780      * <ol>
781      *
782      * <p>For example, view containers should typically return {@code false} for performance reasons
783      * (since the important info is provided by their children), but if its properties have relevant
784      * information (for example, a resource id called {@code credentials}, it should return
785      * {@code true}. On the other hand, views representing labels or editable fields should
786      * typically return {@code true}, but in some cases they could return {@code false}
787      * (for example, if they're part of a "Captcha" mechanism).
788      *
789      * <p>The value returned by this method depends on the value returned by
790      * {@link #getImportantForAutofill(View)}:
791      *
792      * <ol>
793      *   <li>if it returns {@link View#IMPORTANT_FOR_AUTOFILL_YES} or
794      *       {@link View#IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS},
795      *       then it returns {@code true}
796      *   <li>if it returns {@link View#IMPORTANT_FOR_AUTOFILL_NO} or
797      *       {@link View#IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS},
798      *       then it returns {@code false}
799      *   <li>if it returns {@link View#IMPORTANT_FOR_AUTOFILL_AUTO},
800      *   then it uses some simple heuristics that can return {@code true}
801      *   in some cases (like a container with a resource id), but {@code false} in most.
802      *   <li>otherwise, it returns {@code false}.
803      * </ol>
804      *
805      * <p>When a view is considered important for autofill:
806      * <ul>
807      *   <li>The view might automatically trigger an autofill request when focused on.
808      *   <li>The contents of the view are included in the {@link android.view.ViewStructure}
809      *   used in an autofill request.
810      * </ul>
811      *
812      * <p>On the other hand, when a view is considered not important for autofill:
813      * <ul>
814      *   <li>The view never automatically triggers autofill requests, but it can trigger a manual
815      *       request through {@link android.view.autofill.AutofillManager#requestAutofill(View)}.
816      *   <li>The contents of the view are not included in the {@link android.view.ViewStructure}
817      *   used in an autofill request, unless the request has the
818      *       {@link View#AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS} flag.
819      * </ul>
820      *
821      * <p>This method is only supported on API >= 26.
822      * On API 25 and below, it will always return {@code true}.</p>
823      *
824      * @return whether the view is considered important for autofill.
825      *
826      * @see #setImportantForAutofill(View, int)
827      * @see View#IMPORTANT_FOR_AUTOFILL_AUTO
828      * @see View#IMPORTANT_FOR_AUTOFILL_YES
829      * @see View#IMPORTANT_FOR_AUTOFILL_NO
830      * @see View#IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS
831      * @see View#IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS
832      * @see android.view.autofill.AutofillManager#requestAutofill(View)
833      */
isImportantForAutofill(@onNull View v)834     public static boolean isImportantForAutofill(@NonNull View v) {
835         if (Build.VERSION.SDK_INT >= 26) {
836             return v.isImportantForAutofill();
837         }
838         return true;
839     }
840 
841     /**
842      * Checks whether provided View has an accessibility delegate attached to it.
843      *
844      * @param v The View instance to check
845      * @return True if the View has an accessibility delegate
846      */
hasAccessibilityDelegate(@onNull View v)847     public static boolean hasAccessibilityDelegate(@NonNull View v) {
848         if (sAccessibilityDelegateCheckFailed) {
849             return false; // View implementation might have changed.
850         }
851         if (sAccessibilityDelegateField == null) {
852             try {
853                 sAccessibilityDelegateField = View.class
854                         .getDeclaredField("mAccessibilityDelegate");
855                 sAccessibilityDelegateField.setAccessible(true);
856             } catch (Throwable t) {
857                 sAccessibilityDelegateCheckFailed = true;
858                 return false;
859             }
860         }
861         try {
862             return sAccessibilityDelegateField.get(v) != null;
863         } catch (Throwable t) {
864             sAccessibilityDelegateCheckFailed = true;
865             return false;
866         }
867     }
868 
869     /**
870      * Indicates whether the view is currently tracking transient state that the
871      * app should not need to concern itself with saving and restoring, but that
872      * the framework should take special note to preserve when possible.
873      *
874      * @param view View to check for transient state
875      * @return true if the view has transient state
876      */
hasTransientState(@onNull View view)877     public static boolean hasTransientState(@NonNull View view) {
878         if (Build.VERSION.SDK_INT >= 16) {
879             return view.hasTransientState();
880         }
881         return false;
882     }
883 
884     /**
885      * Set whether this view is currently tracking transient state that the
886      * framework should attempt to preserve when possible.
887      *
888      * @param view View tracking transient state
889      * @param hasTransientState true if this view has transient state
890      */
setHasTransientState(@onNull View view, boolean hasTransientState)891     public static void setHasTransientState(@NonNull View view, boolean hasTransientState) {
892         if (Build.VERSION.SDK_INT >= 16) {
893             view.setHasTransientState(hasTransientState);
894         }
895     }
896 
897     /**
898      * <p>Cause an invalidate to happen on the next animation time step, typically the
899      * next display frame.</p>
900      *
901      * <p>This method can be invoked from outside of the UI thread
902      * only when this View is attached to a window.</p>
903      *
904      * @param view View to invalidate
905      */
postInvalidateOnAnimation(@onNull View view)906     public static void postInvalidateOnAnimation(@NonNull View view) {
907         if (Build.VERSION.SDK_INT >= 16) {
908             view.postInvalidateOnAnimation();
909         } else {
910             view.postInvalidate();
911         }
912     }
913 
914     /**
915      * <p>Cause an invalidate of the specified area to happen on the next animation
916      * time step, typically the next display frame.</p>
917      *
918      * <p>This method can be invoked from outside of the UI thread
919      * only when this View is attached to a window.</p>
920      *
921      * @param view View to invalidate
922      * @param left The left coordinate of the rectangle to invalidate.
923      * @param top The top coordinate of the rectangle to invalidate.
924      * @param right The right coordinate of the rectangle to invalidate.
925      * @param bottom The bottom coordinate of the rectangle to invalidate.
926      */
postInvalidateOnAnimation(@onNull View view, int left, int top, int right, int bottom)927     public static void postInvalidateOnAnimation(@NonNull View view, int left, int top,
928             int right, int bottom) {
929         if (Build.VERSION.SDK_INT >= 16) {
930             view.postInvalidateOnAnimation(left, top, right, bottom);
931         } else {
932             view.postInvalidate(left, top, right, bottom);
933         }
934     }
935 
936     /**
937      * <p>Causes the Runnable to execute on the next animation time step.
938      * The runnable will be run on the user interface thread.</p>
939      *
940      * <p>This method can be invoked from outside of the UI thread
941      * only when this View is attached to a window.</p>
942      *
943      * @param view View to post this Runnable to
944      * @param action The Runnable that will be executed.
945      */
postOnAnimation(@onNull View view, Runnable action)946     public static void postOnAnimation(@NonNull View view, Runnable action) {
947         if (Build.VERSION.SDK_INT >= 16) {
948             view.postOnAnimation(action);
949         } else {
950             view.postDelayed(action, ValueAnimator.getFrameDelay());
951         }
952     }
953 
954     /**
955      * <p>Causes the Runnable to execute on the next animation time step,
956      * after the specified amount of time elapses.
957      * The runnable will be run on the user interface thread.</p>
958      *
959      * <p>This method can be invoked from outside of the UI thread
960      * only when this View is attached to a window.</p>
961      *
962      * @param view The view to post this Runnable to
963      * @param action The Runnable that will be executed.
964      * @param delayMillis The delay (in milliseconds) until the Runnable
965      *        will be executed.
966      */
postOnAnimationDelayed(@onNull View view, Runnable action, long delayMillis)967     public static void postOnAnimationDelayed(@NonNull View view, Runnable action,
968             long delayMillis) {
969         if (Build.VERSION.SDK_INT >= 16) {
970             view.postOnAnimationDelayed(action, delayMillis);
971         } else {
972             view.postDelayed(action, ValueAnimator.getFrameDelay() + delayMillis);
973         }
974     }
975 
976     /**
977      * Gets the mode for determining whether this View is important for accessibility
978      * which is if it fires accessibility events and if it is reported to
979      * accessibility services that query the screen.
980      *
981      * @param view The view whose property to get.
982      * @return The mode for determining whether a View is important for accessibility.
983      *
984      * @see #IMPORTANT_FOR_ACCESSIBILITY_YES
985      * @see #IMPORTANT_FOR_ACCESSIBILITY_NO
986      * @see #IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
987      * @see #IMPORTANT_FOR_ACCESSIBILITY_AUTO
988      */
989     @ImportantForAccessibility
getImportantForAccessibility(@onNull View view)990     public static int getImportantForAccessibility(@NonNull View view) {
991         if (Build.VERSION.SDK_INT >= 16) {
992             return view.getImportantForAccessibility();
993         }
994         return IMPORTANT_FOR_ACCESSIBILITY_AUTO;
995     }
996 
997     /**
998      * Sets how to determine whether this view is important for accessibility
999      * which is if it fires accessibility events and if it is reported to
1000      * accessibility services that query the screen.
1001      * <p>
1002      * <em>Note:</em> If the current platform version does not support the
1003      *  {@link #IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS} mode, then
1004      *  {@link #IMPORTANT_FOR_ACCESSIBILITY_NO} will be used as it is the
1005      *  closest terms of semantics.
1006      * </p>
1007      *
1008      * @param view The view whose property to set.
1009      * @param mode How to determine whether this view is important for accessibility.
1010      *
1011      * @see #IMPORTANT_FOR_ACCESSIBILITY_YES
1012      * @see #IMPORTANT_FOR_ACCESSIBILITY_NO
1013      * @see #IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
1014      * @see #IMPORTANT_FOR_ACCESSIBILITY_AUTO
1015      */
setImportantForAccessibility(@onNull View view, @ImportantForAccessibility int mode)1016     public static void setImportantForAccessibility(@NonNull View view,
1017             @ImportantForAccessibility int mode) {
1018         if (Build.VERSION.SDK_INT >= 19) {
1019             view.setImportantForAccessibility(mode);
1020         } else if (Build.VERSION.SDK_INT >= 16) {
1021             // IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS is not available
1022             // on this platform so replace with IMPORTANT_FOR_ACCESSIBILITY_NO
1023             // which is closer semantically.
1024             if (mode == IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS) {
1025                 mode = IMPORTANT_FOR_ACCESSIBILITY_NO;
1026             }
1027             //noinspection WrongConstant
1028             view.setImportantForAccessibility(mode);
1029         }
1030     }
1031 
1032     /**
1033      * Computes whether this view should be exposed for accessibility. In
1034      * general, views that are interactive or provide information are exposed
1035      * while views that serve only as containers are hidden.
1036      * <p>
1037      * If an ancestor of this view has importance
1038      * {@link #IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS}, this method
1039      * returns <code>false</code>.
1040      * <p>
1041      * Otherwise, the value is computed according to the view's
1042      * {@link #getImportantForAccessibility(View)} value:
1043      * <ol>
1044      * <li>{@link #IMPORTANT_FOR_ACCESSIBILITY_NO} or
1045      * {@link #IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS}, return <code>false
1046      * </code>
1047      * <li>{@link #IMPORTANT_FOR_ACCESSIBILITY_YES}, return <code>true</code>
1048      * <li>{@link #IMPORTANT_FOR_ACCESSIBILITY_AUTO}, return <code>true</code> if
1049      * view satisfies any of the following:
1050      * <ul>
1051      * <li>Is actionable, e.g. {@link View#isClickable()},
1052      * {@link View#isLongClickable()}, or {@link View#isFocusable()}
1053      * <li>Has an {@link AccessibilityDelegateCompat}
1054      * <li>Has an interaction listener, e.g. {@link View.OnTouchListener},
1055      * {@link View.OnKeyListener}, etc.
1056      * <li>Is an accessibility live region, e.g.
1057      * {@link #getAccessibilityLiveRegion(View)} is not
1058      * {@link #ACCESSIBILITY_LIVE_REGION_NONE}.
1059      * </ul>
1060      * </ol>
1061      * <p>
1062      * <em>Note:</em> Prior to API 21, this method will always return {@code true}.
1063      *
1064      * @return Whether the view is exposed for accessibility.
1065      * @see #setImportantForAccessibility(View, int)
1066      * @see #getImportantForAccessibility(View)
1067      */
isImportantForAccessibility(@onNull View view)1068     public static boolean isImportantForAccessibility(@NonNull View view) {
1069         if (Build.VERSION.SDK_INT >= 21) {
1070             return view.isImportantForAccessibility();
1071         }
1072         return true;
1073     }
1074 
1075     /**
1076      * Performs the specified accessibility action on the view. For
1077      * possible accessibility actions look at {@link AccessibilityNodeInfoCompat}.
1078      * <p>
1079      * If an {@link AccessibilityDelegateCompat} has been specified via calling
1080      * {@link #setAccessibilityDelegate(View, AccessibilityDelegateCompat)} its
1081      * {@link AccessibilityDelegateCompat#performAccessibilityAction(View, int, Bundle)}
1082      * is responsible for handling this call.
1083      * </p>
1084      *
1085      * @param action The action to perform.
1086      * @param arguments Optional action arguments.
1087      * @return Whether the action was performed.
1088      */
performAccessibilityAction(@onNull View view, int action, Bundle arguments)1089     public static boolean performAccessibilityAction(@NonNull View view, int action,
1090             Bundle arguments) {
1091         if (Build.VERSION.SDK_INT >= 16) {
1092             return view.performAccessibilityAction(action, arguments);
1093         }
1094         return false;
1095     }
1096 
1097     /**
1098      * Gets the provider for managing a virtual view hierarchy rooted at this View
1099      * and reported to {@link android.accessibilityservice.AccessibilityService}s
1100      * that explore the window content.
1101      * <p>
1102      * If this method returns an instance, this instance is responsible for managing
1103      * {@link AccessibilityNodeInfoCompat}s describing the virtual sub-tree rooted at
1104      * this View including the one representing the View itself. Similarly the returned
1105      * instance is responsible for performing accessibility actions on any virtual
1106      * view or the root view itself.
1107      * </p>
1108      * <p>
1109      * If an {@link AccessibilityDelegateCompat} has been specified via calling
1110      * {@link #setAccessibilityDelegate(View, AccessibilityDelegateCompat)} its
1111      * {@link AccessibilityDelegateCompat#getAccessibilityNodeProvider(View)}
1112      * is responsible for handling this call.
1113      * </p>
1114      *
1115      * @param view The view whose property to get.
1116      * @return The provider.
1117      *
1118      * @see AccessibilityNodeProviderCompat
1119      */
getAccessibilityNodeProvider(@onNull View view)1120     public static AccessibilityNodeProviderCompat getAccessibilityNodeProvider(@NonNull View view) {
1121         if (Build.VERSION.SDK_INT >= 16) {
1122             AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
1123             if (provider != null) {
1124                 return new AccessibilityNodeProviderCompat(provider);
1125             }
1126         }
1127         return null;
1128     }
1129 
1130     /**
1131      * The opacity of the view. This is a value from 0 to 1, where 0 means the view is
1132      * completely transparent and 1 means the view is completely opaque.
1133      *
1134      * <p>By default this is 1.0f.
1135      * @return The opacity of the view.
1136      *
1137      * @deprecated Use {@link View#getAlpha()} directly.
1138      */
1139     @Deprecated
getAlpha(View view)1140     public static float getAlpha(View view) {
1141         return view.getAlpha();
1142     }
1143 
1144     /**
1145      * <p>Specifies the type of layer backing this view. The layer can be
1146      * {@link View#LAYER_TYPE_NONE disabled}, {@link View#LAYER_TYPE_SOFTWARE software} or
1147      * {@link View#LAYER_TYPE_HARDWARE hardware}.</p>
1148      *
1149      * <p>A layer is associated with an optional {@link android.graphics.Paint}
1150      * instance that controls how the layer is composed on screen. The following
1151      * properties of the paint are taken into account when composing the layer:</p>
1152      * <ul>
1153      * <li>{@link android.graphics.Paint#getAlpha() Translucency (alpha)}</li>
1154      * <li>{@link android.graphics.Paint#getXfermode() Blending mode}</li>
1155      * <li>{@link android.graphics.Paint#getColorFilter() Color filter}</li>
1156      * </ul>
1157      *
1158      * <p>If this view has an alpha value set to < 1.0 by calling
1159      * setAlpha(float), the alpha value of the layer's paint is replaced by
1160      * this view's alpha value. Calling setAlpha(float) is therefore
1161      * equivalent to setting a hardware layer on this view and providing a paint with
1162      * the desired alpha value.<p>
1163      *
1164      * <p>Refer to the documentation of {@link View#LAYER_TYPE_NONE disabled},
1165      * {@link View#LAYER_TYPE_SOFTWARE software} and {@link View#LAYER_TYPE_HARDWARE hardware}
1166      * for more information on when and how to use layers.</p>
1167      *
1168      * @param view View to set the layer type for
1169      * @param layerType The type of layer to use with this view, must be one of
1170      *        {@link View#LAYER_TYPE_NONE}, {@link View#LAYER_TYPE_SOFTWARE} or
1171      *        {@link View#LAYER_TYPE_HARDWARE}
1172      * @param paint The paint used to compose the layer. This argument is optional
1173      *        and can be null. It is ignored when the layer type is
1174      *        {@link View#LAYER_TYPE_NONE}
1175      *
1176      * @deprecated Use {@link View#setLayerType(int, Paint)} directly.
1177      */
1178     @Deprecated
setLayerType(View view, @LayerType int layerType, Paint paint)1179     public static void setLayerType(View view, @LayerType int layerType, Paint paint) {
1180         view.setLayerType(layerType, paint);
1181     }
1182 
1183     /**
1184      * Indicates what type of layer is currently associated with this view. By default
1185      * a view does not have a layer, and the layer type is {@link View#LAYER_TYPE_NONE}.
1186      * Refer to the documentation of
1187      * {@link #setLayerType(android.view.View, int, android.graphics.Paint)}
1188      * for more information on the different types of layers.
1189      *
1190      * @param view The view to fetch the layer type from
1191      * @return {@link View#LAYER_TYPE_NONE}, {@link View#LAYER_TYPE_SOFTWARE} or
1192      *         {@link View#LAYER_TYPE_HARDWARE}
1193      *
1194      * @see #setLayerType(android.view.View, int, android.graphics.Paint)
1195      * @see View#LAYER_TYPE_NONE
1196      * @see View#LAYER_TYPE_SOFTWARE
1197      * @see View#LAYER_TYPE_HARDWARE
1198      *
1199      * @deprecated Use {@link View#getLayerType()} directly.
1200      */
1201     @Deprecated
1202     @LayerType
getLayerType(View view)1203     public static int getLayerType(View view) {
1204         //noinspection ResourceType
1205         return view.getLayerType();
1206     }
1207 
1208     /**
1209      * Gets the id of a view for which a given view serves as a label for
1210      * accessibility purposes.
1211      *
1212      * @param view The view on which to invoke the corresponding method.
1213      * @return The labeled view id.
1214      */
getLabelFor(@onNull View view)1215     public static int getLabelFor(@NonNull View view) {
1216         if (Build.VERSION.SDK_INT >= 17) {
1217             return view.getLabelFor();
1218         }
1219         return 0;
1220     }
1221 
1222     /**
1223      * Sets the id of a view for which a given view serves as a label for
1224      * accessibility purposes.
1225      *
1226      * @param view The view on which to invoke the corresponding method.
1227      * @param labeledId The labeled view id.
1228      */
setLabelFor(@onNull View view, @IdRes int labeledId)1229     public static void setLabelFor(@NonNull View view, @IdRes int labeledId) {
1230         if (Build.VERSION.SDK_INT >= 17) {
1231             view.setLabelFor(labeledId);
1232         }
1233     }
1234 
1235     /**
1236      * Updates the {@link Paint} object used with the current layer (used only if the current
1237      * layer type is not set to {@link View#LAYER_TYPE_NONE}). Changed properties of the Paint
1238      * provided to {@link #setLayerType(android.view.View, int, android.graphics.Paint)}
1239      * will be used the next time the View is redrawn, but
1240      * {@link #setLayerPaint(android.view.View, android.graphics.Paint)}
1241      * must be called to ensure that the view gets redrawn immediately.
1242      *
1243      * <p>A layer is associated with an optional {@link android.graphics.Paint}
1244      * instance that controls how the layer is composed on screen. The following
1245      * properties of the paint are taken into account when composing the layer:</p>
1246      * <ul>
1247      * <li>{@link android.graphics.Paint#getAlpha() Translucency (alpha)}</li>
1248      * <li>{@link android.graphics.Paint#getXfermode() Blending mode}</li>
1249      * <li>{@link android.graphics.Paint#getColorFilter() Color filter}</li>
1250      * </ul>
1251      *
1252      * <p>If this view has an alpha value set to < 1.0 by calling
1253      * View#setAlpha(float), the alpha value of the layer's paint is replaced by
1254      * this view's alpha value. Calling View#setAlpha(float) is therefore
1255      * equivalent to setting a hardware layer on this view and providing a paint with
1256      * the desired alpha value.</p>
1257      *
1258      * @param view View to set a layer paint for
1259      * @param paint The paint used to compose the layer. This argument is optional
1260      *        and can be null. It is ignored when the layer type is
1261      *        {@link View#LAYER_TYPE_NONE}
1262      *
1263      * @see #setLayerType(View, int, android.graphics.Paint)
1264      */
setLayerPaint(@onNull View view, Paint paint)1265     public static void setLayerPaint(@NonNull View view, Paint paint) {
1266         if (Build.VERSION.SDK_INT >= 17) {
1267             view.setLayerPaint(paint);
1268         } else {
1269             // Make sure the paint is correct; this will be cheap if it's the same
1270             // instance as was used to call setLayerType earlier.
1271             view.setLayerType(view.getLayerType(), paint);
1272             // This is expensive, but the only way to accomplish this before JB-MR1.
1273             view.invalidate();
1274         }
1275     }
1276 
1277     /**
1278      * Returns the resolved layout direction for this view.
1279      *
1280      * @param view View to get layout direction for
1281      * @return {@link #LAYOUT_DIRECTION_RTL} if the layout direction is RTL or returns
1282      * {@link #LAYOUT_DIRECTION_LTR} if the layout direction is not RTL.
1283      *
1284      * For compatibility, this will return {@link #LAYOUT_DIRECTION_LTR} if API version
1285      * is lower than Jellybean MR1 (API 17)
1286      */
1287     @ResolvedLayoutDirectionMode
getLayoutDirection(@onNull View view)1288     public static int getLayoutDirection(@NonNull View view) {
1289         if (Build.VERSION.SDK_INT >= 17) {
1290             return view.getLayoutDirection();
1291         }
1292         return LAYOUT_DIRECTION_LTR;
1293     }
1294 
1295     /**
1296      * Set the layout direction for this view. This will propagate a reset of layout direction
1297      * resolution to the view's children and resolve layout direction for this view.
1298      *
1299      * @param view View to set layout direction for
1300      * @param layoutDirection the layout direction to set. Should be one of:
1301      *
1302      * {@link #LAYOUT_DIRECTION_LTR},
1303      * {@link #LAYOUT_DIRECTION_RTL},
1304      * {@link #LAYOUT_DIRECTION_INHERIT},
1305      * {@link #LAYOUT_DIRECTION_LOCALE}.
1306      *
1307      * Resolution will be done if the value is set to LAYOUT_DIRECTION_INHERIT. The resolution
1308      * proceeds up the parent chain of the view to get the value. If there is no parent, then it
1309      * will return the default {@link #LAYOUT_DIRECTION_LTR}.
1310      */
setLayoutDirection(@onNull View view, @LayoutDirectionMode int layoutDirection)1311     public static void setLayoutDirection(@NonNull View view,
1312             @LayoutDirectionMode int layoutDirection) {
1313         if (Build.VERSION.SDK_INT >= 17) {
1314             view.setLayoutDirection(layoutDirection);
1315         }
1316     }
1317 
1318     /**
1319      * Gets the parent for accessibility purposes. Note that the parent for
1320      * accessibility is not necessary the immediate parent. It is the first
1321      * predecessor that is important for accessibility.
1322      *
1323      * @param view View to retrieve parent for
1324      * @return The parent for use in accessibility inspection
1325      */
getParentForAccessibility(@onNull View view)1326     public static ViewParent getParentForAccessibility(@NonNull View view) {
1327         if (Build.VERSION.SDK_INT >= 16) {
1328             return view.getParentForAccessibility();
1329         }
1330         return view.getParent();
1331     }
1332 
1333     /**
1334      * Finds the first descendant view with the given ID, the view itself if the ID matches
1335      * {@link View#getId()}, or throws an IllegalArgumentException if the ID is invalid or there
1336      * is no matching view in the hierarchy.
1337      * <p>
1338      * <strong>Note:</strong> In most cases -- depending on compiler support --
1339      * the resulting view is automatically cast to the target class type. If
1340      * the target class type is unconstrained, an explicit cast may be
1341      * necessary.
1342      *
1343      * @param id the ID to search for
1344      * @return a view with given ID
1345      * @see View#findViewById(int)
1346      */
1347     @SuppressWarnings("TypeParameterUnusedInFormals")
1348     @NonNull
requireViewById(@onNull View view, @IdRes int id)1349     public static <T extends View> T requireViewById(@NonNull View view, @IdRes int id) {
1350         // TODO: use and link to View#requireViewById() directly, once available
1351         T targetView = view.findViewById(id);
1352         if (targetView == null) {
1353             throw new IllegalArgumentException("ID does not reference a View inside this View");
1354         }
1355         return targetView;
1356     }
1357 
1358     /**
1359      * Indicates whether this View is opaque. An opaque View guarantees that it will
1360      * draw all the pixels overlapping its bounds using a fully opaque color.
1361      *
1362      * @return True if this View is guaranteed to be fully opaque, false otherwise.
1363      * @deprecated Use {@link View#isOpaque()} directly. This method will be
1364      * removed in a future release.
1365      */
1366     @Deprecated
isOpaque(View view)1367     public static boolean isOpaque(View view) {
1368         return view.isOpaque();
1369     }
1370 
1371     /**
1372      * Utility to reconcile a desired size and state, with constraints imposed
1373      * by a MeasureSpec.  Will take the desired size, unless a different size
1374      * is imposed by the constraints.  The returned value is a compound integer,
1375      * with the resolved size in the {@link #MEASURED_SIZE_MASK} bits and
1376      * optionally the bit {@link #MEASURED_STATE_TOO_SMALL} set if the resulting
1377      * size is smaller than the size the view wants to be.
1378      *
1379      * @param size How big the view wants to be
1380      * @param measureSpec Constraints imposed by the parent
1381      * @return Size information bit mask as defined by
1382      * {@link #MEASURED_SIZE_MASK} and {@link #MEASURED_STATE_TOO_SMALL}.
1383      *
1384      * @deprecated Use {@link View#resolveSizeAndState(int, int, int)} directly.
1385      */
1386     @Deprecated
resolveSizeAndState(int size, int measureSpec, int childMeasuredState)1387     public static int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) {
1388         return View.resolveSizeAndState(size, measureSpec, childMeasuredState);
1389     }
1390 
1391     /**
1392      * Return the full width measurement information for this view as computed
1393      * by the most recent call to {@link android.view.View#measure(int, int)}.
1394      * This result is a bit mask as defined by {@link #MEASURED_SIZE_MASK} and
1395      * {@link #MEASURED_STATE_TOO_SMALL}.
1396      * This should be used during measurement and layout calculations only. Use
1397      * {@link android.view.View#getWidth()} to see how wide a view is after layout.
1398      *
1399      * @return The measured width of this view as a bit mask.
1400      *
1401      * @deprecated Use {@link View#getMeasuredWidth()} directly.
1402      */
1403     @Deprecated
getMeasuredWidthAndState(View view)1404     public static int getMeasuredWidthAndState(View view) {
1405         return view.getMeasuredWidthAndState();
1406     }
1407 
1408     /**
1409      * Return the full height measurement information for this view as computed
1410      * by the most recent call to {@link android.view.View#measure(int, int)}.
1411      * This result is a bit mask as defined by {@link #MEASURED_SIZE_MASK} and
1412      * {@link #MEASURED_STATE_TOO_SMALL}.
1413      * This should be used during measurement and layout calculations only. Use
1414      * {@link android.view.View#getHeight()} to see how wide a view is after layout.
1415      *
1416      * @return The measured width of this view as a bit mask.
1417      *
1418      * @deprecated Use {@link View#getMeasuredHeightAndState()} directly.
1419      */
1420     @Deprecated
getMeasuredHeightAndState(View view)1421     public static int getMeasuredHeightAndState(View view) {
1422         return view.getMeasuredHeightAndState();
1423     }
1424 
1425     /**
1426      * Return only the state bits of {@link #getMeasuredWidthAndState}
1427      * and {@link #getMeasuredHeightAndState}, combined into one integer.
1428      * The width component is in the regular bits {@link #MEASURED_STATE_MASK}
1429      * and the height component is at the shifted bits
1430      * {@link #MEASURED_HEIGHT_STATE_SHIFT}>>{@link #MEASURED_STATE_MASK}.
1431      *
1432      * @deprecated Use {@link View#getMeasuredState()} directly.
1433      */
1434     @Deprecated
getMeasuredState(View view)1435     public static int getMeasuredState(View view) {
1436         return view.getMeasuredState();
1437     }
1438 
1439     /**
1440      * Merge two states as returned by {@link #getMeasuredState(View)}.
1441      * @param curState The current state as returned from a view or the result
1442      * of combining multiple views.
1443      * @param newState The new view state to combine.
1444      * @return Returns a new integer reflecting the combination of the two
1445      * states.
1446      *
1447      * @deprecated Use {@link View#combineMeasuredStates(int, int)} directly.
1448      */
1449     @Deprecated
combineMeasuredStates(int curState, int newState)1450     public static int combineMeasuredStates(int curState, int newState) {
1451         return View.combineMeasuredStates(curState, newState);
1452     }
1453 
1454     /**
1455      * Gets the live region mode for the specified View.
1456      *
1457      * @param view The view from which to obtain the live region mode
1458      * @return The live region mode for the view.
1459      *
1460      * @see ViewCompat#setAccessibilityLiveRegion(View, int)
1461      */
1462     @AccessibilityLiveRegion
getAccessibilityLiveRegion(@onNull View view)1463     public static int getAccessibilityLiveRegion(@NonNull View view) {
1464         if (Build.VERSION.SDK_INT >= 19) {
1465             return view.getAccessibilityLiveRegion();
1466         }
1467         return ACCESSIBILITY_LIVE_REGION_NONE;
1468     }
1469 
1470     /**
1471      * Sets the live region mode for the specified view. This indicates to
1472      * accessibility services whether they should automatically notify the user
1473      * about changes to the view's content description or text, or to the
1474      * content descriptions or text of the view's children (where applicable).
1475      * <p>
1476      * For example, in a login screen with a TextView that displays an "incorrect
1477      * password" notification, that view should be marked as a live region with
1478      * mode {@link #ACCESSIBILITY_LIVE_REGION_POLITE}.
1479      * <p>
1480      * To disable change notifications for this view, use
1481      * {@link #ACCESSIBILITY_LIVE_REGION_NONE}. This is the default live region
1482      * mode for most views.
1483      * <p>
1484      * To indicate that the user should be notified of changes, use
1485      * {@link #ACCESSIBILITY_LIVE_REGION_POLITE}.
1486      * <p>
1487      * If the view's changes should interrupt ongoing speech and notify the user
1488      * immediately, use {@link #ACCESSIBILITY_LIVE_REGION_ASSERTIVE}.
1489      *
1490      * @param view The view on which to set the live region mode
1491      * @param mode The live region mode for this view, one of:
1492      *        <ul>
1493      *        <li>{@link #ACCESSIBILITY_LIVE_REGION_NONE}
1494      *        <li>{@link #ACCESSIBILITY_LIVE_REGION_POLITE}
1495      *        <li>{@link #ACCESSIBILITY_LIVE_REGION_ASSERTIVE}
1496      *        </ul>
1497      */
setAccessibilityLiveRegion(@onNull View view, @AccessibilityLiveRegion int mode)1498     public static void setAccessibilityLiveRegion(@NonNull View view,
1499             @AccessibilityLiveRegion int mode) {
1500         if (Build.VERSION.SDK_INT >= 19) {
1501             view.setAccessibilityLiveRegion(mode);
1502         }
1503     }
1504 
1505     /**
1506      * Returns the start padding of the specified view depending on its resolved layout direction.
1507      * If there are inset and enabled scrollbars, this value may include the space
1508      * required to display the scrollbars as well.
1509      *
1510      * @param view The view to get padding for
1511      * @return the start padding in pixels
1512      */
1513     @Px
getPaddingStart(@onNull View view)1514     public static int getPaddingStart(@NonNull View view) {
1515         if (Build.VERSION.SDK_INT >= 17) {
1516             return view.getPaddingStart();
1517         }
1518         return view.getPaddingLeft();
1519     }
1520 
1521     /**
1522      * Returns the end padding of the specified view depending on its resolved layout direction.
1523      * If there are inset and enabled scrollbars, this value may include the space
1524      * required to display the scrollbars as well.
1525      *
1526      * @param view The view to get padding for
1527      * @return the end padding in pixels
1528      */
1529     @Px
getPaddingEnd(@onNull View view)1530     public static int getPaddingEnd(@NonNull View view) {
1531         if (Build.VERSION.SDK_INT >= 17) {
1532             return view.getPaddingEnd();
1533         }
1534         return view.getPaddingRight();
1535     }
1536 
1537     /**
1538      * Sets the relative padding. The view may add on the space required to display
1539      * the scrollbars, depending on the style and visibility of the scrollbars.
1540      * So the values returned from {@link #getPaddingStart}, {@link View#getPaddingTop},
1541      * {@link #getPaddingEnd} and {@link View#getPaddingBottom} may be different
1542      * from the values set in this call.
1543      *
1544      * @param view The view on which to set relative padding
1545      * @param start the start padding in pixels
1546      * @param top the top padding in pixels
1547      * @param end the end padding in pixels
1548      * @param bottom the bottom padding in pixels
1549      */
setPaddingRelative(@onNull View view, @Px int start, @Px int top, @Px int end, @Px int bottom)1550     public static void setPaddingRelative(@NonNull View view, @Px int start, @Px int top,
1551             @Px int end, @Px int bottom) {
1552         if (Build.VERSION.SDK_INT >= 17) {
1553             view.setPaddingRelative(start, top, end, bottom);
1554         } else {
1555             view.setPadding(start, top, end, bottom);
1556         }
1557     }
1558 
bindTempDetach()1559     private static void bindTempDetach() {
1560         try {
1561             sDispatchStartTemporaryDetach = View.class.getDeclaredMethod(
1562                     "dispatchStartTemporaryDetach");
1563             sDispatchFinishTemporaryDetach = View.class.getDeclaredMethod(
1564                     "dispatchFinishTemporaryDetach");
1565         } catch (NoSuchMethodException e) {
1566             Log.e(TAG, "Couldn't find method", e);
1567         }
1568         sTempDetachBound = true;
1569     }
1570 
1571     /**
1572      * Notify a view that it is being temporarily detached.
1573      */
dispatchStartTemporaryDetach(@onNull View view)1574     public static void dispatchStartTemporaryDetach(@NonNull View view) {
1575         if (Build.VERSION.SDK_INT >= 24) {
1576             view.dispatchStartTemporaryDetach();
1577         } else {
1578             if (!sTempDetachBound) {
1579                 bindTempDetach();
1580             }
1581             if (sDispatchStartTemporaryDetach != null) {
1582                 try {
1583                     sDispatchStartTemporaryDetach.invoke(view);
1584                 } catch (Exception e) {
1585                     Log.d(TAG, "Error calling dispatchStartTemporaryDetach", e);
1586                 }
1587             } else {
1588                 // Try this instead
1589                 view.onStartTemporaryDetach();
1590             }
1591         }
1592     }
1593 
1594     /**
1595      * Notify a view that its temporary detach has ended; the view is now reattached.
1596      */
dispatchFinishTemporaryDetach(@onNull View view)1597     public static void dispatchFinishTemporaryDetach(@NonNull View view) {
1598         if (Build.VERSION.SDK_INT >= 24) {
1599             view.dispatchFinishTemporaryDetach();
1600         } else {
1601             if (!sTempDetachBound) {
1602                 bindTempDetach();
1603             }
1604             if (sDispatchFinishTemporaryDetach != null) {
1605                 try {
1606                     sDispatchFinishTemporaryDetach.invoke(view);
1607                 } catch (Exception e) {
1608                     Log.d(TAG, "Error calling dispatchFinishTemporaryDetach", e);
1609                 }
1610             } else {
1611                 // Try this instead
1612                 view.onFinishTemporaryDetach();
1613             }
1614         }
1615     }
1616 
1617     /**
1618      * The horizontal location of this view relative to its {@link View#getLeft() left} position.
1619      * This position is post-layout, in addition to wherever the object's
1620      * layout placed it.
1621      *
1622      * @return The horizontal position of this view relative to its left position, in pixels.
1623      *
1624      * @deprecated Use {@link View#getTranslationX()} directly.
1625      */
1626     @Deprecated
getTranslationX(View view)1627     public static float getTranslationX(View view) {
1628         return view.getTranslationX();
1629     }
1630 
1631     /**
1632      * The vertical location of this view relative to its {@link View#getTop() top} position.
1633      * This position is post-layout, in addition to wherever the object's
1634      * layout placed it.
1635      *
1636      * @return The vertical position of this view relative to its top position, in pixels.
1637      *
1638      * @deprecated Use {@link View#getTranslationY()} directly.
1639      */
1640     @Deprecated
getTranslationY(View view)1641     public static float getTranslationY(View view) {
1642         return view.getTranslationY();
1643     }
1644 
1645     /**
1646      * The transform matrix of this view, which is calculated based on the current
1647      * rotation, scale, and pivot properties.
1648      * <p>
1649      *
1650      * @param view The view whose Matrix will be returned
1651      * @return The current transform matrix for the view
1652      *
1653      * @see #getRotation(View)
1654      * @see #getScaleX(View)
1655      * @see #getScaleY(View)
1656      * @see #getPivotX(View)
1657      * @see #getPivotY(View)
1658      *
1659      * @deprecated Use {@link View#getMatrix()} directly.
1660      */
1661     @Deprecated
1662     @Nullable
getMatrix(View view)1663     public static Matrix getMatrix(View view) {
1664         return view.getMatrix();
1665     }
1666 
1667     /**
1668      * Returns the minimum width of the view.
1669      *
1670      * <p>Prior to API 16, this method may return 0 on some platforms.</p>
1671      *
1672      * @return the minimum width the view will try to be.
1673      */
getMinimumWidth(@onNull View view)1674     public static int getMinimumWidth(@NonNull View view) {
1675         if (Build.VERSION.SDK_INT >= 16) {
1676             return view.getMinimumWidth();
1677         }
1678 
1679         if (!sMinWidthFieldFetched) {
1680             try {
1681                 sMinWidthField = View.class.getDeclaredField("mMinWidth");
1682                 sMinWidthField.setAccessible(true);
1683             } catch (NoSuchFieldException e) {
1684                 // Couldn't find the field. Abort!
1685             }
1686             sMinWidthFieldFetched = true;
1687         }
1688 
1689         if (sMinWidthField != null) {
1690             try {
1691                 return (int) sMinWidthField.get(view);
1692             } catch (Exception e) {
1693                 // Field get failed. Oh well...
1694             }
1695         }
1696 
1697         // We failed, return 0
1698         return 0;
1699     }
1700 
1701     /**
1702      * Returns the minimum height of the view.
1703      *
1704      * <p>Prior to API 16, this method may return 0 on some platforms.</p>
1705      *
1706      * @return the minimum height the view will try to be.
1707      */
getMinimumHeight(@onNull View view)1708     public static int getMinimumHeight(@NonNull View view) {
1709         if (Build.VERSION.SDK_INT >= 16) {
1710             return view.getMinimumHeight();
1711         }
1712 
1713         if (!sMinHeightFieldFetched) {
1714             try {
1715                 sMinHeightField = View.class.getDeclaredField("mMinHeight");
1716                 sMinHeightField.setAccessible(true);
1717             } catch (NoSuchFieldException e) {
1718                 // Couldn't find the field. Abort!
1719             }
1720             sMinHeightFieldFetched = true;
1721         }
1722 
1723         if (sMinHeightField != null) {
1724             try {
1725                 return (int) sMinHeightField.get(view);
1726             } catch (Exception e) {
1727                 // Field get failed. Oh well...
1728             }
1729         }
1730 
1731         // We failed, return 0
1732         return 0;
1733     }
1734 
1735     /**
1736      * This method returns a ViewPropertyAnimator object, which can be used to animate
1737      * specific properties on this View.
1738      *
1739      * @return ViewPropertyAnimator The ViewPropertyAnimator associated with this View.
1740      */
1741     @NonNull
animate(@onNull View view)1742     public static ViewPropertyAnimatorCompat animate(@NonNull View view) {
1743         if (sViewPropertyAnimatorMap == null) {
1744             sViewPropertyAnimatorMap = new WeakHashMap<>();
1745         }
1746         ViewPropertyAnimatorCompat vpa = sViewPropertyAnimatorMap.get(view);
1747         if (vpa == null) {
1748             vpa = new ViewPropertyAnimatorCompat(view);
1749             sViewPropertyAnimatorMap.put(view, vpa);
1750         }
1751         return vpa;
1752     }
1753 
1754     /**
1755      * Sets the horizontal location of this view relative to its left position.
1756      * This effectively positions the object post-layout, in addition to wherever the object's
1757      * layout placed it.
1758      *
1759      * @param value The horizontal position of this view relative to its left position,
1760      * in pixels.
1761      *
1762      * @deprecated Use {@link View#setTranslationX(float)} directly.
1763      */
1764     @Deprecated
setTranslationX(View view, float value)1765     public static void setTranslationX(View view, float value) {
1766         view.setTranslationX(value);
1767     }
1768 
1769     /**
1770      * Sets the vertical location of this view relative to its top position.
1771      * This effectively positions the object post-layout, in addition to wherever the object's
1772      * layout placed it.
1773      *
1774      * @param value The vertical position of this view relative to its top position,
1775      * in pixels.
1776      *
1777      * @attr name android:translationY
1778      *
1779      * @deprecated Use {@link View#setTranslationY(float)} directly.
1780      */
1781     @Deprecated
setTranslationY(View view, float value)1782     public static void setTranslationY(View view, float value) {
1783         view.setTranslationY(value);
1784     }
1785 
1786     /**
1787      * <p>Sets the opacity of the view. This is a value from 0 to 1, where 0 means the view is
1788      * completely transparent and 1 means the view is completely opaque.</p>
1789      *
1790      * <p> Note that setting alpha to a translucent value (0 < alpha < 1) can have significant
1791      * performance implications, especially for large views. It is best to use the alpha property
1792      * sparingly and transiently, as in the case of fading animations.</p>
1793      *
1794      * @param value The opacity of the view.
1795      *
1796      * @deprecated Use {@link View#setAlpha(float)} directly.
1797      */
1798     @Deprecated
setAlpha(View view, @FloatRange(from=0.0, to=1.0) float value)1799     public static void setAlpha(View view, @FloatRange(from=0.0, to=1.0) float value) {
1800         view.setAlpha(value);
1801     }
1802 
1803     /**
1804      * Sets the visual x position of this view, in pixels. This is equivalent to setting the
1805      * {@link #setTranslationX(View, float) translationX} property to be the difference between
1806      * the x value passed in and the current left property of the view as determined
1807      * by the layout bounds.
1808      *
1809      * @param value The visual x position of this view, in pixels.
1810      *
1811      * @deprecated Use {@link View#setX(float)} directly.
1812      */
1813     @Deprecated
setX(View view, float value)1814     public static void setX(View view, float value) {
1815         view.setX(value);
1816     }
1817 
1818     /**
1819      * Sets the visual y position of this view, in pixels. This is equivalent to setting the
1820      * {@link #setTranslationY(View, float) translationY} property to be the difference between
1821      * the y value passed in and the current top property of the view as determined by the
1822      * layout bounds.
1823      *
1824      * @param value The visual y position of this view, in pixels.
1825      *
1826      * @deprecated Use {@link View#setY(float)} directly.
1827      */
1828     @Deprecated
setY(View view, float value)1829     public static void setY(View view, float value) {
1830         view.setY(value);
1831     }
1832 
1833     /**
1834      * Sets the degrees that the view is rotated around the pivot point. Increasing values
1835      * result in clockwise rotation.
1836      *
1837      * @param value The degrees of rotation.
1838      *
1839      * @deprecated Use {@link View#setRotation(float)} directly.
1840      */
1841     @Deprecated
setRotation(View view, float value)1842     public static void setRotation(View view, float value) {
1843         view.setRotation(value);
1844     }
1845 
1846     /**
1847      * Sets the degrees that the view is rotated around the horizontal axis through the pivot point.
1848      * Increasing values result in clockwise rotation from the viewpoint of looking down the
1849      * x axis.
1850      *
1851      * @param value The degrees of X rotation.
1852      *
1853      * @deprecated Use {@link View#setRotationX(float)} directly.
1854      */
1855     @Deprecated
setRotationX(View view, float value)1856     public static void setRotationX(View view, float value) {
1857         view.setRotationX(value);
1858     }
1859 
1860     /**
1861      * Sets the degrees that the view is rotated around the vertical axis through the pivot point.
1862      * Increasing values result in counter-clockwise rotation from the viewpoint of looking
1863      * down the y axis.
1864      *
1865      * @param value The degrees of Y rotation.
1866      *
1867      * @deprecated Use {@link View#setRotationY(float)} directly.
1868      */
1869     @Deprecated
setRotationY(View view, float value)1870     public static void setRotationY(View view, float value) {
1871         view.setRotationY(value);
1872     }
1873 
1874     /**
1875      * Sets the amount that the view is scaled in x around the pivot point, as a proportion of
1876      * the view's unscaled width. A value of 1 means that no scaling is applied.
1877      *
1878      * @param value The scaling factor.
1879      *
1880      * @deprecated Use {@link View#setScaleX(float)} directly.
1881      */
1882     @Deprecated
setScaleX(View view, float value)1883     public static void setScaleX(View view, float value) {
1884         view.setScaleX(value);
1885     }
1886 
1887     /**
1888      * Sets the amount that the view is scaled in Y around the pivot point, as a proportion of
1889      * the view's unscaled width. A value of 1 means that no scaling is applied.
1890      *
1891      * @param value The scaling factor.
1892      *
1893      * @deprecated Use {@link View#setScaleY(float)} directly.
1894      */
1895     @Deprecated
setScaleY(View view, float value)1896     public static void setScaleY(View view, float value) {
1897         view.setScaleY(value);
1898     }
1899 
1900     /**
1901      * The x location of the point around which the view is
1902      * {@link #setRotation(View, float) rotated} and {@link #setScaleX(View, float) scaled}.
1903      *
1904      * @deprecated Use {@link View#getPivotX()} directly.
1905      */
1906     @Deprecated
getPivotX(View view)1907     public static float getPivotX(View view) {
1908         return view.getPivotX();
1909     }
1910 
1911     /**
1912      * Sets the x location of the point around which the view is
1913      * {@link #setRotation(View, float) rotated} and {@link #setScaleX(View, float) scaled}.
1914      * By default, the pivot point is centered on the object.
1915      * Setting this property disables this behavior and causes the view to use only the
1916      * explicitly set pivotX and pivotY values.
1917      *
1918      * @param value The x location of the pivot point.
1919      *
1920      * @deprecated Use {@link View#setPivotX(float)} directly.
1921      */
1922     @Deprecated
setPivotX(View view, float value)1923     public static void setPivotX(View view, float value) {
1924         view.setPivotX(value);
1925     }
1926 
1927     /**
1928      * The y location of the point around which the view is {@link #setRotation(View,
1929      * float) rotated} and {@link #setScaleY(View, float) scaled}.
1930      *
1931      * @return The y location of the pivot point.
1932      *
1933      * @deprecated Use {@link View#getPivotY()} directly.
1934      */
1935     @Deprecated
getPivotY(View view)1936     public static float getPivotY(View view) {
1937         return view.getPivotY();
1938     }
1939 
1940     /**
1941      * Sets the y location of the point around which the view is
1942      * {@link #setRotation(View, float) rotated} and {@link #setScaleY(View, float) scaled}.
1943      * By default, the pivot point is centered on the object.
1944      * Setting this property disables this behavior and causes the view to use only the
1945      * explicitly set pivotX and pivotY values.
1946      *
1947      * @param value The y location of the pivot point.
1948      *
1949      * @deprecated Use {@link View#setPivotX(float)} directly.
1950      */
1951     @Deprecated
setPivotY(View view, float value)1952     public static void setPivotY(View view, float value) {
1953         view.setPivotY(value);
1954     }
1955 
1956     /**
1957      * @deprecated Use {@link View#getRotation()} directly.
1958      */
1959     @Deprecated
getRotation(View view)1960     public static float getRotation(View view) {
1961         return view.getRotation();
1962     }
1963 
1964     /**
1965      * @deprecated Use {@link View#getRotationX()} directly.
1966      */
1967     @Deprecated
getRotationX(View view)1968     public static float getRotationX(View view) {
1969         return view.getRotationX();
1970     }
1971 
1972     /**
1973      * @deprecated Use {@link View#getRotationY()} directly.
1974      */
1975     @Deprecated
getRotationY(View view)1976     public static float getRotationY(View view) {
1977         return view.getRotationY();
1978     }
1979 
1980     /**
1981      * @deprecated Use {@link View#getScaleX()} directly.
1982      */
1983     @Deprecated
getScaleX(View view)1984     public static float getScaleX(View view) {
1985         return view.getScaleX();
1986     }
1987 
1988     /**
1989      * @deprecated Use {@link View#getScaleY()} directly.
1990      */
1991     @Deprecated
getScaleY(View view)1992     public static float getScaleY(View view) {
1993         return view.getScaleY();
1994     }
1995 
1996     /**
1997      * @deprecated Use {@link View#getX()} directly.
1998      */
1999     @Deprecated
getX(View view)2000     public static float getX(View view) {
2001         return view.getX();
2002     }
2003 
2004     /**
2005      * @deprecated Use {@link View#getY()} directly.
2006      */
2007     @Deprecated
getY(View view)2008     public static float getY(View view) {
2009         return view.getY();
2010     }
2011 
2012     /**
2013      * Sets the base elevation of this view, in pixels.
2014      */
setElevation(@onNull View view, float elevation)2015     public static void setElevation(@NonNull View view, float elevation) {
2016         if (Build.VERSION.SDK_INT >= 21) {
2017             view.setElevation(elevation);
2018         }
2019     }
2020 
2021     /**
2022      * The base elevation of this view relative to its parent, in pixels.
2023      *
2024      * @return The base depth position of the view, in pixels.
2025      */
getElevation(@onNull View view)2026     public static float getElevation(@NonNull View view) {
2027         if (Build.VERSION.SDK_INT >= 21) {
2028             return view.getElevation();
2029         }
2030         return 0f;
2031     }
2032 
2033     /**
2034      * Sets the depth location of this view relative to its {@link #getElevation(View) elevation}.
2035      */
setTranslationZ(@onNull View view, float translationZ)2036     public static void setTranslationZ(@NonNull View view, float translationZ) {
2037         if (Build.VERSION.SDK_INT >= 21) {
2038             view.setTranslationZ(translationZ);
2039         }
2040     }
2041 
2042     /**
2043      * The depth location of this view relative to its {@link #getElevation(View) elevation}.
2044      *
2045      * @return The depth of this view relative to its elevation.
2046      */
getTranslationZ(@onNull View view)2047     public static float getTranslationZ(@NonNull View view) {
2048         if (Build.VERSION.SDK_INT >= 21) {
2049             return view.getTranslationZ();
2050         }
2051         return 0f;
2052     }
2053 
2054     /**
2055      * Sets the name of the View to be used to identify Views in Transitions.
2056      * Names should be unique in the View hierarchy.
2057      *
2058      * @param view The View against which to invoke the method.
2059      * @param transitionName The name of the View to uniquely identify it for Transitions.
2060      */
setTransitionName(@onNull View view, String transitionName)2061     public static void setTransitionName(@NonNull View view, String transitionName) {
2062         if (Build.VERSION.SDK_INT >= 21) {
2063             view.setTransitionName(transitionName);
2064         } else {
2065             if (sTransitionNameMap == null) {
2066                 sTransitionNameMap = new WeakHashMap<>();
2067             }
2068             sTransitionNameMap.put(view, transitionName);
2069         }
2070     }
2071 
2072     /**
2073      * Returns the name of the View to be used to identify Views in Transitions.
2074      * Names should be unique in the View hierarchy.
2075      *
2076      * <p>This returns null if the View has not been given a name.</p>
2077      *
2078      * @param view The View against which to invoke the method.
2079      * @return The name used of the View to be used to identify Views in Transitions or null
2080      * if no name has been given.
2081      */
2082     @Nullable
getTransitionName(@onNull View view)2083     public static String getTransitionName(@NonNull View view) {
2084         if (Build.VERSION.SDK_INT >= 21) {
2085             return view.getTransitionName();
2086         }
2087         if (sTransitionNameMap == null) {
2088             return null;
2089         }
2090         return sTransitionNameMap.get(view);
2091     }
2092 
2093     /**
2094      * Returns the current system UI visibility that is currently set for the entire window.
2095      */
getWindowSystemUiVisibility(@onNull View view)2096     public static int getWindowSystemUiVisibility(@NonNull View view) {
2097         if (Build.VERSION.SDK_INT >= 16) {
2098             return view.getWindowSystemUiVisibility();
2099         }
2100         return 0;
2101     }
2102 
2103     /**
2104      * Ask that a new dispatch of {@code View.onApplyWindowInsets(WindowInsets)} be performed. This
2105      * falls back to {@code View.requestFitSystemWindows()} where available.
2106      */
requestApplyInsets(@onNull View view)2107     public static void requestApplyInsets(@NonNull View view) {
2108         if (Build.VERSION.SDK_INT >= 20) {
2109             view.requestApplyInsets();
2110         } else if (Build.VERSION.SDK_INT >= 16) {
2111             view.requestFitSystemWindows();
2112         }
2113     }
2114 
2115     /**
2116      * Tells the ViewGroup whether to draw its children in the order defined by the method
2117      * {@code ViewGroup.getChildDrawingOrder(int, int)}.
2118      *
2119      * @param enabled true if the order of the children when drawing is determined by
2120      *        {@link ViewGroup#getChildDrawingOrder(int, int)}, false otherwise
2121      *
2122      * <p>Prior to API 7 this will have no effect.</p>
2123      *
2124      * @deprecated Use {@link ViewGroup#setChildrenDrawingOrderEnabled(boolean)} directly.
2125      */
2126     @Deprecated
setChildrenDrawingOrderEnabled(ViewGroup viewGroup, boolean enabled)2127     public static void setChildrenDrawingOrderEnabled(ViewGroup viewGroup, boolean enabled) {
2128         if (sChildrenDrawingOrderMethod == null) {
2129             try {
2130                 sChildrenDrawingOrderMethod = ViewGroup.class
2131                         .getDeclaredMethod("setChildrenDrawingOrderEnabled", boolean.class);
2132             } catch (NoSuchMethodException e) {
2133                 Log.e(TAG, "Unable to find childrenDrawingOrderEnabled", e);
2134             }
2135             sChildrenDrawingOrderMethod.setAccessible(true);
2136         }
2137         try {
2138             sChildrenDrawingOrderMethod.invoke(viewGroup, enabled);
2139         } catch (IllegalAccessException e) {
2140             Log.e(TAG, "Unable to invoke childrenDrawingOrderEnabled", e);
2141         } catch (IllegalArgumentException e) {
2142             Log.e(TAG, "Unable to invoke childrenDrawingOrderEnabled", e);
2143         } catch (InvocationTargetException e) {
2144             Log.e(TAG, "Unable to invoke childrenDrawingOrderEnabled", e);
2145         }
2146     }
2147 
2148     /**
2149      * Returns true if this view should adapt to fit system window insets. This method will always
2150      * return false before API 16 (Jellybean).
2151      */
getFitsSystemWindows(@onNull View v)2152     public static boolean getFitsSystemWindows(@NonNull View v) {
2153         if (Build.VERSION.SDK_INT >= 16) {
2154             return v.getFitsSystemWindows();
2155         }
2156         return false;
2157     }
2158 
2159     /**
2160      * Sets whether or not this view should account for system screen decorations
2161      * such as the status bar and inset its content; that is, controlling whether
2162      * the default implementation of {@link View#fitSystemWindows(Rect)} will be
2163      * executed. See that method for more details.
2164      *
2165      * @deprecated Use {@link View#setFitsSystemWindows(boolean)} directly.
2166      */
2167     @Deprecated
setFitsSystemWindows(View view, boolean fitSystemWindows)2168     public static void setFitsSystemWindows(View view, boolean fitSystemWindows) {
2169         view.setFitsSystemWindows(fitSystemWindows);
2170     }
2171 
2172     /**
2173      * On API 11 devices and above, call <code>Drawable.jumpToCurrentState()</code>
2174      * on all Drawable objects associated with this view.
2175      * <p>
2176      * On API 21 and above, also calls <code>StateListAnimator#jumpToCurrentState()</code>
2177      * if there is a StateListAnimator attached to this view.
2178      *
2179      * @deprecated Use {@link View#jumpDrawablesToCurrentState()} directly.
2180      */
2181     @Deprecated
jumpDrawablesToCurrentState(View v)2182     public static void jumpDrawablesToCurrentState(View v) {
2183         v.jumpDrawablesToCurrentState();
2184     }
2185 
2186     /**
2187      * Set an {@link OnApplyWindowInsetsListener} to take over the policy for applying
2188      * window insets to this view. This will only take effect on devices with API 21 or above.
2189      */
setOnApplyWindowInsetsListener(@onNull View v, final OnApplyWindowInsetsListener listener)2190     public static void setOnApplyWindowInsetsListener(@NonNull View v,
2191             final OnApplyWindowInsetsListener listener) {
2192         if (Build.VERSION.SDK_INT >= 21) {
2193             if (listener == null) {
2194                 v.setOnApplyWindowInsetsListener(null);
2195                 return;
2196             }
2197 
2198             v.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
2199                 @Override
2200                 @RequiresApi(21) // TODO remove https://issuetracker.google.com/issues/76458979
2201                 public WindowInsets onApplyWindowInsets(View view, WindowInsets insets) {
2202                     WindowInsetsCompat compatInsets = WindowInsetsCompat.wrap(insets);
2203                     compatInsets = listener.onApplyWindowInsets(view, compatInsets);
2204                     return (WindowInsets) WindowInsetsCompat.unwrap(compatInsets);
2205                 }
2206             });
2207         }
2208     }
2209 
2210     /**
2211      * Called when the view should apply {@link WindowInsetsCompat} according to its internal policy.
2212      *
2213      * <p>Clients may supply an {@link OnApplyWindowInsetsListener} to a view. If one is set
2214      * it will be called during dispatch instead of this method. The listener may optionally
2215      * call this method from its own implementation if it wishes to apply the view's default
2216      * insets policy in addition to its own.</p>
2217      *
2218      * @param view The View against which to invoke the method.
2219      * @param insets Insets to apply
2220      * @return The supplied insets with any applied insets consumed
2221      */
onApplyWindowInsets(@onNull View view, WindowInsetsCompat insets)2222     public static WindowInsetsCompat onApplyWindowInsets(@NonNull View view,
2223             WindowInsetsCompat insets) {
2224         if (Build.VERSION.SDK_INT >= 21) {
2225             WindowInsets unwrapped = (WindowInsets)  WindowInsetsCompat.unwrap(insets);
2226             WindowInsets result = view.onApplyWindowInsets(unwrapped);
2227             if (result != unwrapped) {
2228                 unwrapped = new WindowInsets(result);
2229             }
2230             return WindowInsetsCompat.wrap(unwrapped);
2231         }
2232         return insets;
2233     }
2234 
2235     /**
2236      * Request to apply the given window insets to this view or another view in its subtree.
2237      *
2238      * <p>This method should be called by clients wishing to apply insets corresponding to areas
2239      * obscured by window decorations or overlays. This can include the status and navigation bars,
2240      * action bars, input methods and more. New inset categories may be added in the future.
2241      * The method returns the insets provided minus any that were applied by this view or its
2242      * children.</p>
2243      *
2244      * @param insets Insets to apply
2245      * @return The provided insets minus the insets that were consumed
2246      */
dispatchApplyWindowInsets(@onNull View view, WindowInsetsCompat insets)2247     public static WindowInsetsCompat dispatchApplyWindowInsets(@NonNull View view,
2248             WindowInsetsCompat insets) {
2249         if (Build.VERSION.SDK_INT >= 21) {
2250             WindowInsets unwrapped = (WindowInsets) WindowInsetsCompat.unwrap(insets);
2251             WindowInsets result = view.dispatchApplyWindowInsets(unwrapped);
2252             if (result != unwrapped) {
2253                 unwrapped = new WindowInsets(result);
2254             }
2255             return WindowInsetsCompat.wrap(unwrapped);
2256         }
2257         return insets;
2258     }
2259 
2260     /**
2261      * Controls whether the entire hierarchy under this view will save its
2262      * state when a state saving traversal occurs from its parent.
2263      *
2264      * @param enabled Set to false to <em>disable</em> state saving, or true
2265      * (the default) to allow it.
2266      *
2267      * @deprecated Use {@link View#setSaveFromParentEnabled(boolean)} directly.
2268      */
2269     @Deprecated
setSaveFromParentEnabled(View v, boolean enabled)2270     public static void setSaveFromParentEnabled(View v, boolean enabled) {
2271         v.setSaveFromParentEnabled(enabled);
2272     }
2273 
2274     /**
2275      * Changes the activated state of this view. A view can be activated or not.
2276      * Note that activation is not the same as selection.  Selection is
2277      * a transient property, representing the view (hierarchy) the user is
2278      * currently interacting with.  Activation is a longer-term state that the
2279      * user can move views in and out of.
2280      *
2281      * @param activated true if the view must be activated, false otherwise
2282      *
2283      * @deprecated Use {@link View#setActivated(boolean)} directly.
2284      */
2285     @Deprecated
setActivated(View view, boolean activated)2286     public static void setActivated(View view, boolean activated) {
2287         view.setActivated(activated);
2288     }
2289 
2290     /**
2291      * Returns whether this View has content which overlaps.
2292      *
2293      * <p>This function, intended to be overridden by specific View types, is an optimization when
2294      * alpha is set on a view. If rendering overlaps in a view with alpha < 1, that view is drawn to
2295      * an offscreen buffer and then composited into place, which can be expensive. If the view has
2296      * no overlapping rendering, the view can draw each primitive with the appropriate alpha value
2297      * directly. An example of overlapping rendering is a TextView with a background image, such as
2298      * a Button. An example of non-overlapping rendering is a TextView with no background, or an
2299      * ImageView with only the foreground image. The default implementation returns true; subclasses
2300      * should override if they have cases which can be optimized.</p>
2301      *
2302      * @return true if the content in this view might overlap, false otherwise.
2303      */
hasOverlappingRendering(@onNull View view)2304     public static boolean hasOverlappingRendering(@NonNull View view) {
2305         if (Build.VERSION.SDK_INT >= 16) {
2306             return view.hasOverlappingRendering();
2307         }
2308         return true;
2309     }
2310 
2311     /**
2312      * Return if the padding as been set through relative values
2313      * {@code View.setPaddingRelative(int, int, int, int)} or thru
2314      *
2315      * @return true if the padding is relative or false if it is not.
2316      */
isPaddingRelative(@onNull View view)2317     public static boolean isPaddingRelative(@NonNull View view) {
2318         if (Build.VERSION.SDK_INT >= 17) {
2319             return view.isPaddingRelative();
2320         }
2321         return false;
2322     }
2323 
2324     /**
2325      * Set the background of the {@code view} to a given Drawable, or remove the background. If the
2326      * background has padding, {@code view}'s padding is set to the background's padding. However,
2327      * when a background is removed, this View's padding isn't touched. If setting the padding is
2328      * desired, please use{@code setPadding(int, int, int, int)}.
2329      */
setBackground(@onNull View view, @Nullable Drawable background)2330     public static void setBackground(@NonNull View view, @Nullable Drawable background) {
2331         if (Build.VERSION.SDK_INT >= 16) {
2332             view.setBackground(background);
2333         } else {
2334             view.setBackgroundDrawable(background);
2335         }
2336     }
2337 
2338     /**
2339      * Return the tint applied to the background drawable, if specified.
2340      * <p>
2341      * Only returns meaningful info when running on API v21 or newer, or if {@code view}
2342      * implements the {@code TintableBackgroundView} interface.
2343      */
getBackgroundTintList(@onNull View view)2344     public static ColorStateList getBackgroundTintList(@NonNull View view) {
2345         if (Build.VERSION.SDK_INT >= 21) {
2346             return view.getBackgroundTintList();
2347         }
2348         return (view instanceof TintableBackgroundView)
2349                 ? ((TintableBackgroundView) view).getSupportBackgroundTintList()
2350                 : null;
2351     }
2352 
2353     /**
2354      * Applies a tint to the background drawable.
2355      * <p>
2356      * This will always take effect when running on API v21 or newer. When running on platforms
2357      * previous to API v21, it will only take effect if {@code view} implements the
2358      * {@code TintableBackgroundView} interface.
2359      */
setBackgroundTintList(@onNull View view, ColorStateList tintList)2360     public static void setBackgroundTintList(@NonNull View view, ColorStateList tintList) {
2361         if (Build.VERSION.SDK_INT >= 21) {
2362             view.setBackgroundTintList(tintList);
2363 
2364             if (Build.VERSION.SDK_INT == 21) {
2365                 // Work around a bug in L that did not update the state of the background
2366                 // after applying the tint
2367                 Drawable background = view.getBackground();
2368                 boolean hasTint = (view.getBackgroundTintList() != null)
2369                         || (view.getBackgroundTintMode() != null);
2370                 if ((background != null) && hasTint) {
2371                     if (background.isStateful()) {
2372                         background.setState(view.getDrawableState());
2373                     }
2374                     view.setBackground(background);
2375                 }
2376             }
2377         } else if (view instanceof TintableBackgroundView) {
2378             ((TintableBackgroundView) view).setSupportBackgroundTintList(tintList);
2379         }
2380     }
2381 
2382     /**
2383      * Return the blending mode used to apply the tint to the background
2384      * drawable, if specified.
2385      * <p>
2386      * Only returns meaningful info when running on API v21 or newer, or if {@code view}
2387      * implements the {@code TintableBackgroundView} interface.
2388      */
getBackgroundTintMode(@onNull View view)2389     public static PorterDuff.Mode getBackgroundTintMode(@NonNull View view) {
2390         if (Build.VERSION.SDK_INT >= 21) {
2391             return view.getBackgroundTintMode();
2392         }
2393         return (view instanceof TintableBackgroundView)
2394                 ? ((TintableBackgroundView) view).getSupportBackgroundTintMode()
2395                 : null;
2396     }
2397 
2398     /**
2399      * Specifies the blending mode used to apply the tint specified by
2400      * {@link #setBackgroundTintList(android.view.View, android.content.res.ColorStateList)} to
2401      * the background drawable. The default mode is {@link PorterDuff.Mode#SRC_IN}.
2402      * <p>
2403      * This will always take effect when running on API v21 or newer. When running on platforms
2404      * previous to API v21, it will only take effect if {@code view} implement the
2405      * {@code TintableBackgroundView} interface.
2406      */
setBackgroundTintMode(@onNull View view, PorterDuff.Mode mode)2407     public static void setBackgroundTintMode(@NonNull View view, PorterDuff.Mode mode) {
2408         if (Build.VERSION.SDK_INT >= 21) {
2409             view.setBackgroundTintMode(mode);
2410 
2411             if (Build.VERSION.SDK_INT == 21) {
2412                 // Work around a bug in L that did not update the state of the background
2413                 // after applying the tint
2414                 Drawable background = view.getBackground();
2415                 boolean hasTint = (view.getBackgroundTintList() != null)
2416                         || (view.getBackgroundTintMode() != null);
2417                 if ((background != null) && hasTint) {
2418                     if (background.isStateful()) {
2419                         background.setState(view.getDrawableState());
2420                     }
2421                     view.setBackground(background);
2422                 }
2423             }
2424         } else if (view instanceof TintableBackgroundView) {
2425             ((TintableBackgroundView) view).setSupportBackgroundTintMode(mode);
2426         }
2427     }
2428 
2429     // TODO: getters for various view properties (rotation, etc)
2430 
2431     /**
2432      * Enable or disable nested scrolling for this view.
2433      *
2434      * <p>If this property is set to true the view will be permitted to initiate nested
2435      * scrolling operations with a compatible parent view in the current hierarchy. If this
2436      * view does not implement nested scrolling this will have no effect. Disabling nested scrolling
2437      * while a nested scroll is in progress has the effect of
2438      * {@link #stopNestedScroll(View) stopping} the nested scroll.</p>
2439      *
2440      * @param enabled true to enable nested scrolling, false to disable
2441      *
2442      * @see #isNestedScrollingEnabled(View)
2443      */
2444     @SuppressWarnings("RedundantCast") // Intentionally invoking interface method.
setNestedScrollingEnabled(@onNull View view, boolean enabled)2445     public static void setNestedScrollingEnabled(@NonNull View view, boolean enabled) {
2446         if (Build.VERSION.SDK_INT >= 21) {
2447             view.setNestedScrollingEnabled(enabled);
2448         } else {
2449             if (view instanceof NestedScrollingChild) {
2450                 ((NestedScrollingChild) view).setNestedScrollingEnabled(enabled);
2451             }
2452         }
2453     }
2454 
2455     /**
2456      * Returns true if nested scrolling is enabled for this view.
2457      *
2458      * <p>If nested scrolling is enabled and this View class implementation supports it,
2459      * this view will act as a nested scrolling child view when applicable, forwarding data
2460      * about the scroll operation in progress to a compatible and cooperating nested scrolling
2461      * parent.</p>
2462      *
2463      * @return true if nested scrolling is enabled
2464      *
2465      * @see #setNestedScrollingEnabled(View, boolean)
2466      */
2467     @SuppressWarnings("RedundantCast") // Intentionally invoking interface method.
isNestedScrollingEnabled(@onNull View view)2468     public static boolean isNestedScrollingEnabled(@NonNull View view) {
2469         if (Build.VERSION.SDK_INT >= 21) {
2470             return view.isNestedScrollingEnabled();
2471         }
2472         if (view instanceof NestedScrollingChild) {
2473             return ((NestedScrollingChild) view).isNestedScrollingEnabled();
2474         }
2475         return false;
2476     }
2477 
2478     /**
2479      * Begin a nestable scroll operation along the given axes.
2480      *
2481      * <p>This version of the method just calls {@link #startNestedScroll(View, int, int)} using
2482      * the touch input type.</p>
2483      *
2484      * @param axes Flags consisting of a combination of {@link ViewCompat#SCROLL_AXIS_HORIZONTAL}
2485      *             and/or {@link ViewCompat#SCROLL_AXIS_VERTICAL}.
2486      * @return true if a cooperative parent was found and nested scrolling has been enabled for
2487      *         the current gesture.
2488      */
2489     @SuppressWarnings("RedundantCast") // Intentionally invoking interface method.
startNestedScroll(@onNull View view, @ScrollAxis int axes)2490     public static boolean startNestedScroll(@NonNull View view, @ScrollAxis int axes) {
2491         if (Build.VERSION.SDK_INT >= 21) {
2492             return view.startNestedScroll(axes);
2493         }
2494         if (view instanceof NestedScrollingChild) {
2495             return ((NestedScrollingChild) view).startNestedScroll(axes);
2496         }
2497         return false;
2498     }
2499 
2500     /**
2501      * Stop a nested scroll in progress.
2502      *
2503      * <p>This version of the method just calls {@link #stopNestedScroll(View, int)} using the
2504      * touch input type.</p>
2505      *
2506      * @see #startNestedScroll(View, int)
2507      */
2508     @SuppressWarnings("RedundantCast") // Intentionally invoking interface method.
stopNestedScroll(@onNull View view)2509     public static void stopNestedScroll(@NonNull View view) {
2510         if (Build.VERSION.SDK_INT >= 21) {
2511             view.stopNestedScroll();
2512         } else if (view instanceof NestedScrollingChild) {
2513             ((NestedScrollingChild) view).stopNestedScroll();
2514         }
2515     }
2516 
2517     /**
2518      * Returns true if this view has a nested scrolling parent.
2519      *
2520      * <p>This version of the method just calls {@link #hasNestedScrollingParent(View, int)}
2521      * using the touch input type.</p>
2522      *
2523      * @return whether this view has a nested scrolling parent
2524      */
2525     @SuppressWarnings("RedundantCast") // Intentionally invoking interface method.
hasNestedScrollingParent(@onNull View view)2526     public static boolean hasNestedScrollingParent(@NonNull View view) {
2527         if (Build.VERSION.SDK_INT >= 21) {
2528             return view.hasNestedScrollingParent();
2529         }
2530         if (view instanceof NestedScrollingChild) {
2531             return ((NestedScrollingChild) view).hasNestedScrollingParent();
2532         }
2533         return false;
2534     }
2535 
2536     /**
2537      * Dispatch one step of a nested scroll in progress.
2538      *
2539      * <p>This version of the method just calls
2540      * {@link #dispatchNestedScroll(View, int, int, int, int, int[], int)} using the touch input
2541      * type.</p>
2542      *
2543      * @param dxConsumed Horizontal distance in pixels consumed by this view during this scroll step
2544      * @param dyConsumed Vertical distance in pixels consumed by this view during this scroll step
2545      * @param dxUnconsumed Horizontal scroll distance in pixels not consumed by this view
2546      * @param dyUnconsumed Horizontal scroll distance in pixels not consumed by this view
2547      * @param offsetInWindow Optional. If not null, on return this will contain the offset
2548      *                       in local view coordinates of this view from before this operation
2549      *                       to after it completes. View implementations may use this to adjust
2550      *                       expected input coordinate tracking.
2551      * @return true if the event was dispatched, false if it could not be dispatched.
2552      */
2553     @SuppressWarnings("RedundantCast") // Intentionally invoking interface method.
dispatchNestedScroll(@onNull View view, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, @Nullable int[] offsetInWindow)2554     public static boolean dispatchNestedScroll(@NonNull View view, int dxConsumed, int dyConsumed,
2555             int dxUnconsumed, int dyUnconsumed, @Nullable int[] offsetInWindow) {
2556         if (Build.VERSION.SDK_INT >= 21) {
2557             return view.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
2558                     offsetInWindow);
2559         }
2560         if (view instanceof NestedScrollingChild) {
2561             return ((NestedScrollingChild) view).dispatchNestedScroll(dxConsumed, dyConsumed,
2562                     dxUnconsumed, dyUnconsumed, offsetInWindow);
2563         }
2564         return false;
2565     }
2566 
2567     /**
2568      * Dispatch one step of a nested scroll in progress before this view consumes any portion of it.
2569      *
2570      * <p>This version of the method just calls
2571      * {@link #dispatchNestedPreScroll(View, int, int, int[], int[], int)} using the touch input
2572      * type.</p>
2573      *
2574      * @param dx Horizontal scroll distance in pixels
2575      * @param dy Vertical scroll distance in pixels
2576      * @param consumed Output. If not null, consumed[0] will contain the consumed component of dx
2577      *                 and consumed[1] the consumed dy.
2578      * @param offsetInWindow Optional. If not null, on return this will contain the offset
2579      *                       in local view coordinates of this view from before this operation
2580      *                       to after it completes. View implementations may use this to adjust
2581      *                       expected input coordinate tracking.
2582      * @return true if the parent consumed some or all of the scroll delta
2583      */
2584     @SuppressWarnings("RedundantCast") // Intentionally invoking interface method.
dispatchNestedPreScroll(@onNull View view, int dx, int dy, @Nullable int[] consumed, @Nullable int[] offsetInWindow)2585     public static boolean dispatchNestedPreScroll(@NonNull View view, int dx, int dy,
2586             @Nullable int[] consumed, @Nullable int[] offsetInWindow) {
2587         if (Build.VERSION.SDK_INT >= 21) {
2588             return view.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
2589         }
2590         if (view instanceof NestedScrollingChild) {
2591             return ((NestedScrollingChild) view).dispatchNestedPreScroll(dx, dy, consumed,
2592                     offsetInWindow);
2593         }
2594         return false;
2595     }
2596 
2597     /**
2598      * Begin a nestable scroll operation along the given axes.
2599      *
2600      * <p>A view starting a nested scroll promises to abide by the following contract:</p>
2601      *
2602      * <p>The view will call startNestedScroll upon initiating a scroll operation. In the case
2603      * of a touch scroll this corresponds to the initial {@link MotionEvent#ACTION_DOWN}.
2604      * In the case of touch scrolling the nested scroll will be terminated automatically in
2605      * the same manner as {@link ViewParent#requestDisallowInterceptTouchEvent(boolean)}.
2606      * In the event of programmatic scrolling the caller must explicitly call
2607      * {@link #stopNestedScroll(View)} to indicate the end of the nested scroll.</p>
2608      *
2609      * <p>If <code>startNestedScroll</code> returns true, a cooperative parent was found.
2610      * If it returns false the caller may ignore the rest of this contract until the next scroll.
2611      * Calling startNestedScroll while a nested scroll is already in progress will return true.</p>
2612      *
2613      * <p>At each incremental step of the scroll the caller should invoke
2614      * {@link #dispatchNestedPreScroll(View, int, int, int[], int[]) dispatchNestedPreScroll}
2615      * once it has calculated the requested scrolling delta. If it returns true the nested scrolling
2616      * parent at least partially consumed the scroll and the caller should adjust the amount it
2617      * scrolls by.</p>
2618      *
2619      * <p>After applying the remainder of the scroll delta the caller should invoke
2620      * {@link #dispatchNestedScroll(View, int, int, int, int, int[]) dispatchNestedScroll}, passing
2621      * both the delta consumed and the delta unconsumed. A nested scrolling parent may treat
2622      * these values differently. See
2623      * {@link NestedScrollingParent#onNestedScroll(View, int, int, int, int)}.
2624      * </p>
2625      *
2626      * @param axes Flags consisting of a combination of {@link ViewCompat#SCROLL_AXIS_HORIZONTAL}
2627      *             and/or {@link ViewCompat#SCROLL_AXIS_VERTICAL}.
2628      * @param type the type of input which cause this scroll event
2629      * @return true if a cooperative parent was found and nested scrolling has been enabled for
2630      *         the current gesture.
2631      *
2632      * @see #stopNestedScroll(View)
2633      * @see #dispatchNestedPreScroll(View, int, int, int[], int[])
2634      * @see #dispatchNestedScroll(View, int, int, int, int, int[])
2635      */
startNestedScroll(@onNull View view, @ScrollAxis int axes, @NestedScrollType int type)2636     public static boolean startNestedScroll(@NonNull View view, @ScrollAxis int axes,
2637             @NestedScrollType int type) {
2638         if (view instanceof NestedScrollingChild2) {
2639             return ((NestedScrollingChild2) view).startNestedScroll(axes, type);
2640         } else if (type == ViewCompat.TYPE_TOUCH) {
2641             return startNestedScroll(view, axes);
2642         }
2643         return false;
2644     }
2645 
2646     /**
2647      * Stop a nested scroll in progress.
2648      *
2649      * <p>Calling this method when a nested scroll is not currently in progress is harmless.</p>
2650      *
2651      * @param type the type of input which cause this scroll event
2652      * @see #startNestedScroll(View, int)
2653      */
stopNestedScroll(@onNull View view, @NestedScrollType int type)2654     public static void stopNestedScroll(@NonNull View view, @NestedScrollType int type) {
2655         if (view instanceof NestedScrollingChild2) {
2656             ((NestedScrollingChild2) view).stopNestedScroll(type);
2657         } else if (type == ViewCompat.TYPE_TOUCH) {
2658             stopNestedScroll(view);
2659         }
2660     }
2661 
2662     /**
2663      * Returns true if this view has a nested scrolling parent.
2664      *
2665      * <p>The presence of a nested scrolling parent indicates that this view has initiated
2666      * a nested scroll and it was accepted by an ancestor view further up the view hierarchy.</p>
2667      *
2668      * @param type the type of input which cause this scroll event
2669      * @return whether this view has a nested scrolling parent
2670      */
hasNestedScrollingParent(@onNull View view, @NestedScrollType int type)2671     public static boolean hasNestedScrollingParent(@NonNull View view, @NestedScrollType int type) {
2672         if (view instanceof NestedScrollingChild2) {
2673             ((NestedScrollingChild2) view).hasNestedScrollingParent(type);
2674         } else if (type == ViewCompat.TYPE_TOUCH) {
2675             return hasNestedScrollingParent(view);
2676         }
2677         return false;
2678     }
2679 
2680     /**
2681      * Dispatch one step of a nested scroll in progress.
2682      *
2683      * <p>Implementations of views that support nested scrolling should call this to report
2684      * info about a scroll in progress to the current nested scrolling parent. If a nested scroll
2685      * is not currently in progress or nested scrolling is not
2686      * {@link #isNestedScrollingEnabled(View) enabled} for this view this method does nothing.</p>
2687      *
2688      * <p>Compatible View implementations should also call
2689      * {@link #dispatchNestedPreScroll(View, int, int, int[], int[]) dispatchNestedPreScroll} before
2690      * consuming a component of the scroll event themselves.</p>
2691      *
2692      * @param dxConsumed Horizontal distance in pixels consumed by this view during this scroll step
2693      * @param dyConsumed Vertical distance in pixels consumed by this view during this scroll step
2694      * @param dxUnconsumed Horizontal scroll distance in pixels not consumed by this view
2695      * @param dyUnconsumed Horizontal scroll distance in pixels not consumed by this view
2696      * @param offsetInWindow Optional. If not null, on return this will contain the offset
2697      *                       in local view coordinates of this view from before this operation
2698      *                       to after it completes. View implementations may use this to adjust
2699      *                       expected input coordinate tracking.
2700      * @param type the type of input which cause this scroll event
2701      * @return true if the event was dispatched, false if it could not be dispatched.
2702      * @see #dispatchNestedPreScroll(View, int, int, int[], int[])
2703      */
dispatchNestedScroll(@onNull View view, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, @Nullable int[] offsetInWindow, @NestedScrollType int type)2704     public static boolean dispatchNestedScroll(@NonNull View view, int dxConsumed, int dyConsumed,
2705             int dxUnconsumed, int dyUnconsumed, @Nullable int[] offsetInWindow,
2706             @NestedScrollType int type) {
2707         if (view instanceof NestedScrollingChild2) {
2708             return ((NestedScrollingChild2) view).dispatchNestedScroll(dxConsumed, dyConsumed,
2709                     dxUnconsumed, dyUnconsumed, offsetInWindow, type);
2710         } else if (type == ViewCompat.TYPE_TOUCH) {
2711             return dispatchNestedScroll(view, dxConsumed, dyConsumed, dxUnconsumed,
2712                     dyUnconsumed, offsetInWindow);
2713         }
2714         return false;
2715     }
2716 
2717     /**
2718      * Dispatch one step of a nested scroll in progress before this view consumes any portion of it.
2719      *
2720      * <p>Nested pre-scroll events are to nested scroll events what touch intercept is to touch.
2721      * <code>dispatchNestedPreScroll</code> offers an opportunity for the parent view in a nested
2722      * scrolling operation to consume some or all of the scroll operation before the child view
2723      * consumes it.</p>
2724      *
2725      * @param dx Horizontal scroll distance in pixels
2726      * @param dy Vertical scroll distance in pixels
2727      * @param consumed Output. If not null, consumed[0] will contain the consumed component of dx
2728      *                 and consumed[1] the consumed dy.
2729      * @param offsetInWindow Optional. If not null, on return this will contain the offset
2730      *                       in local view coordinates of this view from before this operation
2731      *                       to after it completes. View implementations may use this to adjust
2732      *                       expected input coordinate tracking.
2733      * @param type the type of input which cause this scroll event
2734      * @return true if the parent consumed some or all of the scroll delta
2735      * @see #dispatchNestedScroll(View, int, int, int, int, int[])
2736      */
dispatchNestedPreScroll(@onNull View view, int dx, int dy, @Nullable int[] consumed, @Nullable int[] offsetInWindow, @NestedScrollType int type)2737     public static boolean dispatchNestedPreScroll(@NonNull View view, int dx, int dy,
2738             @Nullable int[] consumed, @Nullable int[] offsetInWindow, @NestedScrollType int type) {
2739         if (view instanceof NestedScrollingChild2) {
2740             return ((NestedScrollingChild2) view).dispatchNestedPreScroll(dx, dy, consumed,
2741                     offsetInWindow, type);
2742         } else if (type == ViewCompat.TYPE_TOUCH) {
2743             return dispatchNestedPreScroll(view, dx, dy, consumed, offsetInWindow);
2744         }
2745         return false;
2746     }
2747 
2748     /**
2749      * Dispatch a fling to a nested scrolling parent.
2750      *
2751      * <p>This method should be used to indicate that a nested scrolling child has detected
2752      * suitable conditions for a fling. Generally this means that a touch scroll has ended with a
2753      * {@link VelocityTracker velocity} in the direction of scrolling that meets or exceeds
2754      * the {@link ViewConfiguration#getScaledMinimumFlingVelocity() minimum fling velocity}
2755      * along a scrollable axis.</p>
2756      *
2757      * <p>If a nested scrolling child view would normally fling but it is at the edge of
2758      * its own content, it can use this method to delegate the fling to its nested scrolling
2759      * parent instead. The parent may optionally consume the fling or observe a child fling.</p>
2760      *
2761      * @param velocityX Horizontal fling velocity in pixels per second
2762      * @param velocityY Vertical fling velocity in pixels per second
2763      * @param consumed true if the child consumed the fling, false otherwise
2764      * @return true if the nested scrolling parent consumed or otherwise reacted to the fling
2765      */
2766     @SuppressWarnings("RedundantCast") // Intentionally invoking interface method.
dispatchNestedFling(@onNull View view, float velocityX, float velocityY, boolean consumed)2767     public static boolean dispatchNestedFling(@NonNull View view, float velocityX, float velocityY,
2768             boolean consumed) {
2769         if (Build.VERSION.SDK_INT >= 21) {
2770             return view.dispatchNestedFling(velocityX, velocityY, consumed);
2771         }
2772         if (view instanceof NestedScrollingChild) {
2773             return ((NestedScrollingChild) view).dispatchNestedFling(velocityX, velocityY,
2774                     consumed);
2775         }
2776         return false;
2777     }
2778 
2779     /**
2780      * Dispatch a fling to a nested scrolling parent before it is processed by this view.
2781      *
2782      * <p>Nested pre-fling events are to nested fling events what touch intercept is to touch
2783      * and what nested pre-scroll is to nested scroll. <code>dispatchNestedPreFling</code>
2784      * offsets an opportunity for the parent view in a nested fling to fully consume the fling
2785      * before the child view consumes it. If this method returns <code>true</code>, a nested
2786      * parent view consumed the fling and this view should not scroll as a result.</p>
2787      *
2788      * <p>For a better user experience, only one view in a nested scrolling chain should consume
2789      * the fling at a time. If a parent view consumed the fling this method will return false.
2790      * Custom view implementations should account for this in two ways:</p>
2791      *
2792      * <ul>
2793      *     <li>If a custom view is paged and needs to settle to a fixed page-point, do not
2794      *     call <code>dispatchNestedPreFling</code>; consume the fling and settle to a valid
2795      *     position regardless.</li>
2796      *     <li>If a nested parent does consume the fling, this view should not scroll at all,
2797      *     even to settle back to a valid idle position.</li>
2798      * </ul>
2799      *
2800      * <p>Views should also not offer fling velocities to nested parent views along an axis
2801      * where scrolling is not currently supported; a {@link android.widget.ScrollView ScrollView}
2802      * should not offer a horizontal fling velocity to its parents since scrolling along that
2803      * axis is not permitted and carrying velocity along that motion does not make sense.</p>
2804      *
2805      * @param velocityX Horizontal fling velocity in pixels per second
2806      * @param velocityY Vertical fling velocity in pixels per second
2807      * @return true if a nested scrolling parent consumed the fling
2808      */
2809     @SuppressWarnings("RedundantCast") // Intentionally invoking interface method.
dispatchNestedPreFling(@onNull View view, float velocityX, float velocityY)2810     public static boolean dispatchNestedPreFling(@NonNull View view, float velocityX,
2811             float velocityY) {
2812         if (Build.VERSION.SDK_INT >= 21) {
2813             return view.dispatchNestedPreFling(velocityX, velocityY);
2814         }
2815         if (view instanceof NestedScrollingChild) {
2816             return ((NestedScrollingChild) view).dispatchNestedPreFling(velocityX, velocityY);
2817         }
2818         return false;
2819     }
2820 
2821     /**
2822      * Returns whether the view hierarchy is currently undergoing a layout pass. This
2823      * information is useful to avoid situations such as calling {@link View#requestLayout()}
2824      * during a layout pass.
2825      * <p>
2826      * Compatibility:
2827      * <ul>
2828      *     <li>API &lt; 18: Always returns {@code false}</li>
2829      * </ul>
2830      *
2831      * @return whether the view hierarchy is currently undergoing a layout pass
2832      */
isInLayout(@onNull View view)2833     public static boolean isInLayout(@NonNull View view) {
2834         if (Build.VERSION.SDK_INT >= 18) {
2835             return view.isInLayout();
2836         }
2837         return false;
2838     }
2839 
2840     /**
2841      * Returns true if {@code view} has been through at least one layout since it
2842      * was last attached to or detached from a window.
2843      */
isLaidOut(@onNull View view)2844     public static boolean isLaidOut(@NonNull View view) {
2845         if (Build.VERSION.SDK_INT >= 19) {
2846             return view.isLaidOut();
2847         }
2848         return view.getWidth() > 0 && view.getHeight() > 0;
2849     }
2850 
2851     /**
2852      * Returns whether layout direction has been resolved.
2853      * <p>
2854      * Compatibility:
2855      * <ul>
2856      *     <li>API &lt; 19: Always returns {@code false}</li>
2857      * </ul>
2858      *
2859      * @return true if layout direction has been resolved.
2860      */
isLayoutDirectionResolved(@onNull View view)2861     public static boolean isLayoutDirectionResolved(@NonNull View view) {
2862         if (Build.VERSION.SDK_INT >= 19) {
2863             return view.isLayoutDirectionResolved();
2864         }
2865         return false;
2866     }
2867 
2868     /**
2869      * The visual z position of this view, in pixels. This is equivalent to the
2870      * {@link #setTranslationZ(View, float) translationZ} property plus the current
2871      * {@link #getElevation(View) elevation} property.
2872      *
2873      * @return The visual z position of this view, in pixels.
2874      */
getZ(@onNull View view)2875     public static float getZ(@NonNull View view) {
2876         if (Build.VERSION.SDK_INT >= 21) {
2877             return view.getZ();
2878         }
2879         return 0f;
2880     }
2881 
2882     /**
2883      * Sets the visual z position of this view, in pixels. This is equivalent to setting the
2884      * {@link #setTranslationZ(View, float) translationZ} property to be the difference between
2885      * the x value passed in and the current {@link #getElevation(View) elevation} property.
2886      * <p>
2887      * Compatibility:
2888      * <ul>
2889      *     <li>API &lt; 21: No-op
2890      * </ul>
2891      *
2892      * @param z The visual z position of this view, in pixels.
2893      */
setZ(@onNull View view, float z)2894     public static void setZ(@NonNull View view, float z) {
2895         if (Build.VERSION.SDK_INT >= 21) {
2896             view.setZ(z);
2897         }
2898     }
2899 
2900     /**
2901      * Offset this view's vertical location by the specified number of pixels.
2902      *
2903      * @param offset the number of pixels to offset the view by
2904      */
offsetTopAndBottom(@onNull View view, int offset)2905     public static void offsetTopAndBottom(@NonNull View view, int offset) {
2906         if (Build.VERSION.SDK_INT >= 23) {
2907             view.offsetTopAndBottom(offset);
2908         } else if (Build.VERSION.SDK_INT >= 21) {
2909             final Rect parentRect = getEmptyTempRect();
2910             boolean needInvalidateWorkaround = false;
2911 
2912             final ViewParent parent = view.getParent();
2913             if (parent instanceof View) {
2914                 final View p = (View) parent;
2915                 parentRect.set(p.getLeft(), p.getTop(), p.getRight(), p.getBottom());
2916                 // If the view currently does not currently intersect the parent (and is therefore
2917                 // not displayed) we may need need to invalidate
2918                 needInvalidateWorkaround = !parentRect.intersects(view.getLeft(), view.getTop(),
2919                         view.getRight(), view.getBottom());
2920             }
2921 
2922             // Now offset, invoking the API 14+ implementation (which contains its own workarounds)
2923             compatOffsetTopAndBottom(view, offset);
2924 
2925             // The view has now been offset, so let's intersect the Rect and invalidate where
2926             // the View is now displayed
2927             if (needInvalidateWorkaround && parentRect.intersect(view.getLeft(), view.getTop(),
2928                     view.getRight(), view.getBottom())) {
2929                 ((View) parent).invalidate(parentRect);
2930             }
2931         } else {
2932             compatOffsetTopAndBottom(view, offset);
2933         }
2934     }
2935 
compatOffsetTopAndBottom(View view, int offset)2936     private static void compatOffsetTopAndBottom(View view, int offset) {
2937         view.offsetTopAndBottom(offset);
2938         if (view.getVisibility() == View.VISIBLE) {
2939             tickleInvalidationFlag(view);
2940 
2941             ViewParent parent = view.getParent();
2942             if (parent instanceof View) {
2943                 tickleInvalidationFlag((View) parent);
2944             }
2945         }
2946     }
2947 
2948     /**
2949      * Offset this view's horizontal location by the specified amount of pixels.
2950      *
2951      * @param offset the number of pixels to offset the view by
2952      */
offsetLeftAndRight(@onNull View view, int offset)2953     public static void offsetLeftAndRight(@NonNull View view, int offset) {
2954         if (Build.VERSION.SDK_INT >= 23) {
2955             view.offsetLeftAndRight(offset);
2956         } else if (Build.VERSION.SDK_INT >= 21) {
2957             final Rect parentRect = getEmptyTempRect();
2958             boolean needInvalidateWorkaround = false;
2959 
2960             final ViewParent parent = view.getParent();
2961             if (parent instanceof View) {
2962                 final View p = (View) parent;
2963                 parentRect.set(p.getLeft(), p.getTop(), p.getRight(), p.getBottom());
2964                 // If the view currently does not currently intersect the parent (and is therefore
2965                 // not displayed) we may need need to invalidate
2966                 needInvalidateWorkaround = !parentRect.intersects(view.getLeft(), view.getTop(),
2967                         view.getRight(), view.getBottom());
2968             }
2969 
2970             // Now offset, invoking the API 14+ implementation (which contains its own workarounds)
2971             compatOffsetLeftAndRight(view, offset);
2972 
2973             // The view has now been offset, so let's intersect the Rect and invalidate where
2974             // the View is now displayed
2975             if (needInvalidateWorkaround && parentRect.intersect(view.getLeft(), view.getTop(),
2976                     view.getRight(), view.getBottom())) {
2977                 ((View) parent).invalidate(parentRect);
2978             }
2979         } else {
2980             compatOffsetLeftAndRight(view, offset);
2981         }
2982     }
2983 
compatOffsetLeftAndRight(View view, int offset)2984     private static void compatOffsetLeftAndRight(View view, int offset) {
2985         view.offsetLeftAndRight(offset);
2986         if (view.getVisibility() == View.VISIBLE) {
2987             tickleInvalidationFlag(view);
2988 
2989             ViewParent parent = view.getParent();
2990             if (parent instanceof View) {
2991                 tickleInvalidationFlag((View) parent);
2992             }
2993         }
2994     }
2995 
tickleInvalidationFlag(View view)2996     private static void tickleInvalidationFlag(View view) {
2997         final float y = view.getTranslationY();
2998         view.setTranslationY(y + 1);
2999         view.setTranslationY(y);
3000     }
3001 
3002     /**
3003      * Sets a rectangular area on this view to which the view will be clipped
3004      * when it is drawn. Setting the value to null will remove the clip bounds
3005      * and the view will draw normally, using its full bounds.
3006      *
3007      * <p>Prior to API 18 this does nothing.</p>
3008      *
3009      * @param view       The view to set clipBounds.
3010      * @param clipBounds The rectangular area, in the local coordinates of
3011      * this view, to which future drawing operations will be clipped.
3012      */
setClipBounds(@onNull View view, Rect clipBounds)3013     public static void setClipBounds(@NonNull View view, Rect clipBounds) {
3014         if (Build.VERSION.SDK_INT >= 18) {
3015             view.setClipBounds(clipBounds);
3016         }
3017     }
3018 
3019     /**
3020      * Returns a copy of the current {@link #setClipBounds(View, Rect)}.
3021      *
3022      * <p>Prior to API 18 this will return null.</p>
3023      *
3024      * @return A copy of the current clip bounds if clip bounds are set,
3025      * otherwise null.
3026      */
3027     @Nullable
getClipBounds(@onNull View view)3028     public static Rect getClipBounds(@NonNull View view) {
3029         if (Build.VERSION.SDK_INT >= 18) {
3030             return view.getClipBounds();
3031         }
3032         return null;
3033     }
3034 
3035     /**
3036      * Returns true if the provided view is currently attached to a window.
3037      */
isAttachedToWindow(@onNull View view)3038     public static boolean isAttachedToWindow(@NonNull View view) {
3039         if (Build.VERSION.SDK_INT >= 19) {
3040             return view.isAttachedToWindow();
3041         }
3042         return view.getWindowToken() != null;
3043     }
3044 
3045     /**
3046      * Returns whether the provided view has an attached {@link View.OnClickListener}.
3047      *
3048      * @return true if there is a listener, false if there is none.
3049      */
hasOnClickListeners(@onNull View view)3050     public static boolean hasOnClickListeners(@NonNull View view) {
3051         if (Build.VERSION.SDK_INT >= 15) {
3052             return view.hasOnClickListeners();
3053         }
3054         return false;
3055     }
3056 
3057     /**
3058      * Sets the state of all scroll indicators.
3059      * <p>
3060      * See {@link #setScrollIndicators(View, int, int)} for usage information.
3061      *
3062      * @param indicators a bitmask of indicators that should be enabled, or
3063      *                   {@code 0} to disable all indicators
3064      *
3065      * @see #setScrollIndicators(View, int, int)
3066      * @see #getScrollIndicators(View)
3067      */
setScrollIndicators(@onNull View view, @ScrollIndicators int indicators)3068     public static void setScrollIndicators(@NonNull View view, @ScrollIndicators int indicators) {
3069         if (Build.VERSION.SDK_INT >= 23) {
3070             view.setScrollIndicators(indicators);
3071         }
3072     }
3073 
3074     /**
3075      * Sets the state of the scroll indicators specified by the mask. To change
3076      * all scroll indicators at once, see {@link #setScrollIndicators(View, int)}.
3077      * <p>
3078      * When a scroll indicator is enabled, it will be displayed if the view
3079      * can scroll in the direction of the indicator.
3080      * <p>
3081      * Multiple indicator types may be enabled or disabled by passing the
3082      * logical OR of the desired types. If multiple types are specified, they
3083      * will all be set to the same enabled state.
3084      * <p>
3085      * For example, to enable the top scroll indicatorExample: {@code setScrollIndicators}
3086      *
3087      * @param indicators the indicator direction, or the logical OR of multiple
3088      *             indicator directions. One or more of:
3089      *             <ul>
3090      *               <li>{@link #SCROLL_INDICATOR_TOP}</li>
3091      *               <li>{@link #SCROLL_INDICATOR_BOTTOM}</li>
3092      *               <li>{@link #SCROLL_INDICATOR_LEFT}</li>
3093      *               <li>{@link #SCROLL_INDICATOR_RIGHT}</li>
3094      *               <li>{@link #SCROLL_INDICATOR_START}</li>
3095      *               <li>{@link #SCROLL_INDICATOR_END}</li>
3096      *             </ul>
3097      *
3098      * @see #setScrollIndicators(View, int)
3099      * @see #getScrollIndicators(View)
3100      */
setScrollIndicators(@onNull View view, @ScrollIndicators int indicators, @ScrollIndicators int mask)3101     public static void setScrollIndicators(@NonNull View view, @ScrollIndicators int indicators,
3102             @ScrollIndicators int mask) {
3103         if (Build.VERSION.SDK_INT >= 23) {
3104             view.setScrollIndicators(indicators, mask);
3105         }
3106     }
3107 
3108     /**
3109      * Returns a bitmask representing the enabled scroll indicators.
3110      * <p>
3111      * For example, if the top and left scroll indicators are enabled and all
3112      * other indicators are disabled, the return value will be
3113      * {@code ViewCompat.SCROLL_INDICATOR_TOP | ViewCompat.SCROLL_INDICATOR_LEFT}.
3114      * <p>
3115      * To check whether the bottom scroll indicator is enabled, use the value
3116      * of {@code (ViewCompat.getScrollIndicators(view) & ViewCompat.SCROLL_INDICATOR_BOTTOM) != 0}.
3117      *
3118      * @return a bitmask representing the enabled scroll indicators
3119      */
getScrollIndicators(@onNull View view)3120     public static int getScrollIndicators(@NonNull View view) {
3121         if (Build.VERSION.SDK_INT >= 23) {
3122             return view.getScrollIndicators();
3123         }
3124         return 0;
3125     }
3126 
3127     /**
3128      * Set the pointer icon for the current view.
3129      * @param pointerIcon A PointerIconCompat instance which will be shown when the mouse hovers.
3130      */
setPointerIcon(@onNull View view, PointerIconCompat pointerIcon)3131     public static void setPointerIcon(@NonNull View view, PointerIconCompat pointerIcon) {
3132         if (Build.VERSION.SDK_INT >= 24) {
3133             view.setPointerIcon((PointerIcon) (pointerIcon != null
3134                     ? pointerIcon.getPointerIcon() : null));
3135         }
3136     }
3137 
3138     /**
3139      * Gets the logical display to which the view's window has been attached.
3140      * <p>
3141      * Compatibility:
3142      * <ul>
3143      * <li>API &lt; 17: Returns the default display when the view is attached. Otherwise, null.
3144      * </ul>
3145      *
3146      * @return The logical display, or null if the view is not currently attached to a window.
3147      */
3148     @Nullable
getDisplay(@onNull View view)3149     public static Display getDisplay(@NonNull View view) {
3150         if (Build.VERSION.SDK_INT >= 17) {
3151             return view.getDisplay();
3152         }
3153         if (isAttachedToWindow(view)) {
3154             final WindowManager wm = (WindowManager) view.getContext().getSystemService(
3155                     Context.WINDOW_SERVICE);
3156             return wm.getDefaultDisplay();
3157         }
3158         return null;
3159     }
3160 
3161     /**
3162      * Sets the tooltip for the view.
3163      *
3164      * <p>Prior to API 26 this does nothing. Use TooltipCompat class from v7 appcompat library
3165      * for a compatible tooltip implementation.</p>
3166      *
3167      * @param tooltipText the tooltip text
3168      */
setTooltipText(@onNull View view, @Nullable CharSequence tooltipText)3169     public static void setTooltipText(@NonNull View view, @Nullable CharSequence tooltipText) {
3170         if (Build.VERSION.SDK_INT >= 26) {
3171             view.setTooltipText(tooltipText);
3172         }
3173     }
3174 
3175     /**
3176      * Start the drag and drop operation.
3177      */
startDragAndDrop(@onNull View v, ClipData data, View.DragShadowBuilder shadowBuilder, Object localState, int flags)3178     public static boolean startDragAndDrop(@NonNull View v, ClipData data,
3179             View.DragShadowBuilder shadowBuilder, Object localState, int flags) {
3180         if (Build.VERSION.SDK_INT >= 24) {
3181             return v.startDragAndDrop(data, shadowBuilder, localState, flags);
3182         } else {
3183             return v.startDrag(data, shadowBuilder, localState, flags);
3184         }
3185     }
3186 
3187     /**
3188      * Cancel the drag and drop operation.
3189      */
cancelDragAndDrop(@onNull View v)3190     public static void cancelDragAndDrop(@NonNull View v) {
3191         if (Build.VERSION.SDK_INT >= 24) {
3192             v.cancelDragAndDrop();
3193         }
3194     }
3195 
3196     /**
3197      * Update the drag shadow while drag and drop is in progress.
3198      */
updateDragShadow(@onNull View v, View.DragShadowBuilder shadowBuilder)3199     public static void updateDragShadow(@NonNull View v, View.DragShadowBuilder shadowBuilder) {
3200         if (Build.VERSION.SDK_INT >= 24) {
3201             v.updateDragShadow(shadowBuilder);
3202         }
3203     }
3204 
3205     /**
3206      * Gets the ID of the next keyboard navigation cluster root.
3207      *
3208      * @return the next keyboard navigation cluster ID, or {@link View#NO_ID} if the framework
3209      *         should decide automatically or API < 26.
3210      */
getNextClusterForwardId(@onNull View view)3211     public static int getNextClusterForwardId(@NonNull View view) {
3212         if (Build.VERSION.SDK_INT >= 26) {
3213             return view.getNextClusterForwardId();
3214         }
3215         return View.NO_ID;
3216     }
3217 
3218     /**
3219      * Sets the ID of the next keyboard navigation cluster root view. Does nothing if {@code view}
3220      * is not a keyboard navigation cluster or if API < 26.
3221      *
3222      * @param nextClusterForwardId next cluster ID, or {@link View#NO_ID} if the framework
3223      *                             should decide automatically.
3224      */
setNextClusterForwardId(@onNull View view, int nextClusterForwardId)3225     public static void setNextClusterForwardId(@NonNull View view, int nextClusterForwardId) {
3226         if (Build.VERSION.SDK_INT >= 26) {
3227             view.setNextClusterForwardId(nextClusterForwardId);
3228         }
3229     }
3230 
3231     /**
3232      * Returns whether {@code view} is a root of a keyboard navigation cluster. Always returns
3233      * {@code false} on API < 26.
3234      *
3235      * @return {@code true} if this view is a root of a cluster, or {@code false} otherwise.
3236      */
isKeyboardNavigationCluster(@onNull View view)3237     public static boolean isKeyboardNavigationCluster(@NonNull View view) {
3238         if (Build.VERSION.SDK_INT >= 26) {
3239             return view.isKeyboardNavigationCluster();
3240         }
3241         return false;
3242     }
3243 
3244     /**
3245      * Set whether {@code view} is a root of a keyboard navigation cluster. Does nothing if
3246      * API < 26.
3247      *
3248      * @param isCluster {@code true} to mark {@code view} as the root of a cluster, {@code false}
3249      *                  to unmark.
3250      */
setKeyboardNavigationCluster(@onNull View view, boolean isCluster)3251     public static void setKeyboardNavigationCluster(@NonNull View view, boolean isCluster) {
3252         if (Build.VERSION.SDK_INT >= 26) {
3253             view.setKeyboardNavigationCluster(isCluster);
3254         }
3255     }
3256 
3257     /**
3258      * Returns whether {@code view} should receive focus when the focus is restored for the view
3259      * hierarchy containing it. Returns {@code false} on API < 26.
3260      * <p>
3261      * Focus gets restored for a view hierarchy when the root of the hierarchy gets added to a
3262      * window or serves as a target of cluster navigation.
3263      *
3264      * @return {@code true} if {@code view} is the default-focus view, {@code false} otherwise.
3265      */
isFocusedByDefault(@onNull View view)3266     public static boolean isFocusedByDefault(@NonNull View view) {
3267         if (Build.VERSION.SDK_INT >= 26) {
3268             return view.isFocusedByDefault();
3269         }
3270         return false;
3271     }
3272 
3273     /**
3274      * Sets whether {@code view} should receive focus when the focus is restored for the view
3275      * hierarchy containing it.
3276      * <p>
3277      * Focus gets restored for a view hierarchy when the root of the hierarchy gets added to a
3278      * window or serves as a target of cluster navigation.
3279      * <p>
3280      * Does nothing on API < 26.
3281      *
3282      * @param isFocusedByDefault {@code true} to set {@code view} as the default-focus view,
3283      *                           {@code false} otherwise.
3284      */
setFocusedByDefault(@onNull View view, boolean isFocusedByDefault)3285     public static void setFocusedByDefault(@NonNull View view, boolean isFocusedByDefault) {
3286         if (Build.VERSION.SDK_INT >= 26) {
3287             view.setFocusedByDefault(isFocusedByDefault);
3288         }
3289     }
3290 
3291     /**
3292      * Find the nearest keyboard navigation cluster in the specified direction.
3293      * This does not actually give focus to that cluster.
3294      *
3295      * @param currentCluster The starting point of the search. {@code null} means the current
3296      *                       cluster is not found yet.
3297      * @param direction Direction to look.
3298      *
3299      * @return the nearest keyboard navigation cluster in the specified direction, or {@code null}
3300      *         if one can't be found or if API < 26.
3301      */
keyboardNavigationClusterSearch(@onNull View view, View currentCluster, @FocusDirection int direction)3302     public static View keyboardNavigationClusterSearch(@NonNull View view, View currentCluster,
3303             @FocusDirection int direction) {
3304         if (Build.VERSION.SDK_INT >= 26) {
3305             return view.keyboardNavigationClusterSearch(currentCluster, direction);
3306         }
3307         return null;
3308     }
3309 
3310     /**
3311      * Adds any keyboard navigation cluster roots that are descendants of {@code view} (
3312      * including {@code view} if it is a cluster root itself) to {@code views}. Does nothing
3313      * on API < 26.
3314      *
3315      * @param views collection of keyboard navigation cluster roots found so far.
3316      * @param direction direction to look.
3317      */
addKeyboardNavigationClusters(@onNull View view, @NonNull Collection<View> views, int direction)3318     public static void addKeyboardNavigationClusters(@NonNull View view,
3319             @NonNull Collection<View> views, int direction) {
3320         if (Build.VERSION.SDK_INT >= 26) {
3321             view.addKeyboardNavigationClusters(views, direction);
3322         }
3323     }
3324 
3325     /**
3326      * Gives focus to the default-focus view in the view hierarchy rooted at {@code view}.
3327      * If the default-focus view cannot be found or if API < 26, this falls back to calling
3328      * {@link View#requestFocus(int)}.
3329      *
3330      * @return {@code true} if {@code view} or one of its descendants took focus, {@code false}
3331      *         otherwise.
3332      */
restoreDefaultFocus(@onNull View view)3333     public static boolean restoreDefaultFocus(@NonNull View view) {
3334         if (Build.VERSION.SDK_INT >= 26) {
3335             return view.restoreDefaultFocus();
3336         }
3337         return view.requestFocus();
3338     }
3339 
3340     /**
3341      * Returns true if this view is focusable or if it contains a reachable View
3342      * for which {@link View#hasExplicitFocusable()} returns {@code true}.
3343      * A "reachable hasExplicitFocusable()" is a view whose parents do not block descendants focus.
3344      * Only {@link View#VISIBLE} views for which {@link View#getFocusable()} would return
3345      * {@link View#FOCUSABLE} are considered focusable.
3346      *
3347      * <p>This method preserves the pre-{@link Build.VERSION_CODES#O} behavior of
3348      * {@link View#hasFocusable()} in that only views explicitly set focusable will cause
3349      * this method to return true. A view set to {@link View#FOCUSABLE_AUTO} that resolves
3350      * to focusable will not.</p>
3351      *
3352      * @return {@code true} if the view is focusable or if the view contains a focusable
3353      *         view, {@code false} otherwise
3354      */
hasExplicitFocusable(@onNull View view)3355     public static boolean hasExplicitFocusable(@NonNull View view) {
3356         if (Build.VERSION.SDK_INT >= 26) {
3357             return view.hasExplicitFocusable();
3358         }
3359         return view.hasFocusable();
3360     }
3361 
3362     /**
3363      * Generate a value suitable for use in {@link View#setId(int)}.
3364      * This value will not collide with ID values generated at build time by aapt for R.id.
3365      *
3366      * @return a generated ID value
3367      */
generateViewId()3368     public static int generateViewId() {
3369         if (Build.VERSION.SDK_INT >= 17) {
3370             return View.generateViewId();
3371         }
3372         for (;;) {
3373             final int result = sNextGeneratedId.get();
3374             // aapt-generated IDs have the high byte nonzero; clamp to the range under that.
3375             int newValue = result + 1;
3376             if (newValue > 0x00FFFFFF) newValue = 1; // Roll over to 1, not 0.
3377             if (sNextGeneratedId.compareAndSet(result, newValue)) {
3378                 return result;
3379             }
3380         }
3381     }
3382 
ViewCompat()3383     protected ViewCompat() {}
3384 }
3385