1 /*
2  * Copyright (C) 2014 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 
18 package android.view;
19 
20 import static android.view.WindowInsets.Type.DISPLAY_CUTOUT;
21 import static android.view.WindowInsets.Type.FIRST;
22 import static android.view.WindowInsets.Type.IME;
23 import static android.view.WindowInsets.Type.LAST;
24 import static android.view.WindowInsets.Type.MANDATORY_SYSTEM_GESTURES;
25 import static android.view.WindowInsets.Type.NAVIGATION_BARS;
26 import static android.view.WindowInsets.Type.SIZE;
27 import static android.view.WindowInsets.Type.STATUS_BARS;
28 import static android.view.WindowInsets.Type.SYSTEM_GESTURES;
29 import static android.view.WindowInsets.Type.TAPPABLE_ELEMENT;
30 import static android.view.WindowInsets.Type.all;
31 import static android.view.WindowInsets.Type.ime;
32 import static android.view.WindowInsets.Type.indexOf;
33 import static android.view.WindowInsets.Type.systemBars;
34 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
35 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
36 
37 import android.annotation.IntDef;
38 import android.annotation.IntRange;
39 import android.annotation.NonNull;
40 import android.annotation.Nullable;
41 import android.compat.annotation.UnsupportedAppUsage;
42 import android.content.Intent;
43 import android.graphics.Insets;
44 import android.graphics.Rect;
45 import android.util.SparseArray;
46 import android.view.View.OnApplyWindowInsetsListener;
47 import android.view.WindowInsets.Type.InsetsType;
48 import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
49 import android.view.inputmethod.EditorInfo;
50 import android.view.inputmethod.InputMethod;
51 
52 import com.android.internal.annotations.VisibleForTesting;
53 import com.android.internal.util.Preconditions;
54 
55 import java.lang.annotation.Retention;
56 import java.lang.annotation.RetentionPolicy;
57 import java.util.Arrays;
58 import java.util.Objects;
59 
60 /**
61  * Describes a set of insets for window content.
62  *
63  * <p>WindowInsets are immutable and may be expanded to include more inset types in the future.
64  * To adjust insets, use one of the supplied clone methods to obtain a new WindowInsets instance
65  * with the adjusted properties.</p>
66  *
67  * <p>Note: Before {@link android.os.Build.VERSION_CODES#P P}, WindowInsets instances were only
68  * immutable during a single layout pass (i.e. would return the same values between
69  * {@link View#onApplyWindowInsets} and {@link View#onLayout}, but could return other values
70  * otherwise). Starting with {@link android.os.Build.VERSION_CODES#P P}, WindowInsets are
71  * always immutable and implement equality.
72  *
73  * @see View.OnApplyWindowInsetsListener
74  * @see View#onApplyWindowInsets(WindowInsets)
75  */
76 public final class WindowInsets {
77 
78     private final Insets[] mTypeInsetsMap;
79     private final Insets[] mTypeMaxInsetsMap;
80     private final boolean[] mTypeVisibilityMap;
81 
82     @Nullable private Rect mTempRect;
83     private final boolean mIsRound;
84     @Nullable private final DisplayCutout mDisplayCutout;
85 
86     /**
87      * In multi-window we force show the navigation bar. Because we don't want that the surface size
88      * changes in this mode, we instead have a flag whether the navigation bar size should always
89      * be consumed, so the app is treated like there is no virtual navigation bar at all.
90      */
91     private final boolean mAlwaysConsumeSystemBars;
92 
93     private final boolean mSystemWindowInsetsConsumed;
94     private final boolean mStableInsetsConsumed;
95     private final boolean mDisplayCutoutConsumed;
96 
97     private final int mCompatInsetsTypes;
98     private final boolean mCompatIgnoreVisibility;
99 
100     /**
101      * A {@link WindowInsets} instance for which {@link #isConsumed()} returns {@code true}.
102      * <p>
103      * This can be used during insets dispatch in the view hierarchy by returning this value from
104      * {@link View#onApplyWindowInsets(WindowInsets)} or
105      * {@link OnApplyWindowInsetsListener#onApplyWindowInsets(View, WindowInsets)} to stop dispatch
106      * the insets to its children to avoid traversing the entire view hierarchy.
107      * <p>
108      * The application should return this instance once it has taken care of all insets on a certain
109      * level in the view hierarchy, and doesn't need to dispatch to its children anymore for better
110      * performance.
111      *
112      * @see #isConsumed()
113      */
114     public static final @NonNull WindowInsets CONSUMED;
115 
116     static {
117         CONSUMED = new WindowInsets((Rect) null, null, false, false, null);
118     }
119 
120     /**
121      * Construct a new WindowInsets from individual insets.
122      *
123      * A {@code null} inset indicates that the respective inset is consumed.
124      *
125      * @hide
126      * @deprecated Use {@link WindowInsets(SparseArray, SparseArray, boolean, boolean, DisplayCutout)}
127      */
WindowInsets(Rect systemWindowInsetsRect, Rect stableInsetsRect, boolean isRound, boolean alwaysConsumeSystemBars, DisplayCutout displayCutout)128     public WindowInsets(Rect systemWindowInsetsRect, Rect stableInsetsRect,
129             boolean isRound, boolean alwaysConsumeSystemBars, DisplayCutout displayCutout) {
130         this(createCompatTypeMap(systemWindowInsetsRect), createCompatTypeMap(stableInsetsRect),
131                 createCompatVisibilityMap(createCompatTypeMap(systemWindowInsetsRect)),
132                 isRound, alwaysConsumeSystemBars, displayCutout, systemBars(),
133                 false /* compatIgnoreVisibility */);
134     }
135 
136     /**
137      * Construct a new WindowInsets from individual insets.
138      *
139      * {@code typeInsetsMap} and {@code typeMaxInsetsMap} are a map of indexOf(type) -> insets that
140      * contain the information what kind of system bars causes how much insets. The insets in this
141      * map are non-additive; i.e. they have the same origin. In other words: If two system bars
142      * overlap on one side, the insets of the larger bar will also include the insets of the smaller
143      * bar.
144      *
145      * {@code null} type inset map indicates that the respective inset is fully consumed.
146      * @hide
147      */
WindowInsets(@ullable Insets[] typeInsetsMap, @Nullable Insets[] typeMaxInsetsMap, boolean[] typeVisibilityMap, boolean isRound, boolean alwaysConsumeSystemBars, DisplayCutout displayCutout, @InsetsType int compatInsetsTypes, boolean compatIgnoreVisibility)148     public WindowInsets(@Nullable Insets[] typeInsetsMap,
149             @Nullable Insets[] typeMaxInsetsMap,
150             boolean[] typeVisibilityMap,
151             boolean isRound,
152             boolean alwaysConsumeSystemBars, DisplayCutout displayCutout,
153             @InsetsType int compatInsetsTypes, boolean compatIgnoreVisibility) {
154         mSystemWindowInsetsConsumed = typeInsetsMap == null;
155         mTypeInsetsMap = mSystemWindowInsetsConsumed
156                 ? new Insets[SIZE]
157                 : typeInsetsMap.clone();
158 
159         mStableInsetsConsumed = typeMaxInsetsMap == null;
160         mTypeMaxInsetsMap = mStableInsetsConsumed
161                 ? new Insets[SIZE]
162                 : typeMaxInsetsMap.clone();
163 
164         mTypeVisibilityMap = typeVisibilityMap;
165         mIsRound = isRound;
166         mAlwaysConsumeSystemBars = alwaysConsumeSystemBars;
167         mCompatInsetsTypes = compatInsetsTypes;
168         mCompatIgnoreVisibility = compatIgnoreVisibility;
169 
170         mDisplayCutoutConsumed = displayCutout == null;
171         mDisplayCutout = (mDisplayCutoutConsumed || displayCutout.isEmpty())
172                 ? null : displayCutout;
173     }
174 
175     /**
176      * Construct a new WindowInsets, copying all values from a source WindowInsets.
177      *
178      * @param src Source to copy insets from
179      */
WindowInsets(WindowInsets src)180     public WindowInsets(WindowInsets src) {
181         this(src.mSystemWindowInsetsConsumed ? null : src.mTypeInsetsMap,
182                 src.mStableInsetsConsumed ? null : src.mTypeMaxInsetsMap,
183                 src.mTypeVisibilityMap, src.mIsRound,
184                 src.mAlwaysConsumeSystemBars, displayCutoutCopyConstructorArgument(src),
185                 src.mCompatInsetsTypes,
186                 src.mCompatIgnoreVisibility);
187     }
188 
displayCutoutCopyConstructorArgument(WindowInsets w)189     private static DisplayCutout displayCutoutCopyConstructorArgument(WindowInsets w) {
190         if (w.mDisplayCutoutConsumed) {
191             return null;
192         } else if (w.mDisplayCutout == null) {
193             return DisplayCutout.NO_CUTOUT;
194         } else {
195             return w.mDisplayCutout;
196         }
197     }
198 
199     /**
200      * @return The insets that include system bars indicated by {@code typeMask}, taken from
201      *         {@code typeInsetsMap}.
202      */
getInsets(Insets[] typeInsetsMap, @InsetsType int typeMask)203     static Insets getInsets(Insets[] typeInsetsMap, @InsetsType int typeMask) {
204         Insets result = null;
205         for (int i = FIRST; i <= LAST; i = i << 1) {
206             if ((typeMask & i) == 0) {
207                 continue;
208             }
209             Insets insets = typeInsetsMap[indexOf(i)];
210             if (insets == null) {
211                 continue;
212             }
213             if (result == null) {
214                 result = insets;
215             } else {
216                 result = Insets.max(result, insets);
217             }
218         }
219         return result == null ? Insets.NONE : result;
220     }
221 
222     /**
223      * Sets all entries in {@code typeInsetsMap} that belong to {@code typeMask} to {@code insets},
224      */
setInsets(Insets[] typeInsetsMap, @InsetsType int typeMask, Insets insets)225     private static void setInsets(Insets[] typeInsetsMap, @InsetsType int typeMask, Insets insets) {
226         for (int i = FIRST; i <= LAST; i = i << 1) {
227             if ((typeMask & i) == 0) {
228                 continue;
229             }
230             typeInsetsMap[indexOf(i)] = insets;
231         }
232     }
233 
234     /** @hide */
235     @UnsupportedAppUsage
WindowInsets(Rect systemWindowInsets)236     public WindowInsets(Rect systemWindowInsets) {
237         this(createCompatTypeMap(systemWindowInsets), null, new boolean[SIZE], false, false, null,
238                 systemBars(), false /* compatIgnoreVisibility */);
239     }
240 
241     /**
242      * Creates a indexOf(type) -> inset map for which the {@code insets} is just mapped to
243      * {@link InsetsType#statusBars()} and {@link InsetsType#navigationBars()}, depending on the
244      * location of the inset.
245      */
createCompatTypeMap(@ullable Rect insets)246     private static Insets[] createCompatTypeMap(@Nullable Rect insets) {
247         if (insets == null) {
248             return null;
249         }
250         Insets[] typeInsetsMap = new Insets[SIZE];
251         assignCompatInsets(typeInsetsMap, insets);
252         return typeInsetsMap;
253     }
254 
255     /**
256      * @hide
257      */
258     @VisibleForTesting
assignCompatInsets(Insets[] typeInsetsMap, Rect insets)259     public static void assignCompatInsets(Insets[] typeInsetsMap, Rect insets) {
260         typeInsetsMap[indexOf(STATUS_BARS)] = Insets.of(0, insets.top, 0, 0);
261         typeInsetsMap[indexOf(NAVIGATION_BARS)] =
262                 Insets.of(insets.left, 0, insets.right, insets.bottom);
263     }
264 
createCompatVisibilityMap(@ullable Insets[] typeInsetsMap)265     private static boolean[] createCompatVisibilityMap(@Nullable Insets[] typeInsetsMap) {
266         boolean[] typeVisibilityMap = new boolean[SIZE];
267         if (typeInsetsMap == null) {
268             return typeVisibilityMap;
269         }
270         for (int i = FIRST; i <= LAST; i = i << 1) {
271             int index = indexOf(i);
272             if (!Insets.NONE.equals(typeInsetsMap[index])) {
273                 typeVisibilityMap[index] = true;
274             }
275         }
276         return typeVisibilityMap;
277     }
278 
279     /**
280      * Used to provide a safe copy of the system window insets to pass through
281      * to the existing fitSystemWindows method and other similar internals.
282      * @hide
283      *
284      * @deprecated use {@link #getSystemWindowInsets()} instead.
285      */
286     @Deprecated
287     @NonNull
getSystemWindowInsetsAsRect()288     public Rect getSystemWindowInsetsAsRect() {
289         if (mTempRect == null) {
290             mTempRect = new Rect();
291         }
292         Insets insets = getSystemWindowInsets();
293         mTempRect.set(insets.left, insets.top, insets.right, insets.bottom);
294         return mTempRect;
295     }
296 
297     /**
298      * Returns the system window insets in pixels.
299      *
300      * <p>The system window inset represents the area of a full-screen window that is
301      * partially or fully obscured by the status bar, navigation bar, IME or other system windows.
302      * </p>
303      *
304      * @return The system window insets
305      * @deprecated Use {@link #getInsets(int)} with {@link Type#systemBars()}
306      * instead.
307      */
308     @Deprecated
309     @NonNull
getSystemWindowInsets()310     public Insets getSystemWindowInsets() {
311         Insets result = mCompatIgnoreVisibility
312                 ? getInsetsIgnoringVisibility(mCompatInsetsTypes & ~ime())
313                 : getInsets(mCompatInsetsTypes);
314 
315         // We can't query max insets for IME, so we need to add it manually after.
316         if ((mCompatInsetsTypes & ime()) != 0 && mCompatIgnoreVisibility) {
317             result = Insets.max(result, getInsets(ime()));
318         }
319         return result;
320     }
321 
322     /**
323      * Returns the insets of a specific set of windows causing insets, denoted by the
324      * {@code typeMask} bit mask of {@link InsetsType}s.
325      *
326      * @param typeMask Bit mask of {@link InsetsType}s to query the insets for.
327      * @return The insets.
328      */
329     @NonNull
getInsets(@nsetsType int typeMask)330     public Insets getInsets(@InsetsType int typeMask) {
331         return getInsets(mTypeInsetsMap, typeMask);
332     }
333 
334     /**
335      * Returns the insets a specific set of windows can cause, denoted by the
336      * {@code typeMask} bit mask of {@link InsetsType}s, regardless of whether that type is
337      * currently visible or not.
338      *
339      * <p>The insets represents the area of a a window that that <b>may</b> be partially
340      * or fully obscured by the system window identified by {@code type}. This value does not
341      * change based on the visibility state of those elements. For example, if the status bar is
342      * normally shown, but temporarily hidden, the inset returned here will still provide the inset
343      * associated with the status bar being shown.</p>
344      *
345      * @param typeMask Bit mask of {@link InsetsType}s to query the insets for.
346      * @return The insets.
347      *
348      * @throws IllegalArgumentException If the caller tries to query {@link Type#ime()}. Insets are
349      *                                  not available if the IME isn't visible as the height of the
350      *                                  IME is dynamic depending on the {@link EditorInfo} of the
351      *                                  currently focused view, as well as the UI state of the IME.
352      */
353     @NonNull
getInsetsIgnoringVisibility(@nsetsType int typeMask)354     public Insets getInsetsIgnoringVisibility(@InsetsType int typeMask) {
355         if ((typeMask & IME) != 0) {
356             throw new IllegalArgumentException("Unable to query the maximum insets for IME");
357         }
358         return getInsets(mTypeMaxInsetsMap, typeMask);
359     }
360 
361     /**
362      * Returns whether a set of windows that may cause insets is currently visible on screen,
363      * regardless of whether it actually overlaps with this window.
364      *
365      * @param typeMask Bit mask of {@link Type.InsetsType}s to query visibility status.
366      * @return {@code true} if and only if all windows included in {@code typeMask} are currently
367      *         visible on screen.
368      */
isVisible(@nsetsType int typeMask)369     public boolean isVisible(@InsetsType int typeMask) {
370         for (int i = FIRST; i <= LAST; i = i << 1) {
371             if ((typeMask & i) == 0) {
372                 continue;
373             }
374             if (!mTypeVisibilityMap[indexOf(i)]) {
375                 return false;
376             }
377         }
378         return true;
379     }
380 
381     /**
382      * Returns the left system window inset in pixels.
383      *
384      * <p>The system window inset represents the area of a full-screen window that is
385      * partially or fully obscured by the status bar, navigation bar, IME or other system windows.
386      * </p>
387      *
388      * @return The left system window inset
389      * @deprecated Use {@link #getInsets(int)} with {@link Type#systemBars()}
390      * instead.
391      */
392     @Deprecated
getSystemWindowInsetLeft()393     public int getSystemWindowInsetLeft() {
394         return getSystemWindowInsets().left;
395     }
396 
397     /**
398      * Returns the top system window inset in pixels.
399      *
400      * <p>The system window inset represents the area of a full-screen window that is
401      * partially or fully obscured by the status bar, navigation bar, IME or other system windows.
402      * </p>
403      *
404      * @return The top system window inset
405      * @deprecated Use {@link #getInsets(int)} with {@link Type#systemBars()}
406      * instead.
407      */
408     @Deprecated
getSystemWindowInsetTop()409     public int getSystemWindowInsetTop() {
410         return getSystemWindowInsets().top;
411     }
412 
413     /**
414      * Returns the right system window inset in pixels.
415      *
416      * <p>The system window inset represents the area of a full-screen window that is
417      * partially or fully obscured by the status bar, navigation bar, IME or other system windows.
418      * </p>
419      *
420      * @return The right system window inset
421      * @deprecated Use {@link #getInsets(int)} with {@link Type#systemBars()}
422      * instead.
423      */
424     @Deprecated
getSystemWindowInsetRight()425     public int getSystemWindowInsetRight() {
426         return getSystemWindowInsets().right;
427     }
428 
429     /**
430      * Returns the bottom system window inset in pixels.
431      *
432      * <p>The system window inset represents the area of a full-screen window that is
433      * partially or fully obscured by the status bar, navigation bar, IME or other system windows.
434      * </p>
435      *
436      * @return The bottom system window inset
437      * @deprecated Use {@link #getInsets(int)} with {@link Type#systemBars()}
438      * instead.
439      */
440     @Deprecated
getSystemWindowInsetBottom()441     public int getSystemWindowInsetBottom() {
442         return getSystemWindowInsets().bottom;
443     }
444 
445     /**
446      * Returns true if this WindowInsets has nonzero system window insets.
447      *
448      * <p>The system window inset represents the area of a full-screen window that is
449      * partially or fully obscured by the status bar, navigation bar, IME or other system windows.
450      * </p>
451      *
452      * @return true if any of the system window inset values are nonzero
453      * @deprecated Use {@link #getInsets(int)} with {@link Type#systemBars()}
454      * instead.
455      */
456     @Deprecated
hasSystemWindowInsets()457     public boolean hasSystemWindowInsets() {
458         return !getSystemWindowInsets().equals(Insets.NONE);
459     }
460 
461     /**
462      * Returns true if this WindowInsets has any nonzero insets.
463      *
464      * @return true if any inset values are nonzero
465      */
hasInsets()466     public boolean hasInsets() {
467         return !getInsets(mTypeInsetsMap, all()).equals(Insets.NONE)
468                 || !getInsets(mTypeMaxInsetsMap, all()).equals(Insets.NONE)
469                 || mDisplayCutout != null;
470     }
471 
472     /**
473      * Returns the display cutout if there is one.
474      *
475      * <p>Note: the display cutout will already be {@link #consumeDisplayCutout consumed} during
476      * dispatch to {@link View#onApplyWindowInsets}, unless the window has requested a
477      * {@link WindowManager.LayoutParams#layoutInDisplayCutoutMode} other than
478      * {@link WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER never} or
479      * {@link WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT default}.
480      *
481      * @return the display cutout or null if there is none
482      * @see DisplayCutout
483      */
484     @Nullable
getDisplayCutout()485     public DisplayCutout getDisplayCutout() {
486         return mDisplayCutout;
487     }
488 
489     /**
490      * Returns a copy of this WindowInsets with the cutout fully consumed.
491      *
492      * @return A modified copy of this WindowInsets
493      * @deprecated Consuming of different parts individually of a {@link WindowInsets} instance is
494      * deprecated, since {@link WindowInsets} contains many different insets. Use {@link #CONSUMED}
495      * instead to stop dispatching insets.
496      */
497     @Deprecated
498     @NonNull
consumeDisplayCutout()499     public WindowInsets consumeDisplayCutout() {
500         return new WindowInsets(mSystemWindowInsetsConsumed ? null : mTypeInsetsMap,
501                 mStableInsetsConsumed ? null : mTypeMaxInsetsMap,
502                 mTypeVisibilityMap,
503                 mIsRound, mAlwaysConsumeSystemBars,
504                 null /* displayCutout */,
505                 mCompatInsetsTypes, mCompatIgnoreVisibility);
506     }
507 
508 
509     /**
510      * Check if these insets have been fully consumed.
511      *
512      * <p>Insets are considered "consumed" if the applicable <code>consume*</code> methods
513      * have been called such that all insets have been set to zero. This affects propagation of
514      * insets through the view hierarchy; insets that have not been fully consumed will continue
515      * to propagate down to child views.</p>
516      *
517      * <p>The result of this method is equivalent to the return value of
518      * {@link View#fitSystemWindows(android.graphics.Rect)}.</p>
519      *
520      * @return true if the insets have been fully consumed.
521      */
isConsumed()522     public boolean isConsumed() {
523         return mSystemWindowInsetsConsumed && mStableInsetsConsumed
524                 && mDisplayCutoutConsumed;
525     }
526 
527     /**
528      * Returns true if the associated window has a round shape.
529      *
530      * <p>A round window's left, top, right and bottom edges reach all the way to the
531      * associated edges of the window but the corners may not be visible. Views responding
532      * to round insets should take care to not lay out critical elements within the corners
533      * where they may not be accessible.</p>
534      *
535      * @return True if the window is round
536      */
isRound()537     public boolean isRound() {
538         return mIsRound;
539     }
540 
541     /**
542      * Returns a copy of this WindowInsets with the system window insets fully consumed.
543      *
544      * @return A modified copy of this WindowInsets
545      * @deprecated Consuming of different parts individually of a {@link WindowInsets} instance is
546      * deprecated, since {@link WindowInsets} contains many different insets. Use {@link #CONSUMED}
547      * instead to stop dispatching insets.
548      */
549     @Deprecated
550     @NonNull
consumeSystemWindowInsets()551     public WindowInsets consumeSystemWindowInsets() {
552         return new WindowInsets(null, null,
553                 mTypeVisibilityMap,
554                 mIsRound, mAlwaysConsumeSystemBars,
555                 displayCutoutCopyConstructorArgument(this),
556                 mCompatInsetsTypes, mCompatIgnoreVisibility);
557     }
558 
559     // TODO(b/119190588): replace @code with @link below
560     /**
561      * Returns a copy of this WindowInsets with selected system window insets replaced
562      * with new values.
563      *
564      * <p>Note: If the system window insets are already consumed, this method will return them
565      * unchanged on {@link android.os.Build.VERSION_CODES#Q Q} and later. Prior to
566      * {@link android.os.Build.VERSION_CODES#Q Q}, the new values were applied regardless of
567      * whether they were consumed, and this method returns invalid non-zero consumed insets.
568      *
569      * @param left New left inset in pixels
570      * @param top New top inset in pixels
571      * @param right New right inset in pixels
572      * @param bottom New bottom inset in pixels
573      * @return A modified copy of this WindowInsets
574      * @deprecated use {@code Builder#Builder(WindowInsets)} with
575      *             {@link Builder#setSystemWindowInsets(Insets)} instead.
576      */
577     @Deprecated
578     @NonNull
replaceSystemWindowInsets(int left, int top, int right, int bottom)579     public WindowInsets replaceSystemWindowInsets(int left, int top, int right, int bottom) {
580         // Compat edge case: what should this do if the insets have already been consumed?
581         // On platforms prior to Q, the behavior was to override the insets with non-zero values,
582         // but leave them consumed, which is invalid (consumed insets must be zero).
583         // The behavior is now keeping them consumed and discarding the new insets.
584         if (mSystemWindowInsetsConsumed) {
585             return this;
586         }
587         return new Builder(this).setSystemWindowInsets(Insets.of(left, top, right, bottom)).build();
588     }
589 
590     // TODO(b/119190588): replace @code with @link below
591     /**
592      * Returns a copy of this WindowInsets with selected system window insets replaced
593      * with new values.
594      *
595      * <p>Note: If the system window insets are already consumed, this method will return them
596      * unchanged on {@link android.os.Build.VERSION_CODES#Q Q} and later. Prior to
597      * {@link android.os.Build.VERSION_CODES#Q Q}, the new values were applied regardless of
598      * whether they were consumed, and this method returns invalid non-zero consumed insets.
599      *
600      * @param systemWindowInsets New system window insets. Each field is the inset in pixels
601      *                           for that edge
602      * @return A modified copy of this WindowInsets
603      * @deprecated use {@code Builder#Builder(WindowInsets)} with
604      *             {@link Builder#setSystemWindowInsets(Insets)} instead.
605      */
606     @Deprecated
607     @NonNull
replaceSystemWindowInsets(Rect systemWindowInsets)608     public WindowInsets replaceSystemWindowInsets(Rect systemWindowInsets) {
609         return replaceSystemWindowInsets(systemWindowInsets.left, systemWindowInsets.top,
610                 systemWindowInsets.right, systemWindowInsets.bottom);
611     }
612 
613     /**
614      * Returns the stable insets in pixels.
615      *
616      * <p>The stable inset represents the area of a full-screen window that <b>may</b> be
617      * partially or fully obscured by the system UI elements.  This value does not change
618      * based on the visibility state of those elements; for example, if the status bar is
619      * normally shown, but temporarily hidden, the stable inset will still provide the inset
620      * associated with the status bar being shown.</p>
621      *
622      * @return The stable insets
623      * @deprecated Use {@link #getInsetsIgnoringVisibility(int)} with {@link Type#systemBars()}
624      * instead.
625      */
626     @Deprecated
627     @NonNull
getStableInsets()628     public Insets getStableInsets() {
629         return getInsets(mTypeMaxInsetsMap, mCompatInsetsTypes);
630     }
631 
632     /**
633      * Returns the top stable inset in pixels.
634      *
635      * <p>The stable inset represents the area of a full-screen window that <b>may</b> be
636      * partially or fully obscured by the system UI elements.  This value does not change
637      * based on the visibility state of those elements; for example, if the status bar is
638      * normally shown, but temporarily hidden, the stable inset will still provide the inset
639      * associated with the status bar being shown.</p>
640      *
641      * @return The top stable inset
642      * @deprecated Use {@link #getInsetsIgnoringVisibility(int)} with {@link Type#systemBars()}
643      * instead.
644      */
645     @Deprecated
getStableInsetTop()646     public int getStableInsetTop() {
647         return getStableInsets().top;
648     }
649 
650     /**
651      * Returns the left stable inset in pixels.
652      *
653      * <p>The stable inset represents the area of a full-screen window that <b>may</b> be
654      * partially or fully obscured by the system UI elements.  This value does not change
655      * based on the visibility state of those elements; for example, if the status bar is
656      * normally shown, but temporarily hidden, the stable inset will still provide the inset
657      * associated with the status bar being shown.</p>
658      *
659      * @return The left stable inset
660      * @deprecated Use {@link #getInsetsIgnoringVisibility(int)} with {@link Type#systemBars()}
661      * instead.
662      */
663     @Deprecated
getStableInsetLeft()664     public int getStableInsetLeft() {
665         return getStableInsets().left;
666     }
667 
668     /**
669      * Returns the right stable inset in pixels.
670      *
671      * <p>The stable inset represents the area of a full-screen window that <b>may</b> be
672      * partially or fully obscured by the system UI elements.  This value does not change
673      * based on the visibility state of those elements; for example, if the status bar is
674      * normally shown, but temporarily hidden, the stable inset will still provide the inset
675      * associated with the status bar being shown.</p>
676      *
677      * @return The right stable inset
678      * @deprecated Use {@link #getInsetsIgnoringVisibility(int)} with {@link Type#systemBars()}
679      * instead.
680      */
681     @Deprecated
getStableInsetRight()682     public int getStableInsetRight() {
683         return getStableInsets().right;
684     }
685 
686     /**
687      * Returns the bottom stable inset in pixels.
688      *
689      * <p>The stable inset represents the area of a full-screen window that <b>may</b> be
690      * partially or fully obscured by the system UI elements.  This value does not change
691      * based on the visibility state of those elements; for example, if the status bar is
692      * normally shown, but temporarily hidden, the stable inset will still provide the inset
693      * associated with the status bar being shown.</p>
694      *
695      * @return The bottom stable inset
696      * @deprecated Use {@link #getInsetsIgnoringVisibility(int)} with {@link Type#systemBars()}
697      * instead.
698      */
699     @Deprecated
getStableInsetBottom()700     public int getStableInsetBottom() {
701         return getStableInsets().bottom;
702     }
703 
704     /**
705      * Returns true if this WindowInsets has nonzero stable insets.
706      *
707      * <p>The stable inset represents the area of a full-screen window that <b>may</b> be
708      * partially or fully obscured by the system UI elements.  This value does not change
709      * based on the visibility state of those elements; for example, if the status bar is
710      * normally shown, but temporarily hidden, the stable inset will still provide the inset
711      * associated with the status bar being shown.</p>
712      *
713      * @return true if any of the stable inset values are nonzero
714      * @deprecated Use {@link #getInsetsIgnoringVisibility(int)} with {@link Type#systemBars()}
715      * instead.
716      */
717     @Deprecated
hasStableInsets()718     public boolean hasStableInsets() {
719         return !getStableInsets().equals(Insets.NONE);
720     }
721 
722     /**
723      * Returns the system gesture insets.
724      *
725      * <p>The system gesture insets represent the area of a window where system gestures have
726      * priority and may consume some or all touch input, e.g. due to the a system bar
727      * occupying it, or it being reserved for touch-only gestures.
728      *
729      * <p>An app can declare priority over system gestures with
730      * {@link View#setSystemGestureExclusionRects} outside of the
731      * {@link #getMandatorySystemGestureInsets() mandatory system gesture insets}.
732      *
733      * <p>Note: the system will put a limit of <code>200dp</code> on the vertical extent of the
734      * exclusions it takes into account. The limit does not apply while the navigation
735      * bar is {@link View#SYSTEM_UI_FLAG_IMMERSIVE_STICKY stickily} hidden, nor to the
736      * {@link android.inputmethodservice.InputMethodService input method} and
737      * {@link Intent#CATEGORY_HOME home activity}.
738      * </p>
739      *
740      *
741      * <p>Simple taps are guaranteed to reach the window even within the system gesture insets,
742      * as long as they are outside the {@link #getTappableElementInsets() system window insets}.
743      *
744      * <p>When {@link View#SYSTEM_UI_FLAG_LAYOUT_STABLE} is requested, an inset will be returned
745      * even when the system gestures are inactive due to
746      * {@link View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN} or
747      * {@link View#SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION}.
748      *
749      * <p>This inset is consumed together with the {@link #getSystemWindowInsets()
750      * system window insets} by {@link #consumeSystemWindowInsets()}.
751      *
752      * @see #getMandatorySystemGestureInsets
753      * @deprecated Use {@link #getInsets(int)} with {@link Type#systemGestures()} instead.
754      */
755     @Deprecated
756     @NonNull
getSystemGestureInsets()757     public Insets getSystemGestureInsets() {
758         return getInsets(mTypeInsetsMap, SYSTEM_GESTURES);
759     }
760 
761     /**
762      * Returns the mandatory system gesture insets.
763      *
764      * <p>The mandatory system gesture insets represent the area of a window where mandatory system
765      * gestures have priority and may consume some or all touch input, e.g. due to the a system bar
766      * occupying it, or it being reserved for touch-only gestures.
767      *
768      * <p>In contrast to {@link #getSystemGestureInsets regular system gestures}, <b>mandatory</b>
769      * system gestures cannot be overriden by {@link View#setSystemGestureExclusionRects}.
770      *
771      * <p>Simple taps are guaranteed to reach the window even within the system gesture insets,
772      * as long as they are outside the {@link #getTappableElementInsets() system window insets}.
773      *
774      * <p>When {@link View#SYSTEM_UI_FLAG_LAYOUT_STABLE} is requested, an inset will be returned
775      * even when the system gestures are inactive due to
776      * {@link View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN} or
777      * {@link View#SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION}.
778      *
779      * <p>This inset is consumed together with the {@link #getSystemWindowInsets()
780      * system window insets} by {@link #consumeSystemWindowInsets()}.
781      *
782      * @see #getSystemGestureInsets
783      * @deprecated Use {@link #getInsets(int)} with {@link Type#mandatorySystemGestures()} instead.
784      */
785     @Deprecated
786     @NonNull
getMandatorySystemGestureInsets()787     public Insets getMandatorySystemGestureInsets() {
788         return getInsets(mTypeInsetsMap, MANDATORY_SYSTEM_GESTURES);
789     }
790 
791     /**
792      * Returns the tappable element insets.
793      *
794      * <p>The tappable element insets represent how much tappable elements <b>must at least</b> be
795      * inset to remain both tappable and visually unobstructed by persistent system windows.
796      *
797      * <p>This may be smaller than {@link #getSystemWindowInsets()} if the system window is
798      * largely transparent and lets through simple taps (but not necessarily more complex gestures).
799      *
800      * <p>Note that generally, tappable elements <strong>should</strong> be aligned with the
801      * {@link #getSystemWindowInsets() system window insets} instead to avoid overlapping with the
802      * system bars.
803      *
804      * <p>When {@link View#SYSTEM_UI_FLAG_LAYOUT_STABLE} is requested, an inset will be returned
805      * even when the area covered by the inset would be tappable due to
806      * {@link View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN} or
807      * {@link View#SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION}.
808      *
809      * <p>This inset is consumed together with the {@link #getSystemWindowInsets()
810      * system window insets} by {@link #consumeSystemWindowInsets()}.
811      *
812      * @deprecated Use {@link #getInsets(int)} with {@link Type#tappableElement()} instead.
813      */
814     @Deprecated
815     @NonNull
getTappableElementInsets()816     public Insets getTappableElementInsets() {
817         return getInsets(mTypeInsetsMap, TAPPABLE_ELEMENT);
818     }
819 
820     /**
821      * Returns a copy of this WindowInsets with the stable insets fully consumed.
822      *
823      * @return A modified copy of this WindowInsets
824      * @deprecated Consuming of different parts individually of a {@link WindowInsets} instance is
825      * deprecated, since {@link WindowInsets} contains many different insets. Use {@link #CONSUMED}
826      * instead to stop dispatching insets. On {@link android.os.Build.VERSION_CODES#R R}, this
827      * method has no effect.
828      */
829     @Deprecated
830     @NonNull
consumeStableInsets()831     public WindowInsets consumeStableInsets() {
832         return this;
833     }
834 
835     /**
836      * @hide
837      */
shouldAlwaysConsumeSystemBars()838     public boolean shouldAlwaysConsumeSystemBars() {
839         return mAlwaysConsumeSystemBars;
840     }
841 
842     @Override
toString()843     public String toString() {
844         StringBuilder result = new StringBuilder("WindowInsets{\n    ");
845         for (int i = 0; i < SIZE; i++) {
846             Insets insets = mTypeInsetsMap[i];
847             Insets maxInsets = mTypeMaxInsetsMap[i];
848             boolean visible = mTypeVisibilityMap[i];
849             if (!Insets.NONE.equals(insets) || !Insets.NONE.equals(maxInsets) || visible) {
850                 result.append(Type.toString(1 << i)).append("=").append(insets)
851                         .append(" max=").append(maxInsets)
852                         .append(" vis=").append(visible)
853                         .append("\n    ");
854             }
855         }
856 
857         result.append(mDisplayCutout != null ? "cutout=" + mDisplayCutout : "");
858         result.append("\n    ");
859         result.append(isRound() ? "round" : "");
860         result.append("}");
861         return result.toString();
862     }
863 
864     /**
865      * Returns a copy of this instance inset in the given directions.
866      *
867      * @see #inset(int, int, int, int)
868      * @deprecated use {@link #inset(Insets)}
869      * @hide
870      */
871     @Deprecated
872     @NonNull
inset(Rect r)873     public WindowInsets inset(Rect r) {
874         return inset(r.left, r.top, r.right, r.bottom);
875     }
876 
877     /**
878      * Returns a copy of this instance inset in the given directions.
879      *
880      * This is intended for dispatching insets to areas of the window that are smaller than the
881      * current area.
882      *
883      * <p>Example:
884      * <pre>
885      * childView.dispatchApplyWindowInsets(insets.inset(childMargins));
886      * </pre>
887      *
888      * @param insets the amount of insets to remove from all sides.
889      *
890      * @see #inset(int, int, int, int)
891      */
892     @NonNull
inset(@onNull Insets insets)893     public WindowInsets inset(@NonNull Insets insets) {
894         Objects.requireNonNull(insets);
895         return inset(insets.left, insets.top, insets.right, insets.bottom);
896     }
897 
898     /**
899      * Returns a copy of this instance inset in the given directions.
900      *
901      * This is intended for dispatching insets to areas of the window that are smaller than the
902      * current area.
903      *
904      * <p>Example:
905      * <pre>
906      * childView.dispatchApplyWindowInsets(insets.inset(
907      *         childMarginLeft, childMarginTop, childMarginBottom, childMarginRight));
908      * </pre>
909      *
910      * @param left the amount of insets to remove from the left. Must be non-negative.
911      * @param top the amount of insets to remove from the top. Must be non-negative.
912      * @param right the amount of insets to remove from the right. Must be non-negative.
913      * @param bottom the amount of insets to remove from the bottom. Must be non-negative.
914      *
915      * @return the inset insets
916      *
917      * @see #inset(Insets)
918      */
919     @NonNull
inset(@ntRangefrom = 0) int left, @IntRange(from = 0) int top, @IntRange(from = 0) int right, @IntRange(from = 0) int bottom)920     public WindowInsets inset(@IntRange(from = 0) int left, @IntRange(from = 0) int top,
921             @IntRange(from = 0) int right, @IntRange(from = 0) int bottom) {
922         Preconditions.checkArgumentNonnegative(left);
923         Preconditions.checkArgumentNonnegative(top);
924         Preconditions.checkArgumentNonnegative(right);
925         Preconditions.checkArgumentNonnegative(bottom);
926 
927         return new WindowInsets(
928                 mSystemWindowInsetsConsumed
929                         ? null
930                         : insetInsets(mTypeInsetsMap, left, top, right, bottom),
931                 mStableInsetsConsumed
932                         ? null
933                         : insetInsets(mTypeMaxInsetsMap, left, top, right, bottom),
934                 mTypeVisibilityMap,
935                 mIsRound, mAlwaysConsumeSystemBars,
936                 mDisplayCutoutConsumed
937                         ? null
938                         : mDisplayCutout == null
939                                 ? DisplayCutout.NO_CUTOUT
940                                 : mDisplayCutout.inset(left, top, right, bottom),
941                 mCompatInsetsTypes, mCompatIgnoreVisibility);
942     }
943 
944     @Override
equals(Object o)945     public boolean equals(Object o) {
946         if (this == o) return true;
947         if (o == null || !(o instanceof WindowInsets)) return false;
948         WindowInsets that = (WindowInsets) o;
949 
950         return mIsRound == that.mIsRound
951                 && mAlwaysConsumeSystemBars == that.mAlwaysConsumeSystemBars
952                 && mSystemWindowInsetsConsumed == that.mSystemWindowInsetsConsumed
953                 && mStableInsetsConsumed == that.mStableInsetsConsumed
954                 && mDisplayCutoutConsumed == that.mDisplayCutoutConsumed
955                 && Arrays.equals(mTypeInsetsMap, that.mTypeInsetsMap)
956                 && Arrays.equals(mTypeMaxInsetsMap, that.mTypeMaxInsetsMap)
957                 && Arrays.equals(mTypeVisibilityMap, that.mTypeVisibilityMap)
958                 && Objects.equals(mDisplayCutout, that.mDisplayCutout);
959     }
960 
961     @Override
hashCode()962     public int hashCode() {
963         return Objects.hash(Arrays.hashCode(mTypeInsetsMap), Arrays.hashCode(mTypeMaxInsetsMap),
964                 Arrays.hashCode(mTypeVisibilityMap), mIsRound, mDisplayCutout,
965                 mAlwaysConsumeSystemBars, mSystemWindowInsetsConsumed, mStableInsetsConsumed,
966                 mDisplayCutoutConsumed);
967     }
968 
969 
970     /**
971      * Insets every inset in {@code typeInsetsMap} by the specified left, top, right, bottom.
972      *
973      * @return {@code typeInsetsMap} if no inset was modified; a copy of the map with the modified
974      *          insets otherwise.
975      */
insetInsets( Insets[] typeInsetsMap, int left, int top, int right, int bottom)976     private static Insets[] insetInsets(
977             Insets[] typeInsetsMap, int left, int top, int right, int bottom) {
978         boolean cloned = false;
979         for (int i = 0; i < SIZE; i++) {
980             Insets insets = typeInsetsMap[i];
981             if (insets == null) {
982                 continue;
983             }
984             Insets insetInsets = insetInsets(insets, left, top, right, bottom);
985             if (insetInsets != insets) {
986                 if (!cloned) {
987                     typeInsetsMap = typeInsetsMap.clone();
988                     cloned = true;
989                 }
990                 typeInsetsMap[i] = insetInsets;
991             }
992         }
993         return typeInsetsMap;
994     }
995 
insetInsets(Insets insets, int left, int top, int right, int bottom)996     static Insets insetInsets(Insets insets, int left, int top, int right, int bottom) {
997         int newLeft = Math.max(0, insets.left - left);
998         int newTop = Math.max(0, insets.top - top);
999         int newRight = Math.max(0, insets.right - right);
1000         int newBottom = Math.max(0, insets.bottom - bottom);
1001         if (newLeft == left && newTop == top && newRight == right && newBottom == bottom) {
1002             return insets;
1003         }
1004         return Insets.of(newLeft, newTop, newRight, newBottom);
1005     }
1006 
1007     /**
1008      * @return whether system window insets have been consumed.
1009      */
isSystemWindowInsetsConsumed()1010     boolean isSystemWindowInsetsConsumed() {
1011         return mSystemWindowInsetsConsumed;
1012     }
1013 
1014     /**
1015      * Builder for WindowInsets.
1016      */
1017     public static final class Builder {
1018 
1019         private final Insets[] mTypeInsetsMap;
1020         private final Insets[] mTypeMaxInsetsMap;
1021         private final boolean[] mTypeVisibilityMap;
1022         private boolean mSystemInsetsConsumed = true;
1023         private boolean mStableInsetsConsumed = true;
1024 
1025         private DisplayCutout mDisplayCutout;
1026 
1027         private boolean mIsRound;
1028         private boolean mAlwaysConsumeSystemBars;
1029 
1030         /**
1031          * Creates a builder where all insets are initially consumed.
1032          */
Builder()1033         public Builder() {
1034             mTypeInsetsMap = new Insets[SIZE];
1035             mTypeMaxInsetsMap = new Insets[SIZE];
1036             mTypeVisibilityMap = new boolean[SIZE];
1037         }
1038 
1039         /**
1040          * Creates a builder where all insets are initialized from {@link WindowInsets}.
1041          *
1042          * @param insets the instance to initialize from.
1043          */
Builder(@onNull WindowInsets insets)1044         public Builder(@NonNull WindowInsets insets) {
1045             mTypeInsetsMap = insets.mTypeInsetsMap.clone();
1046             mTypeMaxInsetsMap = insets.mTypeMaxInsetsMap.clone();
1047             mTypeVisibilityMap = insets.mTypeVisibilityMap.clone();
1048             mSystemInsetsConsumed = insets.mSystemWindowInsetsConsumed;
1049             mStableInsetsConsumed = insets.mStableInsetsConsumed;
1050             mDisplayCutout = displayCutoutCopyConstructorArgument(insets);
1051             mIsRound = insets.mIsRound;
1052             mAlwaysConsumeSystemBars = insets.mAlwaysConsumeSystemBars;
1053         }
1054 
1055         /**
1056          * Sets system window insets in pixels.
1057          *
1058          * <p>The system window inset represents the area of a full-screen window that is
1059          * partially or fully obscured by the status bar, navigation bar, IME or other system
1060          * windows.</p>
1061          *
1062          * @see #getSystemWindowInsets()
1063          * @return itself
1064          * @deprecated Use {@link #setInsets(int, Insets)} with {@link Type#systemBars()}.
1065          */
1066         @Deprecated
1067         @NonNull
setSystemWindowInsets(@onNull Insets systemWindowInsets)1068         public Builder setSystemWindowInsets(@NonNull Insets systemWindowInsets) {
1069             Preconditions.checkNotNull(systemWindowInsets);
1070             assignCompatInsets(mTypeInsetsMap, systemWindowInsets.toRect());
1071             mSystemInsetsConsumed = false;
1072             return this;
1073         }
1074 
1075         /**
1076          * Sets system gesture insets in pixels.
1077          *
1078          * <p>The system gesture insets represent the area of a window where system gestures have
1079          * priority and may consume some or all touch input, e.g. due to the a system bar
1080          * occupying it, or it being reserved for touch-only gestures.
1081          *
1082          * @see #getSystemGestureInsets()
1083          * @return itself
1084          * @deprecated Use {@link #setInsets(int, Insets)} with {@link Type#systemGestures()}.
1085          */
1086         @Deprecated
1087         @NonNull
setSystemGestureInsets(@onNull Insets insets)1088         public Builder setSystemGestureInsets(@NonNull Insets insets) {
1089             WindowInsets.setInsets(mTypeInsetsMap, SYSTEM_GESTURES, insets);
1090             return this;
1091         }
1092 
1093         /**
1094          * Sets mandatory system gesture insets in pixels.
1095          *
1096          * <p>The mandatory system gesture insets represent the area of a window where mandatory
1097          * system gestures have priority and may consume some or all touch input, e.g. due to the a
1098          * system bar occupying it, or it being reserved for touch-only gestures.
1099          *
1100          * <p>In contrast to {@link #setSystemGestureInsets regular system gestures},
1101          * <b>mandatory</b> system gestures cannot be overriden by
1102          * {@link View#setSystemGestureExclusionRects}.
1103          *
1104          * @see #getMandatorySystemGestureInsets()
1105          * @return itself
1106          * @deprecated Use {@link #setInsets(int, Insets)} with
1107          *             {@link Type#mandatorySystemGestures()}.
1108          */
1109         @Deprecated
1110         @NonNull
setMandatorySystemGestureInsets(@onNull Insets insets)1111         public Builder setMandatorySystemGestureInsets(@NonNull Insets insets) {
1112             WindowInsets.setInsets(mTypeInsetsMap, MANDATORY_SYSTEM_GESTURES, insets);
1113             return this;
1114         }
1115 
1116         /**
1117          * Sets tappable element insets in pixels.
1118          *
1119          * <p>The tappable element insets represent how much tappable elements <b>must at least</b>
1120          * be inset to remain both tappable and visually unobstructed by persistent system windows.
1121          *
1122          * @see #getTappableElementInsets()
1123          * @return itself
1124          * @deprecated Use {@link #setInsets(int, Insets)} with {@link Type#tappableElement()}.
1125          */
1126         @Deprecated
1127         @NonNull
setTappableElementInsets(@onNull Insets insets)1128         public Builder setTappableElementInsets(@NonNull Insets insets) {
1129             WindowInsets.setInsets(mTypeInsetsMap, TAPPABLE_ELEMENT, insets);
1130             return this;
1131         }
1132 
1133         /**
1134          * Sets the insets of a specific window type in pixels.
1135          *
1136          * <p>The insets represents the area of a a window that is partially or fully obscured by
1137          * the system windows identified by {@code typeMask}.
1138          * </p>
1139          *
1140          * @see #getInsets(int)
1141          *
1142          * @param typeMask The bitmask of {@link InsetsType} to set the insets for.
1143          * @param insets The insets to set.
1144          *
1145          * @return itself
1146          */
1147         @NonNull
setInsets(@nsetsType int typeMask, @NonNull Insets insets)1148         public Builder setInsets(@InsetsType int typeMask, @NonNull Insets insets) {
1149             Preconditions.checkNotNull(insets);
1150             WindowInsets.setInsets(mTypeInsetsMap, typeMask, insets);
1151             mSystemInsetsConsumed = false;
1152             return this;
1153         }
1154 
1155         /**
1156          * Sets the insets a specific window type in pixels, while ignoring its visibility state.
1157          *
1158          * <p>The insets represents the area of a a window that that <b>may</b> be partially
1159          * or fully obscured by the system window identified by {@code type}. This value does not
1160          * change based on the visibility state of those elements. For example, if the status bar is
1161          * normally shown, but temporarily hidden, the inset returned here will still provide the
1162          * inset associated with the status bar being shown.</p>
1163          *
1164          * @see #getInsetsIgnoringVisibility(int)
1165          *
1166          * @param typeMask The bitmask of {@link InsetsType} to set the insets for.
1167          * @param insets The insets to set.
1168          *
1169          * @return itself
1170          *
1171          * @throws IllegalArgumentException If {@code typeMask} contains {@link Type#ime()}. Maximum
1172          *                                  insets are not available for this type as the height of
1173          *                                  the IME is dynamic depending on the {@link EditorInfo}
1174          *                                  of the currently focused view, as well as the UI
1175          *                                  state of the IME.
1176          */
1177         @NonNull
setInsetsIgnoringVisibility(@nsetsType int typeMask, @NonNull Insets insets)1178         public Builder setInsetsIgnoringVisibility(@InsetsType int typeMask, @NonNull Insets insets)
1179                 throws IllegalArgumentException{
1180             if (typeMask == IME) {
1181                 throw new IllegalArgumentException("Maximum inset not available for IME");
1182             }
1183             Preconditions.checkNotNull(insets);
1184             WindowInsets.setInsets(mTypeMaxInsetsMap, typeMask, insets);
1185             mStableInsetsConsumed = false;
1186             return this;
1187         }
1188 
1189         /**
1190          * Sets whether windows that can cause insets are currently visible on screen.
1191          *
1192          *
1193          * @see #isVisible(int)
1194          *
1195          * @param typeMask The bitmask of {@link InsetsType} to set the visibility for.
1196          * @param visible Whether to mark the windows as visible or not.
1197          *
1198          * @return itself
1199          */
1200         @NonNull
setVisible(@nsetsType int typeMask, boolean visible)1201         public Builder setVisible(@InsetsType int typeMask, boolean visible) {
1202             for (int i = FIRST; i <= LAST; i = i << 1) {
1203                 if ((typeMask & i) == 0) {
1204                     continue;
1205                 }
1206                 mTypeVisibilityMap[indexOf(i)] = visible;
1207             }
1208             return this;
1209         }
1210 
1211         /**
1212          * Sets the stable insets in pixels.
1213          *
1214          * <p>The stable inset represents the area of a full-screen window that <b>may</b> be
1215          * partially or fully obscured by the system UI elements.  This value does not change
1216          * based on the visibility state of those elements; for example, if the status bar is
1217          * normally shown, but temporarily hidden, the stable inset will still provide the inset
1218          * associated with the status bar being shown.</p>
1219          *
1220          * @see #getStableInsets()
1221          * @return itself
1222          * @deprecated Use {@link #setInsetsIgnoringVisibility(int, Insets)} with
1223          *             {@link Type#systemBars()}.
1224          */
1225         @Deprecated
1226         @NonNull
setStableInsets(@onNull Insets stableInsets)1227         public Builder setStableInsets(@NonNull Insets stableInsets) {
1228             Preconditions.checkNotNull(stableInsets);
1229             assignCompatInsets(mTypeMaxInsetsMap, stableInsets.toRect());
1230             mStableInsetsConsumed = false;
1231             return this;
1232         }
1233 
1234         /**
1235          * Sets the display cutout.
1236          *
1237          * @see #getDisplayCutout()
1238          * @param displayCutout the display cutout or null if there is none
1239          * @return itself
1240          */
1241         @NonNull
setDisplayCutout(@ullable DisplayCutout displayCutout)1242         public Builder setDisplayCutout(@Nullable DisplayCutout displayCutout) {
1243             mDisplayCutout = displayCutout != null ? displayCutout : DisplayCutout.NO_CUTOUT;
1244             if (!mDisplayCutout.isEmpty()) {
1245                 final Insets safeInsets = Insets.of(mDisplayCutout.getSafeInsets());
1246                 final int index = indexOf(DISPLAY_CUTOUT);
1247                 mTypeInsetsMap[index] = safeInsets;
1248                 mTypeMaxInsetsMap[index] = safeInsets;
1249                 mTypeVisibilityMap[index] = true;
1250             }
1251             return this;
1252         }
1253 
1254         /** @hide */
1255         @NonNull
setRound(boolean round)1256         public Builder setRound(boolean round) {
1257             mIsRound = round;
1258             return this;
1259         }
1260 
1261         /** @hide */
1262         @NonNull
setAlwaysConsumeSystemBars(boolean alwaysConsumeSystemBars)1263         public Builder setAlwaysConsumeSystemBars(boolean alwaysConsumeSystemBars) {
1264             mAlwaysConsumeSystemBars = alwaysConsumeSystemBars;
1265             return this;
1266         }
1267 
1268         /**
1269          * Builds a {@link WindowInsets} instance.
1270          *
1271          * @return the {@link WindowInsets} instance.
1272          */
1273         @NonNull
build()1274         public WindowInsets build() {
1275             return new WindowInsets(mSystemInsetsConsumed ? null : mTypeInsetsMap,
1276                     mStableInsetsConsumed ? null : mTypeMaxInsetsMap, mTypeVisibilityMap,
1277                     mIsRound, mAlwaysConsumeSystemBars, mDisplayCutout,
1278                     systemBars(), false /* compatIgnoreVisibility */);
1279         }
1280     }
1281 
1282     /**
1283      * Class that defines different types of sources causing window insets.
1284      */
1285     public static final class Type {
1286 
1287         static final int FIRST = 1 << 0;
1288         static final int STATUS_BARS = FIRST;
1289         static final int NAVIGATION_BARS = 1 << 1;
1290         static final int CAPTION_BAR = 1 << 2;
1291 
1292         static final int IME = 1 << 3;
1293 
1294         static final int SYSTEM_GESTURES = 1 << 4;
1295         static final int MANDATORY_SYSTEM_GESTURES = 1 << 5;
1296         static final int TAPPABLE_ELEMENT = 1 << 6;
1297 
1298         static final int DISPLAY_CUTOUT = 1 << 7;
1299 
1300         static final int LAST = 1 << 8;
1301         static final int SIZE = 9;
1302         static final int WINDOW_DECOR = LAST;
1303 
indexOf(@nsetsType int type)1304         static int indexOf(@InsetsType int type) {
1305             switch (type) {
1306                 case STATUS_BARS:
1307                     return 0;
1308                 case NAVIGATION_BARS:
1309                     return 1;
1310                 case CAPTION_BAR:
1311                     return 2;
1312                 case IME:
1313                     return 3;
1314                 case SYSTEM_GESTURES:
1315                     return 4;
1316                 case MANDATORY_SYSTEM_GESTURES:
1317                     return 5;
1318                 case TAPPABLE_ELEMENT:
1319                     return 6;
1320                 case DISPLAY_CUTOUT:
1321                     return 7;
1322                 case WINDOW_DECOR:
1323                     return 8;
1324                 default:
1325                     throw new IllegalArgumentException("type needs to be >= FIRST and <= LAST,"
1326                             + " type=" + type);
1327             }
1328         }
1329 
toString(@nsetsType int types)1330         static String toString(@InsetsType int types) {
1331             StringBuilder result = new StringBuilder();
1332             if ((types & STATUS_BARS) != 0) {
1333                 result.append("statusBars |");
1334             }
1335             if ((types & NAVIGATION_BARS) != 0) {
1336                 result.append("navigationBars |");
1337             }
1338             if ((types & IME) != 0) {
1339                 result.append("ime |");
1340             }
1341             if ((types & SYSTEM_GESTURES) != 0) {
1342                 result.append("systemGestures |");
1343             }
1344             if ((types & MANDATORY_SYSTEM_GESTURES) != 0) {
1345                 result.append("mandatorySystemGestures |");
1346             }
1347             if ((types & TAPPABLE_ELEMENT) != 0) {
1348                 result.append("tappableElement |");
1349             }
1350             if ((types & DISPLAY_CUTOUT) != 0) {
1351                 result.append("displayCutout |");
1352             }
1353             if ((types & WINDOW_DECOR) != 0) {
1354                 result.append("windowDecor |");
1355             }
1356             if (result.length() > 0) {
1357                 result.delete(result.length() - 2, result.length());
1358             }
1359             return result.toString();
1360         }
1361 
Type()1362         private Type() {
1363         }
1364 
1365         /** @hide */
1366         @Retention(RetentionPolicy.SOURCE)
1367         @IntDef(flag = true, value = {STATUS_BARS, NAVIGATION_BARS, CAPTION_BAR, IME, WINDOW_DECOR,
1368                 SYSTEM_GESTURES, MANDATORY_SYSTEM_GESTURES, TAPPABLE_ELEMENT, DISPLAY_CUTOUT})
1369         public @interface InsetsType {
1370         }
1371 
1372         /**
1373          * @return An insets type representing any system bars for displaying status.
1374          */
statusBars()1375         public static @InsetsType int statusBars() {
1376             return STATUS_BARS;
1377         }
1378 
1379         /**
1380          * @return An insets type representing any system bars for navigation.
1381          */
navigationBars()1382         public static @InsetsType int navigationBars() {
1383             return NAVIGATION_BARS;
1384         }
1385 
1386         /**
1387          * @return An insets type representing the window of a caption bar.
1388          */
captionBar()1389         public static @InsetsType int captionBar() {
1390             return CAPTION_BAR;
1391         }
1392 
1393         /**
1394          * @return An insets type representing the window of an {@link InputMethod}.
1395          */
ime()1396         public static @InsetsType int ime() {
1397             return IME;
1398         }
1399 
1400         /**
1401          * Returns an insets type representing the system gesture insets.
1402          *
1403          * <p>The system gesture insets represent the area of a window where system gestures have
1404          * priority and may consume some or all touch input, e.g. due to the a system bar
1405          * occupying it, or it being reserved for touch-only gestures.
1406          *
1407          * <p>Simple taps are guaranteed to reach the window even within the system gesture insets,
1408          * as long as they are outside the {@link #getSystemWindowInsets() system window insets}.
1409          *
1410          * <p>When {@link View#SYSTEM_UI_FLAG_LAYOUT_STABLE} is requested, an inset will be returned
1411          * even when the system gestures are inactive due to
1412          * {@link View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN} or
1413          * {@link View#SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION}.
1414          *
1415          * @see #getSystemGestureInsets()
1416          */
systemGestures()1417         public static @InsetsType int systemGestures() {
1418             return SYSTEM_GESTURES;
1419         }
1420 
1421         /**
1422          * @see #getMandatorySystemGestureInsets
1423          */
mandatorySystemGestures()1424         public static @InsetsType int mandatorySystemGestures() {
1425             return MANDATORY_SYSTEM_GESTURES;
1426         }
1427 
1428         /**
1429          * @see #getTappableElementInsets
1430          */
tappableElement()1431         public static @InsetsType int tappableElement() {
1432             return TAPPABLE_ELEMENT;
1433         }
1434 
1435         /**
1436          * Returns an insets type representing the area that used by {@link DisplayCutout}.
1437          *
1438          * <p>This is equivalent to the safe insets on {@link #getDisplayCutout()}.
1439          *
1440          * <p>Note: During dispatch to {@link View#onApplyWindowInsets}, if the window is using
1441          * the {@link WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT default}
1442          * {@link WindowManager.LayoutParams#layoutInDisplayCutoutMode}, {@link #getDisplayCutout()}
1443          * will return {@code null} even if the window overlaps a display cutout area, in which case
1444          * the {@link #displayCutout() displayCutout() inset} will still report the accurate value.
1445          *
1446          * @see DisplayCutout#getSafeInsetLeft()
1447          * @see DisplayCutout#getSafeInsetTop()
1448          * @see DisplayCutout#getSafeInsetRight()
1449          * @see DisplayCutout#getSafeInsetBottom()
1450          */
displayCutout()1451         public static @InsetsType int displayCutout() {
1452             return DISPLAY_CUTOUT;
1453         }
1454 
1455         /**
1456          * @return All system bars. Includes {@link #statusBars()}, {@link #captionBar()} as well as
1457          *         {@link #navigationBars()}, but not {@link #ime()}.
1458          */
systemBars()1459         public static @InsetsType int systemBars() {
1460             return STATUS_BARS | NAVIGATION_BARS | CAPTION_BAR;
1461         }
1462 
1463         /**
1464          * @return All inset types combined.
1465          *
1466          * @hide
1467          */
all()1468         public static @InsetsType int all() {
1469             return 0xFFFFFFFF;
1470         }
1471 
1472         /**
1473          * Checks whether the specified type is considered to be part of visible insets.
1474          * @hide
1475          */
isVisibleInsetsType(int type, @SoftInputModeFlags int softInputModeFlags)1476         public static boolean isVisibleInsetsType(int type,
1477                 @SoftInputModeFlags int softInputModeFlags) {
1478             int softInputMode = softInputModeFlags & SOFT_INPUT_MASK_ADJUST;
1479             return (type & Type.systemBars()) != 0
1480                     || (softInputMode != SOFT_INPUT_ADJUST_NOTHING && (type & Type.ime()) != 0);
1481         }
1482     }
1483 
1484     /**
1485      * Class that defines different sides for insets.
1486      */
1487     public static final class Side {
1488 
1489         public static final int LEFT = 1 << 0;
1490         public static final int TOP = 1 << 1;
1491         public static final int RIGHT = 1 << 2;
1492         public static final int BOTTOM = 1 << 3;
1493 
Side()1494         private Side() {
1495         }
1496 
1497         /** @hide */
1498         @Retention(RetentionPolicy.SOURCE)
1499         @IntDef(flag = true, value = {LEFT, TOP, RIGHT, BOTTOM})
1500         public @interface InsetsSide {}
1501 
1502         /**
1503          * @return all four sides.
1504          */
all()1505         public static @InsetsSide int all() {
1506             return LEFT | TOP | RIGHT | BOTTOM;
1507         }
1508     }
1509 }
1510