1 /*
2  * Copyright 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.core.view;
18 
19 import static android.os.Build.VERSION.SDK_INT;
20 
21 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
22 import static androidx.core.graphics.Insets.toCompatInsets;
23 
24 import android.annotation.SuppressLint;
25 import android.graphics.Rect;
26 import android.util.Log;
27 import android.view.View;
28 import android.view.WindowInsets;
29 import android.view.inputmethod.EditorInfo;
30 import android.view.inputmethod.InputMethod;
31 
32 import androidx.annotation.IntDef;
33 import androidx.annotation.IntRange;
34 import androidx.annotation.RequiresApi;
35 import androidx.annotation.RestrictTo;
36 import androidx.core.graphics.Insets;
37 import androidx.core.util.ObjectsCompat;
38 import androidx.core.util.Preconditions;
39 import androidx.core.view.WindowInsetsCompat.Type.InsetsType;
40 
41 import org.jspecify.annotations.NonNull;
42 import org.jspecify.annotations.Nullable;
43 
44 import java.lang.annotation.Retention;
45 import java.lang.annotation.RetentionPolicy;
46 import java.lang.reflect.Constructor;
47 import java.lang.reflect.Field;
48 import java.lang.reflect.Method;
49 import java.util.Objects;
50 
51 /**
52  * Describes a set of insets for window content.
53  *
54  * <p>WindowInsetsCompats are immutable and may be expanded to include more inset types in the
55  * future. To adjust insets, use one of the supplied clone methods to obtain a new
56  * WindowInsetsCompat instance with the adjusted properties.</p>
57  */
58 public class WindowInsetsCompat {
59     private static final String TAG = "WindowInsetsCompat";
60 
61     /**
62      * A {@link WindowInsetsCompat} instance for which {@link #isConsumed()} returns {@code true}.
63      * <p>
64      * This can be used during insets dispatch in the view hierarchy by returning this value from
65      * {@code View.onApplyWindowInsets(WindowInsets)} or
66      * {@link OnApplyWindowInsetsListener#onApplyWindowInsets(View, WindowInsetsCompat)} to stop
67      * dispatch the insets to its children to avoid traversing the entire view hierarchy.
68      * <p>
69      * The application should return this instance once it has taken care of all insets on a certain
70      * level in the view hierarchy, and doesn't need to dispatch to its children anymore for better
71      * performance.
72      *
73      * @see #isConsumed()
74      */
75     public static final @NonNull WindowInsetsCompat CONSUMED;
76 
77     static {
78         if (SDK_INT >= 34) {
79             CONSUMED = Impl34.CONSUMED;
80         } else if (SDK_INT >= 30) {
81             CONSUMED = Impl30.CONSUMED;
82         } else {
83             CONSUMED = Impl.CONSUMED;
84         }
85     }
86 
87     private final Impl mImpl;
88 
89     @RequiresApi(20)
WindowInsetsCompat(@onNull WindowInsets insets)90     private WindowInsetsCompat(@NonNull WindowInsets insets) {
91         if (SDK_INT >= 36) {
92             mImpl = new Impl36(this, insets);
93         } else if (SDK_INT >= 34) {
94             mImpl = new Impl34(this, insets);
95         } else if (SDK_INT >= 30) {
96             mImpl = new Impl30(this, insets);
97         } else if (SDK_INT >= 29) {
98             mImpl = new Impl29(this, insets);
99         } else if (SDK_INT >= 28) {
100             mImpl = new Impl28(this, insets);
101         } else if (SDK_INT >= 21) {
102             mImpl = new Impl21(this, insets);
103         } else if (SDK_INT >= 20) {
104             mImpl = new Impl20(this, insets);
105         } else {
106             mImpl = new Impl(this);
107         }
108     }
109 
110     /**
111      * Constructs a new WindowInsetsCompat, copying all values from a source WindowInsetsCompat.
112      *
113      * @param src source from which values are copied
114      */
WindowInsetsCompat(final @Nullable WindowInsetsCompat src)115     public WindowInsetsCompat(final @Nullable WindowInsetsCompat src) {
116         if (src != null) {
117             // We'll copy over from the 'src' instance's impl
118             final Impl srcImpl = src.mImpl;
119             if (SDK_INT >= 34 && srcImpl instanceof Impl34) {
120                 mImpl = new Impl34(this, (Impl34) srcImpl);
121             } else if (SDK_INT >= 30 && srcImpl instanceof Impl30) {
122                 mImpl = new Impl30(this, (Impl30) srcImpl);
123             } else if (SDK_INT >= 29 && srcImpl instanceof Impl29) {
124                 mImpl = new Impl29(this, (Impl29) srcImpl);
125             } else if (SDK_INT >= 28 && srcImpl instanceof Impl28) {
126                 mImpl = new Impl28(this, (Impl28) srcImpl);
127             } else if (SDK_INT >= 21 && srcImpl instanceof Impl21) {
128                 mImpl = new Impl21(this, (Impl21) srcImpl);
129             } else if (SDK_INT >= 20 && srcImpl instanceof Impl20) {
130                 mImpl = new Impl20(this, (Impl20) srcImpl);
131             } else {
132                 mImpl = new Impl(this);
133             }
134             srcImpl.copyWindowDataInto(this);
135         } else {
136             // Ideally src would be @NonNull, oh well.
137             mImpl = new Impl(this);
138         }
139     }
140 
141     /**
142      * Wrap an instance of {@link WindowInsets} into a {@link WindowInsetsCompat}.
143      * <p>
144      * This version of the function does not allow the resulting {@link WindowInsetsCompat} to be
145      * aware of the root window insets and root view, meaning that the returned values for many of
146      * the different inset {@link Type}s will be incorrect.
147      * <p>
148      * Prefer using {@link #toWindowInsetsCompat(WindowInsets, View)} instead.
149      *
150      * @param insets source insets to wrap
151      * @return the wrapped instance
152      */
153     @RequiresApi(20)
toWindowInsetsCompat(@onNull WindowInsets insets)154     public static @NonNull WindowInsetsCompat toWindowInsetsCompat(@NonNull WindowInsets insets) {
155         return toWindowInsetsCompat(insets, null);
156     }
157 
158     /**
159      * Wrap an instance of {@link WindowInsets} into a {@link WindowInsetsCompat}.
160      * <p>
161      * This version of the function allows the resulting {@link WindowInsetsCompat} to be
162      * aware of the root window insets and root view through the {@code view} parameter. This is
163      * required for many of the different inset {@link Type}s to return correct values when used
164      * on devices running Android 10 and before.
165      *
166      * @param insets source insets to wrap
167      * @param view view to use as an entry point for obtaining root window information. This
168      *             view needs be attached to the window, otherwise it will be ignored.
169      * @return the wrapped instance
170      */
171     @RequiresApi(20)
toWindowInsetsCompat(@onNull WindowInsets insets, @Nullable View view)172     public static @NonNull WindowInsetsCompat toWindowInsetsCompat(@NonNull WindowInsets insets,
173             @Nullable View view) {
174         WindowInsetsCompat wic = new WindowInsetsCompat(Preconditions.checkNotNull(insets));
175         if (view != null && view.isAttachedToWindow()) {
176             // Pass the root window insets, which is useful if the Activity is adjustResize
177             wic.setRootWindowInsets(ViewCompat.getRootWindowInsets(view));
178             // Pass in the root view which allows the WIC to make of a copy of it's visible bounds
179             wic.copyRootViewBounds(view.getRootView());
180             // Take System UI visibility into account while computing system bar insets
181             wic.setSystemUiVisibility(view.getWindowSystemUiVisibility());
182         }
183         return wic;
184     }
185 
186     /**
187      * Returns the left system window inset in pixels.
188      *
189      * <p>The system window inset represents the area of a full-screen window that is
190      * partially or fully obscured by the status bar, navigation bar, IME or other system windows.
191      *
192      * <p>When running on platforms with API 19 and below, this method always returns {@code 0}.
193      *
194      * @return The left system window inset
195      * @deprecated Use {@link #getInsets(int)} with {@link Type#systemBars()} instead.
196      */
197     @Deprecated
getSystemWindowInsetLeft()198     public int getSystemWindowInsetLeft() {
199         return mImpl.getSystemWindowInsets().left;
200     }
201 
202     /**
203      * Returns the top system window inset in pixels.
204      *
205      * <p>The system window inset represents the area of a full-screen window that is
206      * partially or fully obscured by the status bar, navigation bar, IME or other system windows.
207      *
208      * <p>When running on platforms with API 19 and below, this method always returns {@code 0}.
209      *
210      * @return The top system window inset
211      * @deprecated Use {@link #getInsets(int)} with {@link Type#systemBars()} instead.
212      */
213     @Deprecated
getSystemWindowInsetTop()214     public int getSystemWindowInsetTop() {
215         return mImpl.getSystemWindowInsets().top;
216     }
217 
218     /**
219      * Returns the right system window inset in pixels.
220      *
221      * <p>The system window inset represents the area of a full-screen window that is
222      * partially or fully obscured by the status bar, navigation bar, IME or other system windows.
223      *
224      * <p>When running on platforms with API 19 and below, this method always returns {@code 0}.
225      *
226      * @return The right system window inset
227      * @deprecated Use {@link #getInsets(int)} with {@link Type#systemBars()} instead.
228      */
229     @Deprecated
getSystemWindowInsetRight()230     public int getSystemWindowInsetRight() {
231         return mImpl.getSystemWindowInsets().right;
232     }
233 
234     /**
235      * Returns the bottom system window inset in pixels.
236      *
237      * <p>The system window inset represents the area of a full-screen window that is
238      * partially or fully obscured by the status bar, navigation bar, IME or other system windows.
239      *
240      * <p>When running on platforms with API 19 and below, this method always returns {@code 0}.
241      *
242      * @return The bottom system window inset
243      * @deprecated Use {@link #getInsets(int)} with {@link Type#systemBars()} instead.
244      */
245     @Deprecated
getSystemWindowInsetBottom()246     public int getSystemWindowInsetBottom() {
247         return mImpl.getSystemWindowInsets().bottom;
248     }
249 
250     /**
251      * Returns true if this WindowInsets has nonzero system window insets.
252      *
253      * <p>The system window inset represents the area of a full-screen window that is
254      * partially or fully obscured by the status bar, navigation bar, IME or other system windows.
255      *
256      * <p>When running on platforms with API 19 and below, this method always returns {@code false}.
257      *
258      * @return true if any of the system window inset values are nonzero
259      * @deprecated Use {@link #getInsets(int)} with {@link Type#systemBars()}
260      * instead.
261      */
262     @Deprecated
hasSystemWindowInsets()263     public boolean hasSystemWindowInsets() {
264         return !mImpl.getSystemWindowInsets().equals(Insets.NONE);
265     }
266 
267     /**
268      * Returns true if this WindowInsets has any non-zero insets.
269      *
270      * <p>When running on platforms with API 19 and below, this method always returns {@code false}.
271      *
272      * @return true if any inset values are nonzero
273      */
hasInsets()274     public boolean hasInsets() {
275         return !getInsets(Type.all()).equals(Insets.NONE)
276                 || !getInsetsIgnoringVisibility(Type.all() ^ Type.ime()).equals(Insets.NONE)
277                 || getDisplayCutout() != null;
278     }
279 
280     /**
281      * Check if these insets have been fully consumed.
282      *
283      * <p>Insets are considered "consumed" if the applicable <code>consume*</code> methods
284      * have been called such that all insets have been set to zero. This affects propagation of
285      * insets through the view hierarchy; insets that have not been fully consumed will continue
286      * to propagate down to child views.</p>
287      *
288      * <p>The result of this method is equivalent to the return value of
289      * {@link android.view.View#fitSystemWindows(android.graphics.Rect)}.</p>
290      *
291      * @return true if the insets have been fully consumed.
292      */
isConsumed()293     public boolean isConsumed() {
294         return mImpl.isConsumed();
295     }
296 
297     /**
298      * Returns true if the associated window has a round shape.
299      *
300      * <p>A round window's left, top, right and bottom edges reach all the way to the
301      * associated edges of the window but the corners may not be visible. Views responding
302      * to round insets should take care to not lay out critical elements within the corners
303      * where they may not be accessible.</p>
304      *
305      * <p>When running on platforms with API 19 and below, this method always returns {@code false}.
306      *
307      * @return true if the window is round
308      */
isRound()309     public boolean isRound() {
310         return mImpl.isRound();
311     }
312 
313     /**
314      * Returns a copy of this WindowInsets with the system window insets fully consumed.
315      *
316      * <p>When running on platforms with API 19 and below, this method always returns {@code null}.
317      *
318      * @return A modified copy of this WindowInsets
319      * @deprecated Consuming of different parts individually of a {@link WindowInsetsCompat}
320      * instance is deprecated, since {@link WindowInsetsCompat} contains many different insets. Use
321      * {@link #CONSUMED} instead to stop dispatching insets.
322      */
323     @Deprecated
consumeSystemWindowInsets()324     public @NonNull WindowInsetsCompat consumeSystemWindowInsets() {
325         return mImpl.consumeSystemWindowInsets();
326     }
327 
328     /**
329      * Returns a copy of this WindowInsets with selected system window insets replaced
330      * with new values.
331      *
332      * <p>When running on platforms with API 19 and below, this method always returns {@code null}.
333      *
334      * @param left   New left inset in pixels
335      * @param top    New top inset in pixels
336      * @param right  New right inset in pixels
337      * @param bottom New bottom inset in pixels
338      * @return A modified copy of this WindowInsets
339      * @deprecated use {@link WindowInsetsCompat.Builder} with
340      * {@link WindowInsetsCompat.Builder#setSystemWindowInsets(Insets)} instead.
341      */
342     @SuppressWarnings("deprecation") // Builder.setSystemWindowInsets
343     @Deprecated
replaceSystemWindowInsets(int left, int top, int right, int bottom)344     public @NonNull WindowInsetsCompat replaceSystemWindowInsets(int left, int top, int right,
345             int bottom) {
346         return new Builder(this)
347                 .setSystemWindowInsets(Insets.of(left, top, right, bottom))
348                 .build();
349     }
350 
351     /**
352      * Returns a copy of this WindowInsets with selected system window insets replaced
353      * with new values.
354      *
355      * <p>When running on platforms with API 19 and below, this method always returns {@code null}.
356      *
357      * @param systemWindowInsets New system window insets. Each field is the inset in pixels
358      *                           for that edge
359      * @return A modified copy of this WindowInsets
360      * @deprecated use {@link WindowInsetsCompat.Builder} with
361      * {@link WindowInsetsCompat.Builder#setSystemWindowInsets(Insets)} instead.
362      */
363     @SuppressWarnings("deprecation")
364     @Deprecated
replaceSystemWindowInsets(@onNull Rect systemWindowInsets)365     public @NonNull WindowInsetsCompat replaceSystemWindowInsets(@NonNull Rect systemWindowInsets) {
366         return new Builder(this)
367                 .setSystemWindowInsets(Insets.of(systemWindowInsets))
368                 .build();
369     }
370 
371     /**
372      * Returns the top stable inset in pixels.
373      *
374      * <p>The stable inset represents the area of a full-screen window that <b>may</b> be
375      * partially or fully obscured by the system UI elements.  This value does not change
376      * based on the visibility state of those elements; for example, if the status bar is
377      * normally shown, but temporarily hidden, the stable inset will still provide the inset
378      * associated with the status bar being shown.</p>
379      *
380      * <p>When running on platforms with API 20 and below, this method always returns {@code 0}.
381      *
382      * @deprecated Use {@link #getInsetsIgnoringVisibility(int)} with {@link Type#systemBars()}
383      * instead.
384      */
385     @Deprecated
getStableInsetTop()386     public int getStableInsetTop() {
387         return mImpl.getStableInsets().top;
388     }
389 
390     /**
391      * Returns the left stable inset in pixels.
392      *
393      * <p>The stable inset represents the area of a full-screen window that <b>may</b> be
394      * partially or fully obscured by the system UI elements.  This value does not change
395      * based on the visibility state of those elements; for example, if the status bar is
396      * normally shown, but temporarily hidden, the stable inset will still provide the inset
397      * associated with the status bar being shown.</p>
398      *
399      * <p>When running on platforms with API 20 and below, this method always returns {@code 0}.
400      *
401      * @return The left stable inset
402      * @deprecated Use {@link #getInsetsIgnoringVisibility(int)} with {@link Type#systemBars()}
403      * instead.
404      */
405     @Deprecated
getStableInsetLeft()406     public int getStableInsetLeft() {
407         return mImpl.getStableInsets().left;
408     }
409 
410     /**
411      * Returns the right stable inset in pixels.
412      *
413      * <p>The stable inset represents the area of a full-screen window that <b>may</b> be
414      * partially or fully obscured by the system UI elements.  This value does not change
415      * based on the visibility state of those elements; for example, if the status bar is
416      * normally shown, but temporarily hidden, the stable inset will still provide the inset
417      * associated with the status bar being shown.</p>
418      *
419      * <p>When running on platforms with API 20 and below, this method always returns {@code 0}.
420      *
421      * @return The right stable inset
422      * @deprecated Use {@link #getInsetsIgnoringVisibility(int)} with {@link Type#systemBars()}
423      * instead.
424      */
425     @Deprecated
getStableInsetRight()426     public int getStableInsetRight() {
427         return mImpl.getStableInsets().right;
428     }
429 
430     /**
431      * Returns the bottom stable inset in pixels.
432      *
433      * <p>The stable inset represents the area of a full-screen window that <b>may</b> be
434      * partially or fully obscured by the system UI elements.  This value does not change
435      * based on the visibility state of those elements; for example, if the status bar is
436      * normally shown, but temporarily hidden, the stable inset will still provide the inset
437      * associated with the status bar being shown.</p>
438      *
439      * <p>When running on platforms with API 20 and below, this method always returns {@code 0}.
440      *
441      * @return The bottom stable inset
442      * @deprecated Use {@link #getInsetsIgnoringVisibility(int)} with {@link Type#systemBars()}
443      * instead.
444      */
445     @Deprecated
getStableInsetBottom()446     public int getStableInsetBottom() {
447         return mImpl.getStableInsets().bottom;
448     }
449 
450     /**
451      * Returns true if this WindowInsets has nonzero stable insets.
452      *
453      * <p>The stable inset represents the area of a full-screen window that <b>may</b> be
454      * partially or fully obscured by the system UI elements.  This value does not change
455      * based on the visibility state of those elements; for example, if the status bar is
456      * normally shown, but temporarily hidden, the stable inset will still provide the inset
457      * associated with the status bar being shown.</p>
458      *
459      * <p>When running on platforms with API 20 and below, this method always returns {@code false}.
460      *
461      * @return true if any of the stable inset values are nonzero
462      * @deprecated Use {@link #getInsetsIgnoringVisibility(int)} with {@link Type#systemBars()}
463      * instead.
464      */
465     @Deprecated
hasStableInsets()466     public boolean hasStableInsets() {
467         return !mImpl.getStableInsets().equals(Insets.NONE);
468     }
469 
470     /**
471      * Returns a copy of this WindowInsets with the stable insets fully consumed.
472      *
473      * <p>When running on platforms with API 20 and below, this method always returns {@code null}.
474      *
475      * @return A modified copy of this WindowInsetsCompat
476      * @deprecated Consuming of different parts individually of a {@link WindowInsetsCompat}
477      * instance is deprecated, since {@link WindowInsetsCompat} contains many different insets. Use
478      * {@link #CONSUMED} instead to stop dispatching insets.
479      */
480     @Deprecated
consumeStableInsets()481     public @NonNull WindowInsetsCompat consumeStableInsets() {
482         return mImpl.consumeStableInsets();
483     }
484 
485     /**
486      * Returns the display cutout if there is one.
487      *
488      * <p>When running on platforms with API 27 and below, this method always returns {@code null}.
489      *
490      * @return the display cutout or null if there is none
491      * @see DisplayCutoutCompat
492      */
getDisplayCutout()493     public @Nullable DisplayCutoutCompat getDisplayCutout() {
494         return mImpl.getDisplayCutout();
495     }
496 
497     /**
498      * Returns a copy of this WindowInsets with the cutout fully consumed.
499      *
500      * <p>When running on platforms with API 27 and below, this method is a no-op.
501      *
502      * @return A modified copy of this WindowInsets
503      * @deprecated Consuming of different parts individually of a {@link WindowInsetsCompat}
504      * instance is deprecated, since {@link WindowInsetsCompat} contains many different insets. Use
505      * {@link #CONSUMED} instead to stop dispatching insets.
506      */
507     @Deprecated
consumeDisplayCutout()508     public @NonNull WindowInsetsCompat consumeDisplayCutout() {
509         return mImpl.consumeDisplayCutout();
510     }
511 
512     /**
513      * Returns the system window insets in pixels.
514      *
515      * <p>The system window inset represents the area of a full-screen window that is
516      * partially or fully obscured by the status bar, navigation bar, IME or other system windows.
517      * </p>
518      *
519      * @return The system window insets
520      * @see #getSystemWindowInsetLeft()
521      * @see #getSystemWindowInsetTop()
522      * @see #getSystemWindowInsetRight()
523      * @see #getSystemWindowInsetBottom()
524      * @deprecated Use {@link #getInsets(int)} with {@link Type#systemBars()} instead.
525      */
526     @Deprecated
getSystemWindowInsets()527     public @NonNull Insets getSystemWindowInsets() {
528         return mImpl.getSystemWindowInsets();
529     }
530 
531     /**
532      * Returns the stable insets in pixels.
533      *
534      * <p>The stable inset represents the area of a full-screen window that <b>may</b> be
535      * partially or fully obscured by the system UI elements.  This value does not change
536      * based on the visibility state of those elements; for example, if the status bar is
537      * normally shown, but temporarily hidden, the stable inset will still provide the inset
538      * associated with the status bar being shown.</p>
539      *
540      * @return The stable insets
541      * @see #getStableInsetLeft()
542      * @see #getStableInsetTop()
543      * @see #getStableInsetRight()
544      * @see #getStableInsetBottom()
545      * @deprecated Use {@link #getInsetsIgnoringVisibility(int)} with {@link Type#systemBars()}
546      * instead.
547      */
548     @Deprecated
getStableInsets()549     public @NonNull Insets getStableInsets() {
550         return mImpl.getStableInsets();
551     }
552 
553     /**
554      * Returns the mandatory system gesture insets.
555      *
556      * <p>The mandatory system gesture insets represent the area of a window where mandatory system
557      * gestures have priority and may consume some or all touch input, e.g. due to the a system bar
558      * occupying it, or it being reserved for touch-only gestures.
559      *
560      * @see WindowInsets#getMandatorySystemGestureInsets
561      * @deprecated Use {@link #getInsets(int)} with {@link Type#mandatorySystemGestures()}
562      * instead.
563      */
564     @Deprecated
getMandatorySystemGestureInsets()565     public @NonNull Insets getMandatorySystemGestureInsets() {
566         return mImpl.getMandatorySystemGestureInsets();
567     }
568 
569     /**
570      * Returns the tappable element insets.
571      *
572      * <p>The tappable element insets represent how much tappable elements <b>must at least</b> be
573      * inset to remain both tappable and visually unobstructed by persistent system windows.
574      *
575      * <p>This may be smaller than {@link #getSystemWindowInsets()} if the system window is
576      * largely transparent and lets through simple taps (but not necessarily more complex gestures).
577      *
578      * @see WindowInsets#getTappableElementInsets
579      * @deprecated Use {@link #getInsets(int)} with {@link Type#tappableElement()}
580      * instead.
581      */
582     @Deprecated
getTappableElementInsets()583     public @NonNull Insets getTappableElementInsets() {
584         return mImpl.getTappableElementInsets();
585     }
586 
587     /**
588      * Returns the system gesture insets.
589      *
590      * <p>The system gesture insets represent the area of a window where system gestures have
591      * priority and may consume some or all touch input, e.g. due to the a system bar
592      * occupying it, or it being reserved for touch-only gestures.
593      *
594      * <p>An app can declare priority over system gestures with
595      * {@link android.view.View#setSystemGestureExclusionRects} outside of the
596      * {@link #getMandatorySystemGestureInsets() mandatory system gesture insets}.
597      *
598      * @see WindowInsets#getSystemGestureInsets
599      * @deprecated Use {@link #getInsets(int)} with {@link Type#systemGestures()}
600      * instead.
601      */
602     @Deprecated
getSystemGestureInsets()603     public @NonNull Insets getSystemGestureInsets() {
604         return mImpl.getSystemGestureInsets();
605     }
606 
607     /**
608      * Returns a copy of this instance inset in the given directions.
609      *
610      * This is intended for dispatching insets to areas of the window that are smaller than the
611      * current area.
612      *
613      * <p>Example:
614      * <pre>
615      * childView.dispatchApplyWindowInsets(insets.inset(childMargins));
616      * </pre>
617      *
618      * @param insets the amount of insets to remove from all sides.
619      *
620      * @see #inset(int, int, int, int)
621      */
inset(@onNull Insets insets)622     public @NonNull WindowInsetsCompat inset(@NonNull Insets insets) {
623         return inset(insets.left, insets.top, insets.right, insets.bottom);
624     }
625 
626     /**
627      * Returns a copy of this instance inset in the given directions.
628      *
629      * This is intended for dispatching insets to areas of the window that are smaller than the
630      * current area.
631      *
632      * <p>Example:
633      * <pre>
634      * childView.dispatchApplyWindowInsets(insets.inset(
635      *         childMarginLeft, childMarginTop, childMarginBottom, childMarginRight));
636      * </pre>
637      *
638      * @param left the amount of insets to remove from the left. Must be non-negative.
639      * @param top the amount of insets to remove from the top. Must be non-negative.
640      * @param right the amount of insets to remove from the right. Must be non-negative.
641      * @param bottom the amount of insets to remove from the bottom. Must be non-negative.
642      *
643      * @return the inset insets
644      */
inset(@ntRangefrom = 0) int left, @IntRange(from = 0) int top, @IntRange(from = 0) int right, @IntRange(from = 0) int bottom)645     public @NonNull WindowInsetsCompat inset(@IntRange(from = 0) int left,
646             @IntRange(from = 0) int top, @IntRange(from = 0) int right,
647             @IntRange(from = 0) int bottom) {
648         return mImpl.inset(left, top, right, bottom);
649     }
650 
651     /**
652      * Returns the insets of a specific set of windows causing insets, denoted by the
653      * {@code typeMask} bit mask of {@link Type}s.
654      *
655      * When running on devices with API Level 29 and before, the returned insets are an
656      * approximation based on the information available. This is especially true for the {@link
657      * Type#ime IME} type, which currently only works when running on devices with SDK level 23
658      * and above.
659      *
660      * @param typeMask Bit mask of {@link Type}s to query the insets for.
661      * @return The insets.
662      */
getInsets(@nsetsType int typeMask)663     public @NonNull Insets getInsets(@InsetsType int typeMask) {
664         return mImpl.getInsets(typeMask);
665     }
666 
667     /**
668      * Returns the insets a specific set of windows can cause, denoted by the
669      * {@code typeMask} bit mask of {@link Type}s, regardless of whether that type is
670      * currently visible or not.
671      *
672      * <p>The insets represents the area of a a window that that <b>may</b> be partially
673      * or fully obscured by the system window identified by {@code typeMask}. This value does not
674      * change based on the visibility state of those elements. For example, if the status bar is
675      * normally shown, but temporarily hidden, the inset returned here will still provide the inset
676      * associated with the status bar being shown.</p>
677      *
678      * When running on devices with API Level 29 and before, the returned insets are an
679      * approximation based on the information available. This is especially true for the {@link
680      * Type#ime IME} type, which currently only works when running on devices with SDK level 23
681      * and above.
682      *
683      * @param typeMask Bit mask of {@link Type}s to query the insets for.
684      * @return The insets.
685      * @throws IllegalArgumentException If the caller tries to query {@link Type#ime()}. Insets are
686      *                                  not available if the IME isn't visible as the height of the
687      *                                  IME is dynamic depending on the {@link EditorInfo} of the
688      *                                  currently focused view, as well as the UI state of the IME.
689      */
getInsetsIgnoringVisibility(@nsetsType int typeMask)690     public @NonNull Insets getInsetsIgnoringVisibility(@InsetsType int typeMask) {
691         return mImpl.getInsetsIgnoringVisibility(typeMask);
692     }
693 
694     /**
695      * Returns whether a set of windows that may cause insets is currently visible on screen,
696      * regardless of whether it actually overlaps with this window.
697      *
698      * When running on devices with API Level 29 and before, the returned value is an
699      * approximation based on the information available. This is especially true for the {@link
700      * Type#ime IME} type, which currently only works when running on devices with SDK level 23
701      * and above.
702      *
703      * @param typeMask Bit mask of {@link Type}s to query visibility status.
704      * @return {@code true} if and only if all windows included in {@code typeMask} are currently
705      * visible on screen.
706      */
isVisible(@nsetsType int typeMask)707     public boolean isVisible(@InsetsType int typeMask) {
708         return mImpl.isVisible(typeMask);
709     }
710 
711     @Override
equals(Object o)712     public boolean equals(Object o) {
713         if (this == o) {
714             return true;
715         }
716         if (!(o instanceof WindowInsetsCompat)) {
717             return false;
718         }
719         WindowInsetsCompat other = (WindowInsetsCompat) o;
720         return ObjectsCompat.equals(mImpl, other.mImpl);
721     }
722 
723     @Override
hashCode()724     public int hashCode() {
725         return mImpl == null ? 0 : mImpl.hashCode();
726     }
727 
728     /**
729      * Return the source {@link WindowInsets} instance used in this {@link WindowInsetsCompat}.
730      *
731      * @return the wrapped WindowInsets instance
732      */
733     @RequiresApi(20)
toWindowInsets()734     public @Nullable WindowInsets toWindowInsets() {
735         return mImpl instanceof Impl20 ? ((Impl20) mImpl).mPlatformInsets : null;
736     }
737 
738     private static class Impl {
739         @SuppressWarnings("deprecation")
740         static final @NonNull WindowInsetsCompat CONSUMED = new WindowInsetsCompat.Builder()
741                 .build()
742                 .consumeDisplayCutout()
743                 .consumeStableInsets()
744                 .consumeSystemWindowInsets();
745 
746         final WindowInsetsCompat mHost;
747 
Impl(@onNull WindowInsetsCompat host)748         Impl(@NonNull WindowInsetsCompat host) {
749             mHost = host;
750         }
751 
isRound()752         boolean isRound() {
753             return false;
754         }
755 
isConsumed()756         boolean isConsumed() {
757             return false;
758         }
759 
consumeSystemWindowInsets()760         @NonNull WindowInsetsCompat consumeSystemWindowInsets() {
761             return mHost;
762         }
763 
consumeStableInsets()764         @NonNull WindowInsetsCompat consumeStableInsets() {
765             return mHost;
766         }
767 
getDisplayCutout()768         @Nullable DisplayCutoutCompat getDisplayCutout() {
769             return null;
770         }
771 
consumeDisplayCutout()772         @NonNull WindowInsetsCompat consumeDisplayCutout() {
773             return mHost;
774         }
775 
getSystemWindowInsets()776         @NonNull Insets getSystemWindowInsets() {
777             return Insets.NONE;
778         }
779 
getStableInsets()780         @NonNull Insets getStableInsets() {
781             return Insets.NONE;
782         }
783 
getSystemGestureInsets()784         @NonNull Insets getSystemGestureInsets() {
785             // Pre-Q return the system window insets
786             return getSystemWindowInsets();
787         }
788 
getMandatorySystemGestureInsets()789         @NonNull Insets getMandatorySystemGestureInsets() {
790             // Pre-Q return the system window insets
791             return getSystemWindowInsets();
792         }
793 
getTappableElementInsets()794         @NonNull Insets getTappableElementInsets() {
795             // Pre-Q return the system window insets
796             return getSystemWindowInsets();
797         }
798 
inset(int left, int top, int right, int bottom)799         @NonNull WindowInsetsCompat inset(int left, int top, int right, int bottom) {
800             return CONSUMED;
801         }
802 
getInsets(@nsetsType int typeMask)803         @NonNull Insets getInsets(@InsetsType int typeMask) {
804             return Insets.NONE;
805         }
806 
getInsetsIgnoringVisibility(@nsetsType int typeMask)807         @NonNull Insets getInsetsIgnoringVisibility(@InsetsType int typeMask) {
808             if ((typeMask & Type.IME) != 0) {
809                 throw new IllegalArgumentException("Unable to query the maximum insets for IME");
810             }
811             return Insets.NONE;
812         }
813 
isVisible(@nsetsType int typeMask)814         boolean isVisible(@InsetsType int typeMask) {
815             return true;
816         }
817 
818         @Override
equals(Object o)819         public boolean equals(Object o) {
820             // On API < 28 we can not rely on WindowInsets.equals(), so we handle it manually
821             if (this == o) return true;
822             if (!(o instanceof Impl)) return false;
823             final Impl impl = (Impl) o;
824             return isRound() == impl.isRound()
825                     && isConsumed() == impl.isConsumed()
826                     && ObjectsCompat.equals(getSystemWindowInsets(), impl.getSystemWindowInsets())
827                     && ObjectsCompat.equals(getStableInsets(), impl.getStableInsets())
828                     && ObjectsCompat.equals(getDisplayCutout(), impl.getDisplayCutout());
829         }
830 
831         @Override
hashCode()832         public int hashCode() {
833             // On API < 28 we can not rely on WindowInsets.hashCode(), so we handle it manually
834             return ObjectsCompat.hash(isRound(), isConsumed(), getSystemWindowInsets(),
835                     getStableInsets(), getDisplayCutout());
836         }
837 
setRootWindowInsets(@ullable WindowInsetsCompat rootWindowInsets)838         void setRootWindowInsets(@Nullable WindowInsetsCompat rootWindowInsets) {
839         }
840 
setRootViewData(@onNull Insets visibleInsets)841         void setRootViewData(@NonNull Insets visibleInsets) {
842         }
843 
copyRootViewBounds(@onNull View rootView)844         void copyRootViewBounds(@NonNull View rootView) {
845         }
846 
setSystemUiVisibility(int systemUiVisibility)847         void setSystemUiVisibility(int systemUiVisibility) {
848         }
849 
copyWindowDataInto(@onNull WindowInsetsCompat other)850         void copyWindowDataInto(@NonNull WindowInsetsCompat other) {
851         }
852 
setOverriddenInsets(Insets[] insetsTypeMask)853         public void setOverriddenInsets(Insets[] insetsTypeMask) {
854         }
855 
setStableInsets(Insets stableInsets)856         public void setStableInsets(Insets stableInsets) {
857         }
858     }
859 
860     @RequiresApi(20)
861     private static class Impl20 extends Impl {
862 
863         private static final int SYSTEM_BAR_VISIBILITY_MASK =
864                 View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
865         private static boolean sVisibleRectReflectionFetched = false;
866         private static Method sGetViewRootImplMethod;
867         private static Class<?> sAttachInfoClass;
868         private static Field sVisibleInsetsField;
869         private static Field sAttachInfoField;
870 
871         final @NonNull WindowInsets mPlatformInsets;
872 
873         // TODO(175859616) save all insets in the array
874         private Insets[] mOverriddenInsets;
875 
876         // Used to cache the wrapped value
877         private Insets mSystemWindowInsets = null;
878 
879         private WindowInsetsCompat mRootWindowInsets;
880         Insets mRootViewVisibleInsets;
881 
882         int mSystemUiVisibility;
883 
Impl20(@onNull WindowInsetsCompat host, @NonNull WindowInsets insets)884         Impl20(@NonNull WindowInsetsCompat host, @NonNull WindowInsets insets) {
885             super(host);
886             mPlatformInsets = insets;
887         }
888 
Impl20(@onNull WindowInsetsCompat host, @NonNull Impl20 other)889         Impl20(@NonNull WindowInsetsCompat host, @NonNull Impl20 other) {
890             this(host, new WindowInsets(other.mPlatformInsets));
891         }
892 
893         @Override
isRound()894         boolean isRound() {
895             return mPlatformInsets.isRound();
896         }
897 
898         @Override
getInsets(int typeMask)899         public @NonNull Insets getInsets(int typeMask) {
900             return getInsets(typeMask, false);
901         }
902 
903         @Override
getInsetsIgnoringVisibility(int typeMask)904         public @NonNull Insets getInsetsIgnoringVisibility(int typeMask) {
905             return getInsets(typeMask, true);
906         }
907 
908         @Override
909         @SuppressLint("WrongConstant")
isVisible(final int typeMask)910         boolean isVisible(final int typeMask) {
911             for (int i = Type.FIRST; i <= Type.LAST; i = i << 1) {
912                 if ((typeMask & i) == 0) {
913                     continue;
914                 }
915                 if (!isTypeVisible(i)) {
916                     return false;
917                 }
918             }
919             return true;
920         }
921 
922         @SuppressLint("WrongConstant")
getInsets(final int typeMask, final boolean ignoreVisibility)923         private @NonNull Insets getInsets(final int typeMask, final boolean ignoreVisibility) {
924             Insets result = Insets.NONE;
925             for (int i = Type.FIRST; i <= Type.LAST; i = i << 1) {
926                 if ((typeMask & i) == 0) {
927                     continue;
928                 }
929                 result = Insets.max(result, getInsetsForType(i, ignoreVisibility));
930             }
931             return result;
932         }
933 
934         @SuppressWarnings("deprecation")
getInsetsForType(@nsetsType int type, boolean ignoreVisibility)935         protected @NonNull Insets getInsetsForType(@InsetsType int type, boolean ignoreVisibility) {
936             switch (type) {
937                 case Type.STATUS_BARS: {
938                     if (ignoreVisibility) {
939                         final Insets rootStable = getRootStableInsets();
940                         return Insets.of(0,
941                                 Math.max(rootStable.top, getSystemWindowInsets().top), 0, 0);
942                     } else if ((mSystemUiVisibility & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0) {
943                         return Insets.NONE;
944                     } else {
945                         return Insets.of(0, getSystemWindowInsets().top, 0, 0);
946                     }
947                 }
948                 case Type.NAVIGATION_BARS: {
949                     if (ignoreVisibility) {
950                         final Insets rootStable = getRootStableInsets();
951                         final Insets stable = getStableInsets();
952                         return Insets.of(
953                                 Math.max(rootStable.left, stable.left),
954                                 0, /* zero top inset (== status bar) */
955                                 Math.max(rootStable.right, stable.right),
956                                 Math.max(rootStable.bottom, stable.bottom)
957                         );
958                     } else if ((mSystemUiVisibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0) {
959                         return Insets.NONE;
960                     } else {
961                         final Insets systemWindow = getSystemWindowInsets();
962                         final Insets rootStable = mRootWindowInsets != null
963                                 ? mRootWindowInsets.getStableInsets()
964                                 : null;
965 
966                         int bottom = systemWindow.bottom;
967                         if (rootStable != null) {
968                             // If we have root stable insets, and its bottom is less than the
969                             // system window bottom, it's likely that the IME is visible. In this
970                             // case, we want to only return the stable bottom.
971                             bottom = Math.min(bottom, rootStable.bottom);
972                         }
973                         return Insets.of(
974                                 systemWindow.left,
975                                 0, /* zero top inset (== status bar) */
976                                 systemWindow.right,
977                                 bottom
978                         );
979                     }
980                 }
981                 case Type.IME: {
982                     Insets overriddenInsets = mOverriddenInsets != null
983                             ? mOverriddenInsets[Type.indexOf(Type.IME)] : null;
984                     if (overriddenInsets != null) {
985                         return overriddenInsets;
986                     }
987                     final Insets systemWindow = getSystemWindowInsets();
988                     final Insets rootStable = getRootStableInsets();
989 
990                     if (systemWindow.bottom > rootStable.bottom) {
991                         // This handles the adjustResize case on < API 30, since
992                         // systemWindow.bottom is probably going to be the IME
993                         return Insets.of(0, 0, 0, systemWindow.bottom);
994                     } else if (mRootViewVisibleInsets != null
995                             && !mRootViewVisibleInsets.equals(Insets.NONE)) {
996                         // This handles the adjustPan case on < API 30. We look at the root view's
997                         // visible rect and check it's bottom against the root stable insets
998                         if (mRootViewVisibleInsets.bottom > rootStable.bottom) {
999                             return Insets.of(0, 0, 0, mRootViewVisibleInsets.bottom);
1000                         }
1001                     }
1002                     return Insets.NONE;
1003                 }
1004                 case Type.SYSTEM_GESTURES: {
1005                     // Visibility does not affect this type of inset
1006                     return getSystemGestureInsets();
1007                 }
1008                 case Type.MANDATORY_SYSTEM_GESTURES: {
1009                     // Visibility does not affect this type of inset
1010                     return getMandatorySystemGestureInsets();
1011                 }
1012                 case Type.TAPPABLE_ELEMENT: {
1013                     // Visibility does not affect this type of inset
1014                     return getTappableElementInsets();
1015                 }
1016                 case Type.DISPLAY_CUTOUT: {
1017                     // Visibility does not affect this type of inset
1018                     final DisplayCutoutCompat cutout = mRootWindowInsets != null
1019                             ? mRootWindowInsets.getDisplayCutout()
1020                             : getDisplayCutout();
1021                     if (cutout != null) {
1022                         return Insets.of(cutout.getSafeInsetLeft(), cutout.getSafeInsetTop(),
1023                                 cutout.getSafeInsetRight(), cutout.getSafeInsetBottom());
1024                     } else {
1025                         return Insets.NONE;
1026                     }
1027                 }
1028                 default:
1029                     return Insets.NONE;
1030             }
1031         }
1032 
isTypeVisible(@nsetsType final int type)1033         protected boolean isTypeVisible(@InsetsType final int type) {
1034             switch (type) {
1035                 case Type.STATUS_BARS:
1036                 case Type.NAVIGATION_BARS:
1037                 case Type.IME:
1038                 case Type.DISPLAY_CUTOUT:
1039                     return !getInsetsForType(type, false).equals(Insets.NONE);
1040                 case Type.CAPTION_BAR:
1041                     // Caption bar is always false on API < 30
1042                     return false;
1043                 default:
1044                     return true;
1045             }
1046         }
1047 
1048         @Override
getSystemWindowInsets()1049         final @NonNull Insets getSystemWindowInsets() {
1050             if (mSystemWindowInsets == null) {
1051                 mSystemWindowInsets = Insets.of(
1052                         mPlatformInsets.getSystemWindowInsetLeft(),
1053                         mPlatformInsets.getSystemWindowInsetTop(),
1054                         mPlatformInsets.getSystemWindowInsetRight(),
1055                         mPlatformInsets.getSystemWindowInsetBottom());
1056             }
1057             return mSystemWindowInsets;
1058         }
1059 
1060         @Override
1061         @SuppressWarnings("deprecation")
inset(int left, int top, int right, int bottom)1062         @NonNull WindowInsetsCompat inset(int left, int top, int right, int bottom) {
1063             Builder b = new Builder(toWindowInsetsCompat(mPlatformInsets));
1064             b.setSystemWindowInsets(insetInsets(getSystemWindowInsets(), left, top, right, bottom));
1065             b.setStableInsets(insetInsets(getStableInsets(), left, top, right, bottom));
1066             return b.build();
1067         }
1068 
1069         @Override
copyWindowDataInto(@onNull WindowInsetsCompat other)1070         void copyWindowDataInto(@NonNull WindowInsetsCompat other) {
1071             other.setRootWindowInsets(mRootWindowInsets);
1072             other.setRootViewData(mRootViewVisibleInsets);
1073             other.setSystemUiVisibility(mSystemUiVisibility);
1074         }
1075 
1076         @Override
setRootWindowInsets(@ullable WindowInsetsCompat rootWindowInsets)1077         void setRootWindowInsets(@Nullable WindowInsetsCompat rootWindowInsets) {
1078             mRootWindowInsets = rootWindowInsets;
1079         }
1080 
1081         @Override
setRootViewData(@onNull Insets visibleInsets)1082         void setRootViewData(@NonNull Insets visibleInsets) {
1083             mRootViewVisibleInsets = visibleInsets;
1084         }
1085 
1086         @SuppressWarnings("deprecation")
getRootStableInsets()1087         private Insets getRootStableInsets() {
1088             if (mRootWindowInsets != null) {
1089                 return mRootWindowInsets.getStableInsets();
1090             } else {
1091                 return Insets.NONE;
1092             }
1093         }
1094 
1095         @Override
copyRootViewBounds(@onNull View rootView)1096         void copyRootViewBounds(@NonNull View rootView) {
1097             Insets visibleInsets = getVisibleInsets(rootView);
1098             if (visibleInsets == null) {
1099                 visibleInsets = Insets.NONE;
1100             }
1101             setRootViewData(visibleInsets);
1102         }
1103 
1104         @Override
setSystemUiVisibility(int systemUiVisibility)1105         void setSystemUiVisibility(int systemUiVisibility) {
1106             mSystemUiVisibility = systemUiVisibility;
1107         }
1108 
1109         /**
1110          * Attempt to get a copy of the visible rect from this rootView's AttachInfo.
1111          *
1112          * @return a copy of the provided view's AttachInfo.mVisibleRect or null if anything fails
1113          */
getVisibleInsets(@onNull View rootView)1114         private @Nullable Insets getVisibleInsets(@NonNull View rootView) {
1115             if (SDK_INT >= 30) {
1116                 throw new UnsupportedOperationException("getVisibleInsets() should not be called "
1117                         + "on API >= 30. Use WindowInsets.isVisible() instead.");
1118             } else {
1119                 if (!sVisibleRectReflectionFetched) {
1120                     loadReflectionField();
1121                 }
1122 
1123                 if (sGetViewRootImplMethod == null
1124                         || sAttachInfoClass == null
1125                         || sVisibleInsetsField == null) {
1126                     return null;
1127                 }
1128 
1129                 try {
1130                     Object viewRootImpl = sGetViewRootImplMethod.invoke(rootView);
1131                     if (viewRootImpl == null) {
1132                         Log.w(TAG, "Failed to get visible insets. getViewRootImpl() returned null"
1133                                         + " from the provided view. This means that the view is "
1134                                         + "either not attached or the method has been overridden",
1135                                 new NullPointerException());
1136                         return null;
1137                     } else {
1138                         Object mAttachInfo = sAttachInfoField.get(viewRootImpl);
1139                         Rect visibleRect = (Rect) sVisibleInsetsField.get(mAttachInfo);
1140                         return visibleRect != null ? Insets.of(visibleRect) : null;
1141                     }
1142                 } catch (ReflectiveOperationException e) {
1143                     Log.e(TAG,
1144                             "Failed to get visible insets. (Reflection error). " + e.getMessage(),
1145                             e);
1146                 }
1147             }
1148             return null;
1149         }
1150 
1151         @Override
setOverriddenInsets(Insets[] insetsTypeMask)1152         public void setOverriddenInsets(Insets[] insetsTypeMask) {
1153             mOverriddenInsets = insetsTypeMask;
1154         }
1155 
1156         @SuppressWarnings("JavaReflectionMemberAccess") // Reflection on private method
1157         @SuppressLint("PrivateApi")
loadReflectionField()1158         private static void loadReflectionField() {
1159             try {
1160                 sGetViewRootImplMethod = View.class.getDeclaredMethod("getViewRootImpl");
1161                 sAttachInfoClass = Class.forName("android.view.View$AttachInfo");
1162                 sVisibleInsetsField = sAttachInfoClass.getDeclaredField("mVisibleInsets");
1163                 Class<?> viewRootImplClass = Class.forName("android.view.ViewRootImpl");
1164                 sAttachInfoField = viewRootImplClass.getDeclaredField("mAttachInfo");
1165                 sVisibleInsetsField.setAccessible(true);
1166                 sAttachInfoField.setAccessible(true);
1167             } catch (ReflectiveOperationException e) {
1168                 Log.e(TAG, "Failed to get visible insets. (Reflection error). " + e.getMessage(),
1169                         e);
1170             }
1171             sVisibleRectReflectionFetched = true;
1172         }
1173 
1174         @Override
equals(Object o)1175         public boolean equals(Object o) {
1176             if (!super.equals(o)) return false;
1177             Impl20 impl20 = (Impl20) o;
1178             return Objects.equals(mRootViewVisibleInsets, impl20.mRootViewVisibleInsets)
1179                     && systemBarVisibilityEquals(mSystemUiVisibility, impl20.mSystemUiVisibility);
1180         }
1181 
systemBarVisibilityEquals(int vis1, int vis2)1182         static boolean systemBarVisibilityEquals(int vis1, int vis2) {
1183             return (SYSTEM_BAR_VISIBILITY_MASK & vis1) == (SYSTEM_BAR_VISIBILITY_MASK & vis2);
1184         }
1185     }
1186 
1187     @RequiresApi(21)
1188     private static class Impl21 extends Impl20 {
1189         private Insets mStableInsets = null;
1190 
Impl21(@onNull WindowInsetsCompat host, @NonNull WindowInsets insets)1191         Impl21(@NonNull WindowInsetsCompat host, @NonNull WindowInsets insets) {
1192             super(host, insets);
1193         }
1194 
Impl21(@onNull WindowInsetsCompat host, @NonNull Impl21 other)1195         Impl21(@NonNull WindowInsetsCompat host, @NonNull Impl21 other) {
1196             super(host, other);
1197             mStableInsets = other.mStableInsets;
1198         }
1199 
1200         @Override
isConsumed()1201         boolean isConsumed() {
1202             return mPlatformInsets.isConsumed();
1203         }
1204 
1205         @Override
consumeStableInsets()1206         @NonNull WindowInsetsCompat consumeStableInsets() {
1207             return toWindowInsetsCompat(mPlatformInsets.consumeStableInsets());
1208         }
1209 
1210         @Override
consumeSystemWindowInsets()1211         @NonNull WindowInsetsCompat consumeSystemWindowInsets() {
1212             return toWindowInsetsCompat(mPlatformInsets.consumeSystemWindowInsets());
1213         }
1214 
1215         @Override
getStableInsets()1216         final @NonNull Insets getStableInsets() {
1217             if (mStableInsets == null) {
1218                 mStableInsets = Insets.of(
1219                         mPlatformInsets.getStableInsetLeft(),
1220                         mPlatformInsets.getStableInsetTop(),
1221                         mPlatformInsets.getStableInsetRight(),
1222                         mPlatformInsets.getStableInsetBottom());
1223             }
1224             return mStableInsets;
1225         }
1226 
1227         @Override
setStableInsets(@ullable Insets stableInsets)1228         public void setStableInsets(@Nullable Insets stableInsets) {
1229             mStableInsets = stableInsets;
1230         }
1231 
1232     }
1233 
1234     @RequiresApi(28)
1235     private static class Impl28 extends Impl21 {
Impl28(@onNull WindowInsetsCompat host, @NonNull WindowInsets insets)1236         Impl28(@NonNull WindowInsetsCompat host, @NonNull WindowInsets insets) {
1237             super(host, insets);
1238         }
1239 
Impl28(@onNull WindowInsetsCompat host, @NonNull Impl28 other)1240         Impl28(@NonNull WindowInsetsCompat host, @NonNull Impl28 other) {
1241             super(host, other);
1242         }
1243 
1244         @Override
getDisplayCutout()1245         @Nullable DisplayCutoutCompat getDisplayCutout() {
1246             return DisplayCutoutCompat.wrap(mPlatformInsets.getDisplayCutout());
1247         }
1248 
1249         @Override
consumeDisplayCutout()1250         @NonNull WindowInsetsCompat consumeDisplayCutout() {
1251             return toWindowInsetsCompat(mPlatformInsets.consumeDisplayCutout());
1252         }
1253 
1254         @Override
equals(Object o)1255         public boolean equals(Object o) {
1256             if (this == o) return true;
1257             if (!(o instanceof Impl28)) return false;
1258             Impl28 otherImpl28 = (Impl28) o;
1259             // On API 28+ we can rely on WindowInsets.equals()
1260             return Objects.equals(mPlatformInsets, otherImpl28.mPlatformInsets)
1261                     && Objects.equals(mRootViewVisibleInsets, otherImpl28.mRootViewVisibleInsets)
1262                     && systemBarVisibilityEquals(
1263                             mSystemUiVisibility, otherImpl28.mSystemUiVisibility);
1264         }
1265 
1266         @Override
hashCode()1267         public int hashCode() {
1268             return mPlatformInsets.hashCode();
1269         }
1270     }
1271 
1272     @RequiresApi(29)
1273     private static class Impl29 extends Impl28 {
1274         // Used to cache the wrapped values
1275         private Insets mSystemGestureInsets = null;
1276         private Insets mMandatorySystemGestureInsets = null;
1277         private Insets mTappableElementInsets = null;
1278 
Impl29(@onNull WindowInsetsCompat host, @NonNull WindowInsets insets)1279         Impl29(@NonNull WindowInsetsCompat host, @NonNull WindowInsets insets) {
1280             super(host, insets);
1281         }
1282 
Impl29(@onNull WindowInsetsCompat host, @NonNull Impl29 other)1283         Impl29(@NonNull WindowInsetsCompat host, @NonNull Impl29 other) {
1284             super(host, other);
1285         }
1286 
1287         @Override
getSystemGestureInsets()1288         @NonNull Insets getSystemGestureInsets() {
1289             if (mSystemGestureInsets == null) {
1290                 mSystemGestureInsets = toCompatInsets(mPlatformInsets.getSystemGestureInsets());
1291             }
1292             return mSystemGestureInsets;
1293         }
1294 
1295         @Override
getMandatorySystemGestureInsets()1296         @NonNull Insets getMandatorySystemGestureInsets() {
1297             if (mMandatorySystemGestureInsets == null) {
1298                 mMandatorySystemGestureInsets =
1299                         toCompatInsets(mPlatformInsets.getMandatorySystemGestureInsets());
1300             }
1301             return mMandatorySystemGestureInsets;
1302         }
1303 
1304         @Override
getTappableElementInsets()1305         @NonNull Insets getTappableElementInsets() {
1306             if (mTappableElementInsets == null) {
1307                 mTappableElementInsets = toCompatInsets(mPlatformInsets.getTappableElementInsets());
1308             }
1309             return mTappableElementInsets;
1310         }
1311 
1312         @Override
inset(int left, int top, int right, int bottom)1313         @NonNull WindowInsetsCompat inset(int left, int top, int right, int bottom) {
1314             final WindowInsetsCompat newInsets =
1315                     toWindowInsetsCompat(mPlatformInsets.inset(left, top, right, bottom));
1316 
1317             // WindowInsets#insetInsets of API 29 has a bug, so here uses our own implementation.
1318             final WindowInsetsCompat.Builder builder = new WindowInsetsCompat.Builder(newInsets);
1319             builder.setSystemWindowInsets(
1320                     insetInsets(getSystemWindowInsets(), left, top, right, bottom));
1321             builder.setStableInsets(
1322                     insetInsets(getStableInsets(), left, top, right, bottom));
1323             builder.setSystemGestureInsets(
1324                     insetInsets(getSystemGestureInsets(), left, top, right, bottom));
1325             builder.setMandatorySystemGestureInsets(
1326                     insetInsets(getMandatorySystemGestureInsets(), left, top, right, bottom));
1327             builder.setTappableElementInsets(
1328                     insetInsets(getTappableElementInsets(), left, top, right, bottom));
1329             return builder.build();
1330         }
1331 
1332         @Override
setStableInsets(@ullable Insets stableInsets)1333         public void setStableInsets(@Nullable Insets stableInsets) {
1334             //no-op already in mPlatformInsets
1335         }
1336     }
1337 
insetInsets(@onNull Insets insets, int left, int top, int right, int bottom)1338     static Insets insetInsets(@NonNull Insets insets, int left, int top, int right, int bottom) {
1339         int newLeft = Math.max(0, insets.left - left);
1340         int newTop = Math.max(0, insets.top - top);
1341         int newRight = Math.max(0, insets.right - right);
1342         int newBottom = Math.max(0, insets.bottom - bottom);
1343         if (newLeft == insets.left && newTop == insets.top
1344                 && newRight == insets.right && newBottom == insets.bottom) {
1345             return insets;
1346         }
1347         return Insets.of(newLeft, newTop, newRight, newBottom);
1348     }
1349 
1350     @RequiresApi(30)
1351     private static class Impl30 extends Impl29 {
1352         static final @NonNull WindowInsetsCompat CONSUMED =
1353                 toWindowInsetsCompat(WindowInsets.CONSUMED);
1354 
Impl30(@onNull WindowInsetsCompat host, @NonNull WindowInsets insets)1355         Impl30(@NonNull WindowInsetsCompat host, @NonNull WindowInsets insets) {
1356             super(host, insets);
1357         }
1358 
Impl30(@onNull WindowInsetsCompat host, @NonNull Impl30 other)1359         Impl30(@NonNull WindowInsetsCompat host, @NonNull Impl30 other) {
1360             super(host, other);
1361         }
1362 
1363         @Override
getInsets(int typeMask)1364         public @NonNull Insets getInsets(int typeMask) {
1365             return toCompatInsets(
1366                     mPlatformInsets.getInsets(TypeImpl30.toPlatformType(typeMask))
1367             );
1368         }
1369 
1370         @Override
getInsetsIgnoringVisibility(int typeMask)1371         public @NonNull Insets getInsetsIgnoringVisibility(int typeMask) {
1372             return toCompatInsets(
1373                     mPlatformInsets.getInsetsIgnoringVisibility(TypeImpl30.toPlatformType(typeMask))
1374             );
1375         }
1376 
1377         @Override
isVisible(int typeMask)1378         public boolean isVisible(int typeMask) {
1379             return mPlatformInsets.isVisible(TypeImpl30.toPlatformType(typeMask));
1380         }
1381 
1382         @Override
copyRootViewBounds(@onNull View rootView)1383         final void copyRootViewBounds(@NonNull View rootView) {
1384             // This is only used to copy the root view visible insets which is
1385             // then only used to get the visibility of the IME on API < 30.
1386             // Overriding this avoid to go through the code path to get the visible insets via
1387             // reflection.
1388         }
1389 
1390         @SuppressLint("WrongConstant")
1391         @Override
inset(int left, int top, int right, int bottom)1392         @NonNull WindowInsetsCompat inset(int left, int top, int right, int bottom) {
1393             final WindowInsetsCompat newInsets =
1394                     toWindowInsetsCompat(mPlatformInsets.inset(left, top, right, bottom));
1395 
1396             // WindowInsets#insetInsets of API 30-35 has a bug, so here uses our own implementation.
1397             final WindowInsetsCompat.Builder builder = new WindowInsetsCompat.Builder(newInsets);
1398             for (int i = Type.FIRST; i <= Type.LAST; i = i << 1) {
1399                 builder.setInsets(i, insetInsets(getInsets(i), left, top, right, bottom));
1400                 if ((i & Type.IME) == 0) {
1401                     builder.setInsetsIgnoringVisibility(
1402                             i,
1403                             insetInsets(getInsetsIgnoringVisibility(i), left, top, right, bottom));
1404                 }
1405             }
1406             return builder.build();
1407         }
1408     }
1409 
1410     @RequiresApi(34)
1411     private static class Impl34 extends Impl30 {
1412         static final @NonNull WindowInsetsCompat CONSUMED =
1413                 toWindowInsetsCompat(WindowInsets.CONSUMED);
1414 
Impl34(@onNull WindowInsetsCompat host, @NonNull WindowInsets insets)1415         Impl34(@NonNull WindowInsetsCompat host, @NonNull WindowInsets insets) {
1416             super(host, insets);
1417         }
1418 
Impl34(@onNull WindowInsetsCompat host, @NonNull Impl34 other)1419         Impl34(@NonNull WindowInsetsCompat host, @NonNull Impl34 other) {
1420             super(host, other);
1421         }
1422 
1423         @Override
getInsets(int typeMask)1424         public @NonNull Insets getInsets(int typeMask) {
1425             return toCompatInsets(
1426                     mPlatformInsets.getInsets(TypeImpl34.toPlatformType(typeMask))
1427             );
1428         }
1429 
1430         @Override
getInsetsIgnoringVisibility(int typeMask)1431         public @NonNull Insets getInsetsIgnoringVisibility(int typeMask) {
1432             return toCompatInsets(
1433                     mPlatformInsets.getInsetsIgnoringVisibility(TypeImpl34.toPlatformType(typeMask))
1434             );
1435         }
1436 
1437         @Override
isVisible(int typeMask)1438         public boolean isVisible(int typeMask) {
1439             return mPlatformInsets.isVisible(TypeImpl34.toPlatformType(typeMask));
1440         }
1441     }
1442 
1443     @RequiresApi(36)
1444     private static class Impl36 extends Impl34 {
1445 
Impl36(@onNull WindowInsetsCompat host, @NonNull WindowInsets insets)1446         Impl36(@NonNull WindowInsetsCompat host, @NonNull WindowInsets insets) {
1447             super(host, insets);
1448         }
1449 
Impl36(@onNull WindowInsetsCompat host, @NonNull Impl36 other)1450         Impl36(@NonNull WindowInsetsCompat host, @NonNull Impl36 other) {
1451             super(host, other);
1452         }
1453 
1454         @Override
inset(int left, int top, int right, int bottom)1455         @NonNull WindowInsetsCompat inset(int left, int top, int right, int bottom) {
1456             return toWindowInsetsCompat(mPlatformInsets.inset(left, top, right, bottom));
1457         }
1458 
1459     }
1460 
1461     /**
1462      * Builder for {@link WindowInsetsCompat}.
1463      */
1464     public static final class Builder {
1465         private final BuilderImpl mImpl;
1466 
1467         /**
1468          * Creates a builder where all insets are initially consumed.
1469          */
Builder()1470         public Builder() {
1471             if (SDK_INT >= 34) {
1472                 mImpl = new BuilderImpl34();
1473             } else if (SDK_INT >= 30) {
1474                 mImpl = new BuilderImpl30();
1475             } else if (SDK_INT >= 29) {
1476                 mImpl = new BuilderImpl29();
1477             } else if (SDK_INT >= 20) {
1478                 mImpl = new BuilderImpl20();
1479             } else {
1480                 mImpl = new BuilderImpl();
1481             }
1482         }
1483 
1484         /**
1485          * Creates a builder where all insets are initialized from {@link WindowInsetsCompat}.
1486          *
1487          * @param insets the instance to initialize from.
1488          */
Builder(@onNull WindowInsetsCompat insets)1489         public Builder(@NonNull WindowInsetsCompat insets) {
1490             if (SDK_INT >= 34) {
1491                 mImpl = new BuilderImpl34(insets);
1492             } else if (SDK_INT >= 30) {
1493                 mImpl = new BuilderImpl30(insets);
1494             } else if (SDK_INT >= 29) {
1495                 mImpl = new BuilderImpl29(insets);
1496             } else if (SDK_INT >= 20) {
1497                 mImpl = new BuilderImpl20(insets);
1498             } else {
1499                 mImpl = new BuilderImpl(insets);
1500             }
1501         }
1502 
1503         /**
1504          * Sets system window insets in pixels.
1505          *
1506          * <p>The system window inset represents the area of a full-screen window that is
1507          * partially or fully obscured by the status bar, navigation bar, IME or other system
1508          * windows.</p>
1509          *
1510          * @return itself
1511          * @see #getSystemWindowInsets()
1512          * @deprecated Use {@link #setInsets(int, Insets)} with {@link Type#systemBars()}.
1513          */
1514         @Deprecated
setSystemWindowInsets(@onNull Insets insets)1515         public @NonNull Builder setSystemWindowInsets(@NonNull Insets insets) {
1516             mImpl.setSystemWindowInsets(insets);
1517             return this;
1518         }
1519 
1520         /**
1521          * Sets system gesture insets in pixels.
1522          *
1523          * <p>The system gesture insets represent the area of a window where system gestures have
1524          * priority and may consume some or all touch input, e.g. due to the a system bar
1525          * occupying it, or it being reserved for touch-only gestures.
1526          *
1527          * <p>The insets passed will only take effect when running on API 29 and above.
1528          *
1529          * @return itself
1530          * @see #getSystemGestureInsets()
1531          * @deprecated Use {@link #setInsets(int, Insets)} with {@link Type#systemGestures()}.
1532          */
1533         @Deprecated
setSystemGestureInsets(@onNull Insets insets)1534         public @NonNull Builder setSystemGestureInsets(@NonNull Insets insets) {
1535             mImpl.setSystemGestureInsets(insets);
1536             return this;
1537         }
1538 
1539         /**
1540          * Sets mandatory system gesture insets in pixels.
1541          *
1542          * <p>The mandatory system gesture insets represent the area of a window where mandatory
1543          * system gestures have priority and may consume some or all touch input, e.g. due to the a
1544          * system bar occupying it, or it being reserved for touch-only gestures.
1545          *
1546          * <p>In contrast to {@link #setSystemGestureInsets regular system gestures},
1547          * <b>mandatory</b> system gestures cannot be overridden by
1548          * {@link ViewCompat#setSystemGestureExclusionRects}.
1549          *
1550          * <p>The insets passed will only take effect when running on API 29 and above.
1551          *
1552          * @return itself
1553          * @see #getMandatorySystemGestureInsets()
1554          * @deprecated Use {@link #setInsets(int, Insets)} with
1555          * {@link Type#mandatorySystemGestures()}.
1556          */
1557         @Deprecated
setMandatorySystemGestureInsets(@onNull Insets insets)1558         public @NonNull Builder setMandatorySystemGestureInsets(@NonNull Insets insets) {
1559             mImpl.setMandatorySystemGestureInsets(insets);
1560             return this;
1561         }
1562 
1563         /**
1564          * Sets tappable element insets in pixels.
1565          *
1566          * <p>The tappable element insets represent how much tappable elements <b>must at least</b>
1567          * be inset to remain both tappable and visually unobstructed by persistent system windows.
1568          *
1569          * <p>The insets passed will only take effect when running on API 29 and above.
1570          *
1571          * @return itself
1572          * @see #getTappableElementInsets()
1573          * @deprecated Use {@link #setInsets(int, Insets)} with {@link Type#tappableElement()}.
1574          */
1575         @Deprecated
setTappableElementInsets(@onNull Insets insets)1576         public @NonNull Builder setTappableElementInsets(@NonNull Insets insets) {
1577             mImpl.setTappableElementInsets(insets);
1578             return this;
1579         }
1580 
1581         /**
1582          * Sets the insets of a specific window type in pixels.
1583          *
1584          * <p>The insets represents the area of a a window that is partially or fully obscured by
1585          * the system windows identified by {@code typeMask}.
1586          * </p>
1587          *
1588          * @param typeMask The bitmask of {@link Type} to set the insets for.
1589          * @param insets   The insets to set.
1590          * @return itself
1591          * @see #getInsets(int)
1592          */
setInsets(@nsetsType int typeMask, @NonNull Insets insets)1593         public @NonNull Builder setInsets(@InsetsType int typeMask, @NonNull Insets insets) {
1594             mImpl.setInsets(typeMask, insets);
1595             return this;
1596         }
1597 
1598         /**
1599          * Sets the insets a specific window type in pixels, while ignoring its visibility state.
1600          *
1601          * <p>The insets represents the area of a a window that that <b>may</b> be partially
1602          * or fully obscured by the system window identified by {@code typeMask}. This value does
1603          * not change based on the visibility state of those elements. For example, if the status
1604          * bar is normally shown, but temporarily hidden, the inset returned here will still
1605          * provide the inset associated with the status bar being shown.</p>
1606          *
1607          * @param typeMask The bitmask of {@link Type} to set the insets for.
1608          * @param insets   The insets to set.
1609          * @return itself
1610          * @throws IllegalArgumentException If {@code typeMask} contains {@link Type#ime()}. Maximum
1611          *                                  insets are not available for this type as the height of
1612          *                                  the IME is dynamic depending on the {@link EditorInfo}
1613          *                                  of the currently focused view, as well as the UI
1614          *                                  state of the IME.
1615          * @see #getInsetsIgnoringVisibility(int)
1616          */
setInsetsIgnoringVisibility(@nsetsType int typeMask, @NonNull Insets insets)1617         public @NonNull Builder setInsetsIgnoringVisibility(@InsetsType int typeMask,
1618                 @NonNull Insets insets) {
1619             mImpl.setInsetsIgnoringVisibility(typeMask, insets);
1620             return this;
1621         }
1622 
1623         /**
1624          * Sets whether windows that can cause insets are currently visible on screen.
1625          *
1626          * @param typeMask The bitmask of {@link Type} to set the visibility for.
1627          * @param visible  Whether to mark the windows as visible or not.
1628          * @return itself
1629          * @see #isVisible(int)
1630          */
setVisible(@nsetsType int typeMask, boolean visible)1631         public @NonNull Builder setVisible(@InsetsType int typeMask, boolean visible) {
1632             mImpl.setVisible(typeMask, visible);
1633             return this;
1634         }
1635 
1636         /**
1637          * Sets the stable insets in pixels.
1638          *
1639          * <p>The stable inset represents the area of a full-screen window that <b>may</b> be
1640          * partially or fully obscured by the system UI elements.  This value does not change
1641          * based on the visibility state of those elements; for example, if the status bar is
1642          * normally shown, but temporarily hidden, the stable inset will still provide the inset
1643          * associated with the status bar being shown.</p>
1644          *
1645          * <p>The insets passed will only take effect when running on API 29 and above.
1646          *
1647          * @return itself
1648          * @see #getStableInsets()
1649          * @deprecated Use {@link #setInsetsIgnoringVisibility(int, Insets)} with
1650          * {@link Type#systemBars()}.
1651          */
1652         @Deprecated
setStableInsets(@onNull Insets insets)1653         public @NonNull Builder setStableInsets(@NonNull Insets insets) {
1654             mImpl.setStableInsets(insets);
1655             return this;
1656         }
1657 
1658         /**
1659          * Sets the display cutout.
1660          *
1661          * <p>The cutout passed will only take effect when running on API 29 and above.
1662          *
1663          * @param displayCutout the display cutout or null if there is none
1664          * @return itself
1665          * @see #getDisplayCutout()
1666          */
setDisplayCutout(@ullable DisplayCutoutCompat displayCutout)1667         public @NonNull Builder setDisplayCutout(@Nullable DisplayCutoutCompat displayCutout) {
1668             mImpl.setDisplayCutout(displayCutout);
1669             return this;
1670         }
1671 
1672         /**
1673          * Builds a {@link WindowInsetsCompat} instance.
1674          *
1675          * @return the {@link WindowInsetsCompat} instance.
1676          */
build()1677         public @NonNull WindowInsetsCompat build() {
1678             return mImpl.build();
1679         }
1680     }
1681 
1682     private static class BuilderImpl {
1683         private final WindowInsetsCompat mInsets;
1684 
1685         Insets[] mInsetsTypeMask;
1686 
BuilderImpl()1687         BuilderImpl() {
1688             this(new WindowInsetsCompat((WindowInsetsCompat) null));
1689         }
1690 
BuilderImpl(@onNull WindowInsetsCompat insets)1691         BuilderImpl(@NonNull WindowInsetsCompat insets) {
1692             mInsets = insets;
1693         }
1694 
setSystemWindowInsets(@onNull Insets insets)1695         void setSystemWindowInsets(@NonNull Insets insets) {}
1696 
setSystemGestureInsets(@onNull Insets insets)1697         void setSystemGestureInsets(@NonNull Insets insets) {}
1698 
setMandatorySystemGestureInsets(@onNull Insets insets)1699         void setMandatorySystemGestureInsets(@NonNull Insets insets) {}
1700 
setTappableElementInsets(@onNull Insets insets)1701         void setTappableElementInsets(@NonNull Insets insets) {}
1702 
setStableInsets(@onNull Insets insets)1703         void setStableInsets(@NonNull Insets insets) {}
1704 
setDisplayCutout(@ullable DisplayCutoutCompat displayCutout)1705         void setDisplayCutout(@Nullable DisplayCutoutCompat displayCutout) {}
1706 
1707         @SuppressWarnings("WrongConstant")
setInsets(int typeMask, @NonNull Insets insets)1708         void setInsets(int typeMask, @NonNull Insets insets) {
1709             if (mInsetsTypeMask == null) {
1710                 mInsetsTypeMask = new Insets[Type.SIZE];
1711             }
1712             for (int i = Type.FIRST; i <= Type.LAST; i = i << 1) {
1713                 if ((typeMask & i) == 0) {
1714                     continue;
1715                 }
1716                 mInsetsTypeMask[Type.indexOf(i)] = insets;
1717             }
1718         }
1719 
setInsetsIgnoringVisibility(int typeMask, @NonNull Insets insets)1720         void setInsetsIgnoringVisibility(int typeMask, @NonNull Insets insets) {
1721             if (typeMask == Type.IME) {
1722                 // Not strictly necessary but means we will throw an exception on all platforms,
1723                 // rather than just R, which is clearer for all users
1724                 throw new IllegalArgumentException("Ignoring visibility inset not available"
1725                         + " for IME");
1726             }
1727         }
1728 
setVisible(int typeMask, boolean visible)1729         void setVisible(int typeMask, boolean visible) {}
1730 
1731         /**
1732          * This method tries to apply any insets set via {@link #setInsets(int, Insets)} to
1733          * the insets builder. This function will be a no-op on API 30 since
1734          * {@link BuilderImpl30#setInsets(int, Insets)} does not update the array.
1735          */
applyInsetTypes()1736         protected final void applyInsetTypes() {
1737             if (mInsetsTypeMask != null) {
1738                 Insets statusBars = mInsetsTypeMask[Type.indexOf(Type.STATUS_BARS)];
1739                 Insets navigationBars = mInsetsTypeMask[Type.indexOf(Type.NAVIGATION_BARS)];
1740 
1741                 // If the insets are not set in the builder, default to the insets passed in
1742                 // the builder parameter to avoid accidentally setting them to 0
1743                 if (navigationBars == null) {
1744                     navigationBars = mInsets.getInsets(Type.NAVIGATION_BARS);
1745                 }
1746                 if (statusBars == null) {
1747                     statusBars = mInsets.getInsets(Type.STATUS_BARS);
1748                 }
1749 
1750                 setSystemWindowInsets(Insets.max(statusBars, navigationBars));
1751 
1752                 Insets i = mInsetsTypeMask[Type.indexOf(Type.SYSTEM_GESTURES)];
1753                 if (i != null) setSystemGestureInsets(i);
1754 
1755                 i = mInsetsTypeMask[Type.indexOf(Type.MANDATORY_SYSTEM_GESTURES)];
1756                 if (i != null) setMandatorySystemGestureInsets(i);
1757 
1758                 i = mInsetsTypeMask[Type.indexOf(Type.TAPPABLE_ELEMENT)];
1759                 if (i != null) setTappableElementInsets(i);
1760             }
1761         }
1762 
build()1763         @NonNull WindowInsetsCompat build() {
1764             applyInsetTypes();
1765             return mInsets;
1766         }
1767     }
1768 
setOverriddenInsets(Insets[] insetsTypeMask)1769     void setOverriddenInsets(Insets[] insetsTypeMask) {
1770         mImpl.setOverriddenInsets(insetsTypeMask);
1771     }
1772 
1773     @RequiresApi(api = 20)
1774     private static class BuilderImpl20 extends BuilderImpl {
1775         private static Field sConsumedField;
1776         private static boolean sConsumedFieldFetched = false;
1777 
1778         private static Constructor<WindowInsets> sConstructor;
1779         private static boolean sConstructorFetched = false;
1780 
1781         private WindowInsets mPlatformInsets;
1782         private Insets mStableInsets;
1783 
BuilderImpl20()1784         BuilderImpl20() {
1785             mPlatformInsets = createWindowInsetsInstance();
1786         }
1787 
BuilderImpl20(@onNull WindowInsetsCompat insets)1788         BuilderImpl20(@NonNull WindowInsetsCompat insets) {
1789             super(insets);
1790             mPlatformInsets = insets.toWindowInsets();
1791         }
1792 
1793         @Override
setSystemWindowInsets(@onNull Insets insets)1794         void setSystemWindowInsets(@NonNull Insets insets) {
1795             if (mPlatformInsets != null) {
1796                 mPlatformInsets = mPlatformInsets.replaceSystemWindowInsets(
1797                         insets.left, insets.top, insets.right, insets.bottom);
1798             }
1799         }
1800 
1801         @Override
setStableInsets(@ullable Insets insets)1802         void setStableInsets(@Nullable Insets insets) {
1803             mStableInsets = insets;
1804         }
1805 
1806         @Override
build()1807         @NonNull WindowInsetsCompat build() {
1808             applyInsetTypes();
1809             WindowInsetsCompat windowInsetsCompat = WindowInsetsCompat.toWindowInsetsCompat(
1810                     mPlatformInsets);
1811             windowInsetsCompat.setOverriddenInsets(this.mInsetsTypeMask);
1812             windowInsetsCompat.setStableInsets(mStableInsets);
1813             return windowInsetsCompat;
1814         }
1815 
1816         @SuppressWarnings("JavaReflectionMemberAccess")
createWindowInsetsInstance()1817         private static @Nullable WindowInsets createWindowInsetsInstance() {
1818             // On API 20-28, there is no public way to create an WindowInsets instance, so we
1819             // need to use reflection.
1820 
1821             // We will first try getting the WindowInsets.CONSUMED static field, and creating a
1822             // copy of it
1823             if (!sConsumedFieldFetched) {
1824                 try {
1825                     sConsumedField = WindowInsets.class.getDeclaredField("CONSUMED");
1826                 } catch (ReflectiveOperationException e) {
1827                     Log.i(TAG, "Could not retrieve WindowInsets.CONSUMED field", e);
1828                 }
1829                 sConsumedFieldFetched = true;
1830             }
1831             if (sConsumedField != null) {
1832                 try {
1833                     WindowInsets consumed = (WindowInsets) sConsumedField.get(null);
1834                     if (consumed != null) {
1835                         return new WindowInsets(consumed);
1836                     }
1837                 } catch (ReflectiveOperationException e) {
1838                     Log.i(TAG, "Could not get value from WindowInsets.CONSUMED field", e);
1839                 }
1840             }
1841 
1842             // If we reached here, the WindowInsets.CONSUMED field did not exist. We can try
1843             // the hidden WindowInsets(Rect) constructor instead
1844             if (!sConstructorFetched) {
1845                 try {
1846                     sConstructor = WindowInsets.class.getConstructor(Rect.class);
1847                 } catch (ReflectiveOperationException e) {
1848                     Log.i(TAG, "Could not retrieve WindowInsets(Rect) constructor", e);
1849                 }
1850                 sConstructorFetched = true;
1851             }
1852             if (sConstructor != null) {
1853                 try {
1854                     return sConstructor.newInstance(new Rect());
1855                 } catch (ReflectiveOperationException e) {
1856                     Log.i(TAG, "Could not invoke WindowInsets(Rect) constructor", e);
1857                 }
1858             }
1859 
1860             // If the reflective calls failed, return null
1861             return null;
1862         }
1863     }
1864 
setStableInsets(@ullable Insets stableInsets)1865     void setStableInsets(@Nullable Insets stableInsets) {
1866         mImpl.setStableInsets(stableInsets);
1867     }
1868 
1869     @RequiresApi(api = 29)
1870     private static class BuilderImpl29 extends BuilderImpl {
1871         final WindowInsets.Builder mPlatBuilder;
1872 
BuilderImpl29()1873         BuilderImpl29() {
1874             super();
1875             mPlatBuilder = new WindowInsets.Builder();
1876         }
1877 
BuilderImpl29(@onNull WindowInsetsCompat insets)1878         BuilderImpl29(@NonNull WindowInsetsCompat insets) {
1879             super(insets);
1880             final WindowInsets platInsets = insets.toWindowInsets();
1881             mPlatBuilder = platInsets != null
1882                     ? new WindowInsets.Builder(platInsets)
1883                     : new WindowInsets.Builder();
1884         }
1885 
1886         @Override
setSystemWindowInsets(@onNull Insets insets)1887         void setSystemWindowInsets(@NonNull Insets insets) {
1888             mPlatBuilder.setSystemWindowInsets(insets.toPlatformInsets());
1889         }
1890 
1891         @Override
setSystemGestureInsets(@onNull Insets insets)1892         void setSystemGestureInsets(@NonNull Insets insets) {
1893             mPlatBuilder.setSystemGestureInsets(insets.toPlatformInsets());
1894         }
1895 
1896         @Override
setMandatorySystemGestureInsets(@onNull Insets insets)1897         void setMandatorySystemGestureInsets(@NonNull Insets insets) {
1898             mPlatBuilder.setMandatorySystemGestureInsets(insets.toPlatformInsets());
1899         }
1900 
1901         @Override
setTappableElementInsets(@onNull Insets insets)1902         void setTappableElementInsets(@NonNull Insets insets) {
1903             mPlatBuilder.setTappableElementInsets(insets.toPlatformInsets());
1904         }
1905 
1906         @Override
setStableInsets(@onNull Insets insets)1907         void setStableInsets(@NonNull Insets insets) {
1908             mPlatBuilder.setStableInsets(insets.toPlatformInsets());
1909         }
1910 
1911         @Override
setDisplayCutout(@ullable DisplayCutoutCompat displayCutout)1912         void setDisplayCutout(@Nullable DisplayCutoutCompat displayCutout) {
1913             mPlatBuilder.setDisplayCutout(displayCutout != null ? displayCutout.unwrap() : null);
1914         }
1915 
1916         @Override
build()1917         @NonNull WindowInsetsCompat build() {
1918             applyInsetTypes();
1919             WindowInsetsCompat windowInsetsCompat = WindowInsetsCompat.toWindowInsetsCompat(
1920                     mPlatBuilder.build());
1921             windowInsetsCompat.setOverriddenInsets(mInsetsTypeMask);
1922             return windowInsetsCompat;
1923         }
1924     }
1925 
1926     @RequiresApi(30)
1927     private static class BuilderImpl30 extends BuilderImpl29 {
BuilderImpl30()1928         BuilderImpl30() {
1929             super();
1930         }
1931 
BuilderImpl30(@onNull WindowInsetsCompat insets)1932         BuilderImpl30(@NonNull WindowInsetsCompat insets) {
1933             super(insets);
1934         }
1935 
1936         @Override
setInsets(int typeMask, @NonNull Insets insets)1937         void setInsets(int typeMask, @NonNull Insets insets) {
1938             mPlatBuilder.setInsets(
1939                     TypeImpl30.toPlatformType(typeMask),
1940                     insets.toPlatformInsets()
1941             );
1942         }
1943 
1944         @Override
setInsetsIgnoringVisibility(int typeMask, @NonNull Insets insets)1945         void setInsetsIgnoringVisibility(int typeMask, @NonNull Insets insets) {
1946             mPlatBuilder.setInsetsIgnoringVisibility(
1947                     TypeImpl30.toPlatformType(typeMask),
1948                     insets.toPlatformInsets()
1949             );
1950         }
1951 
1952         @Override
setVisible(int typeMask, boolean visible)1953         void setVisible(int typeMask, boolean visible) {
1954             mPlatBuilder.setVisible(TypeImpl30.toPlatformType(typeMask), visible);
1955         }
1956     }
1957 
1958     @RequiresApi(34)
1959     private static class BuilderImpl34 extends BuilderImpl30 {
BuilderImpl34()1960         BuilderImpl34() {
1961             super();
1962         }
1963 
BuilderImpl34(@onNull WindowInsetsCompat insets)1964         BuilderImpl34(@NonNull WindowInsetsCompat insets) {
1965             super(insets);
1966         }
1967 
1968         @Override
setInsets(int typeMask, @NonNull Insets insets)1969         void setInsets(int typeMask, @NonNull Insets insets) {
1970             mPlatBuilder.setInsets(
1971                     TypeImpl34.toPlatformType(typeMask),
1972                     insets.toPlatformInsets()
1973             );
1974         }
1975 
1976         @Override
setInsetsIgnoringVisibility(int typeMask, @NonNull Insets insets)1977         void setInsetsIgnoringVisibility(int typeMask, @NonNull Insets insets) {
1978             mPlatBuilder.setInsetsIgnoringVisibility(
1979                     TypeImpl34.toPlatformType(typeMask),
1980                     insets.toPlatformInsets()
1981             );
1982         }
1983 
1984         @Override
setVisible(int typeMask, boolean visible)1985         void setVisible(int typeMask, boolean visible) {
1986             mPlatBuilder.setVisible(TypeImpl34.toPlatformType(typeMask), visible);
1987         }
1988     }
1989 
1990     /**
1991      * Class that defines different types of sources causing window insets.
1992      */
1993     public static final class Type {
1994         static final int FIRST = 1;
1995         static final int STATUS_BARS = FIRST;
1996         static final int NAVIGATION_BARS = 1 << 1;
1997         static final int CAPTION_BAR = 1 << 2;
1998 
1999         static final int IME = 1 << 3;
2000 
2001         static final int SYSTEM_GESTURES = 1 << 4;
2002         static final int MANDATORY_SYSTEM_GESTURES = 1 << 5;
2003         static final int TAPPABLE_ELEMENT = 1 << 6;
2004 
2005         static final int DISPLAY_CUTOUT = 1 << 7;
2006 
2007         static final int WINDOW_DECOR = 1 << 8;
2008         static final int SYSTEM_OVERLAYS = 1 << 9;
2009         static final int LAST = SYSTEM_OVERLAYS;
2010         static final int SIZE = 10;
2011 
Type()2012         private Type() {}
2013 
2014         /**
2015          * @return An insets type representing any system bars for displaying status.
2016          */
2017         @InsetsType
statusBars()2018         public static int statusBars() {
2019             return STATUS_BARS;
2020         }
2021 
2022         /**
2023          * @return An insets type representing any system bars for navigation.
2024          */
2025         @InsetsType
navigationBars()2026         public static int navigationBars() {
2027             return NAVIGATION_BARS;
2028         }
2029 
2030         /**
2031          * @return An insets type representing the window of a caption bar.
2032          */
2033         @InsetsType
captionBar()2034         public static int captionBar() {
2035             return CAPTION_BAR;
2036         }
2037 
2038         /**
2039          * @return An insets type representing the window of an {@link InputMethod}.
2040          */
2041         @InsetsType
ime()2042         public static int ime() {
2043             return IME;
2044         }
2045 
2046         /**
2047          * Returns an insets type representing the system gesture insets.
2048          *
2049          * <p>The system gesture insets represent the area of a window where system gestures have
2050          * priority and may consume some or all touch input, e.g. due to the a system bar
2051          * occupying it, or it being reserved for touch-only gestures.
2052          *
2053          * <p>Simple taps are guaranteed to reach the window even within the system gesture insets,
2054          * as long as they are outside the {@link #getSystemWindowInsets() system window insets}.
2055          *
2056          * <p>When {@link View#SYSTEM_UI_FLAG_LAYOUT_STABLE} is requested, an inset will be returned
2057          * even when the system gestures are inactive due to
2058          * {@link View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN} or
2059          * {@link View#SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION}.
2060          *
2061          * @see #getSystemGestureInsets()
2062          */
2063         @InsetsType
systemGestures()2064         public static int systemGestures() {
2065             return SYSTEM_GESTURES;
2066         }
2067 
2068         /**
2069          * @see #getMandatorySystemGestureInsets
2070          */
2071         @InsetsType
mandatorySystemGestures()2072         public static int mandatorySystemGestures() {
2073             return MANDATORY_SYSTEM_GESTURES;
2074         }
2075 
2076         /**
2077          * @see #getTappableElementInsets
2078          */
2079         @InsetsType
tappableElement()2080         public static int tappableElement() {
2081             return TAPPABLE_ELEMENT;
2082         }
2083 
2084         /**
2085          * Returns an insets type representing the area that used by {@link DisplayCutoutCompat}.
2086          *
2087          * <p>This is equivalent to the safe insets on {@link #getDisplayCutout()}.</p>
2088          *
2089          * @see DisplayCutoutCompat#getSafeInsetLeft()
2090          * @see DisplayCutoutCompat#getSafeInsetTop()
2091          * @see DisplayCutoutCompat#getSafeInsetRight()
2092          * @see DisplayCutoutCompat#getSafeInsetBottom()
2093          */
2094         @InsetsType
displayCutout()2095         public static int displayCutout() {
2096             return DISPLAY_CUTOUT;
2097         }
2098 
2099         /**
2100          * System overlays represent the insets caused by the system visible elements. Unlike
2101          * {@link #navigationBars()} or {@link #statusBars()}, system overlays might not be
2102          * hidden by the client.
2103          *
2104          * For compatibility reasons, this type is included in {@link #systemBars()}. In this
2105          * way, views which fit {@link #systemBars()} fit {@link #systemOverlays()}.
2106          *
2107          * Examples include climate controls, multi-tasking affordances, etc.
2108          *
2109          * @return An insets type representing the system overlays.
2110          */
2111         @InsetsType
systemOverlays()2112         public static int systemOverlays() {
2113             return SYSTEM_OVERLAYS;
2114         }
2115 
2116         /**
2117          * @return All system bars. Includes {@link #statusBars()}, {@link #captionBar()} as well as
2118          * {@link #navigationBars()}, {@link #systemOverlays()} but not {@link #ime()}.
2119          */
2120         @InsetsType
systemBars()2121         public static int systemBars() {
2122             return STATUS_BARS | NAVIGATION_BARS | CAPTION_BAR | SYSTEM_OVERLAYS;
2123         }
2124 
2125         /**
2126          * @return All inset types combined.
2127          */
2128         @InsetsType
2129         @RestrictTo(LIBRARY_GROUP)
2130         @SuppressLint("WrongConstant")
all()2131         static int all() {
2132             return 0xFFFFFFFF;
2133         }
2134 
indexOf(@nsetsType int type)2135         static int indexOf(@InsetsType int type) {
2136             switch (type) {
2137                 case STATUS_BARS:
2138                     return 0;
2139                 case NAVIGATION_BARS:
2140                     return 1;
2141                 case CAPTION_BAR:
2142                     return 2;
2143                 case IME:
2144                     return 3;
2145                 case SYSTEM_GESTURES:
2146                     return 4;
2147                 case MANDATORY_SYSTEM_GESTURES:
2148                     return 5;
2149                 case TAPPABLE_ELEMENT:
2150                     return 6;
2151                 case DISPLAY_CUTOUT:
2152                     return 7;
2153                 case WINDOW_DECOR:
2154                     return 8;
2155                 case SYSTEM_OVERLAYS:
2156                     return 9;
2157                 default:
2158                     throw new IllegalArgumentException("type needs to be >= FIRST and <= LAST,"
2159                             + " type=" + type);
2160             }
2161         }
2162 
2163         @RestrictTo(LIBRARY_GROUP)
2164         @Retention(RetentionPolicy.SOURCE)
2165         @IntDef(flag = true, value = {STATUS_BARS, NAVIGATION_BARS, CAPTION_BAR, IME, WINDOW_DECOR,
2166                 SYSTEM_GESTURES, MANDATORY_SYSTEM_GESTURES, TAPPABLE_ELEMENT, DISPLAY_CUTOUT,
2167                 SYSTEM_OVERLAYS})
2168         public @interface InsetsType {
2169         }
2170     }
2171 
2172     /**
2173      * Class that defines different sides for insets.
2174      */
2175     public static final class Side {
2176         public static final int LEFT = 1 << 0;
2177         public static final int TOP = 1 << 1;
2178         public static final int RIGHT = 1 << 2;
2179         public static final int BOTTOM = 1 << 3;
2180 
Side()2181         private Side() {
2182         }
2183 
2184         @RestrictTo(LIBRARY_GROUP)
2185         @Retention(RetentionPolicy.SOURCE)
2186         @IntDef(flag = true, value = {LEFT, TOP, RIGHT, BOTTOM})
2187         public @interface InsetsSide {
2188         }
2189 
2190         /**
2191          * @return all four sides.
2192          */
all()2193         public static @InsetsSide int all() {
2194             return LEFT | TOP | RIGHT | BOTTOM;
2195         }
2196     }
2197 
2198     @RequiresApi(30)
2199     private static final class TypeImpl30 {
TypeImpl30()2200         private TypeImpl30() {}
2201 
2202         /**
2203          * Maps from our internal type mask constants to the platform's. Ideally we will keep the
2204          * constant values in sync, but this allows the platform to return different constants in
2205          * the future without breaking the logic in this class.
2206          */
toPlatformType(@nsetsType final int typeMask)2207         static int toPlatformType(@InsetsType final int typeMask) {
2208             int result = 0;
2209             for (int i = Type.FIRST; i <= Type.LAST; i = i << 1) {
2210                 if ((typeMask & i) != 0) {
2211                     switch (i) {
2212                         case Type.STATUS_BARS:
2213                             result |= WindowInsets.Type.statusBars();
2214                             break;
2215                         case Type.NAVIGATION_BARS:
2216                             result |= WindowInsets.Type.navigationBars();
2217                             break;
2218                         case Type.CAPTION_BAR:
2219                             result |= WindowInsets.Type.captionBar();
2220                             break;
2221                         case Type.IME:
2222                             result |= WindowInsets.Type.ime();
2223                             break;
2224                         case Type.SYSTEM_GESTURES:
2225                             result |= WindowInsets.Type.systemGestures();
2226                             break;
2227                         case Type.MANDATORY_SYSTEM_GESTURES:
2228                             result |= WindowInsets.Type.mandatorySystemGestures();
2229                             break;
2230                         case Type.TAPPABLE_ELEMENT:
2231                             result |= WindowInsets.Type.tappableElement();
2232                             break;
2233                         case Type.DISPLAY_CUTOUT:
2234                             result |= WindowInsets.Type.displayCutout();
2235                             break;
2236                     }
2237                 }
2238             }
2239             return result;
2240         }
2241     }
2242 
2243     @RequiresApi(34)
2244     private static final class TypeImpl34 {
TypeImpl34()2245         private TypeImpl34() {}
2246 
2247         /**
2248          * Maps from our internal type mask constants to the platform's. Ideally we will keep the
2249          * constant values in sync, but this allows the platform to return different constants in
2250          * the future without breaking the logic in this class.
2251          */
toPlatformType(@nsetsType final int typeMask)2252         static int toPlatformType(@InsetsType final int typeMask) {
2253             int result = 0;
2254             for (int i = Type.FIRST; i <= Type.LAST; i = i << 1) {
2255                 if ((typeMask & i) != 0) {
2256                     switch (i) {
2257                         case Type.STATUS_BARS:
2258                             result |= WindowInsets.Type.statusBars();
2259                             break;
2260                         case Type.NAVIGATION_BARS:
2261                             result |= WindowInsets.Type.navigationBars();
2262                             break;
2263                         case Type.CAPTION_BAR:
2264                             result |= WindowInsets.Type.captionBar();
2265                             break;
2266                         case Type.IME:
2267                             result |= WindowInsets.Type.ime();
2268                             break;
2269                         case Type.SYSTEM_GESTURES:
2270                             result |= WindowInsets.Type.systemGestures();
2271                             break;
2272                         case Type.MANDATORY_SYSTEM_GESTURES:
2273                             result |= WindowInsets.Type.mandatorySystemGestures();
2274                             break;
2275                         case Type.TAPPABLE_ELEMENT:
2276                             result |= WindowInsets.Type.tappableElement();
2277                             break;
2278                         case Type.DISPLAY_CUTOUT:
2279                             result |= WindowInsets.Type.displayCutout();
2280                             break;
2281                         case Type.SYSTEM_OVERLAYS:
2282                             result |= WindowInsets.Type.systemOverlays();
2283                             break;
2284                     }
2285                 }
2286             }
2287             return result;
2288         }
2289     }
2290 
setRootWindowInsets(@ullable WindowInsetsCompat rootWindowInsets)2291     void setRootWindowInsets(@Nullable WindowInsetsCompat rootWindowInsets) {
2292         mImpl.setRootWindowInsets(rootWindowInsets);
2293     }
2294 
setRootViewData(@onNull Insets visibleInsets)2295     void setRootViewData(@NonNull Insets visibleInsets) {
2296         mImpl.setRootViewData(visibleInsets);
2297     }
2298 
copyRootViewBounds(@onNull View rootView)2299     void copyRootViewBounds(@NonNull View rootView) {
2300         mImpl.copyRootViewBounds(rootView);
2301     }
2302 
setSystemUiVisibility(int systemUiVisibility)2303     void setSystemUiVisibility(int systemUiVisibility) {
2304         mImpl.setSystemUiVisibility(systemUiVisibility);
2305     }
2306 
2307     @SuppressWarnings("JavaReflectionMemberAccess") // Reflection on private field
2308     @SuppressLint("SoonBlockedPrivateApi") // mAttachInfo is only accessed on SDK 21 and 22
2309     @RequiresApi(21)
2310     static class Api21ReflectionHolder {
2311 
Api21ReflectionHolder()2312         private Api21ReflectionHolder() {
2313             // This class is not instantiable.
2314         }
2315 
2316         private static Field sViewAttachInfoField; // Only accessed on SDK 21 and 222
2317         private static Field sStableInsets;
2318         private static Field sContentInsets;
2319         private static boolean sReflectionSucceeded;
2320 
2321         static {
2322             try {
2323                 sViewAttachInfoField = View.class.getDeclaredField("mAttachInfo");
2324                 sViewAttachInfoField.setAccessible(true);
2325                 Class<?> sAttachInfoClass = Class.forName("android.view.View$AttachInfo");
2326                 sStableInsets = sAttachInfoClass.getDeclaredField("mStableInsets");
2327                 sStableInsets.setAccessible(true);
2328                 sContentInsets = sAttachInfoClass.getDeclaredField("mContentInsets");
2329                 sContentInsets.setAccessible(true);
2330                 sReflectionSucceeded = true;
2331             } catch (ReflectiveOperationException e) {
2332                 Log.w(TAG, "Failed to get visible insets from AttachInfo " + e.getMessage(), e);
2333             }
2334         }
2335 
2336         // Only called on SDK 21 and 22
2337         @SuppressWarnings("deprecation")
getRootWindowInsets(@onNull View v)2338         public static @Nullable WindowInsetsCompat getRootWindowInsets(@NonNull View v) {
2339             if (!sReflectionSucceeded || !v.isAttachedToWindow()) {
2340                 return null;
2341             }
2342 
2343             View rootView = v.getRootView();
2344             try {
2345                 Object attachInfo = sViewAttachInfoField.get(rootView);
2346                 if (attachInfo != null) {
2347                     Rect stableInsets = (Rect) sStableInsets.get(attachInfo);
2348                     Rect visibleInsets = (Rect) sContentInsets.get(attachInfo);
2349                     if (stableInsets != null && visibleInsets != null) {
2350                         WindowInsetsCompat insets = new Builder()
2351                                 .setStableInsets(Insets.of(stableInsets))
2352                                 .setSystemWindowInsets(Insets.of(visibleInsets))
2353                                 .build();
2354 
2355                         // The WindowInsetsCompat instance still needs to know about
2356                         // what the root window insets, and the root view visible bounds are
2357                         insets.setRootWindowInsets(insets);
2358                         insets.copyRootViewBounds(v.getRootView());
2359                         return insets;
2360                     }
2361                 }
2362             } catch (IllegalAccessException e) {
2363                 Log.w(TAG, "Failed to get insets from AttachInfo. " + e.getMessage(), e);
2364             }
2365             return null;
2366         }
2367     }
2368 }
2369