• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 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 android.view;
18 
19 import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
20 import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
21 import static android.view.ViewRootImpl.NEW_INSETS_MODE_IME;
22 import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE;
23 import static android.view.ViewRootImpl.sNewInsetsMode;
24 import static android.view.WindowInsets.Type.MANDATORY_SYSTEM_GESTURES;
25 import static android.view.WindowInsets.Type.SYSTEM_GESTURES;
26 import static android.view.WindowInsets.Type.displayCutout;
27 import static android.view.WindowInsets.Type.ime;
28 import static android.view.WindowInsets.Type.indexOf;
29 import static android.view.WindowInsets.Type.isVisibleInsetsType;
30 import static android.view.WindowInsets.Type.statusBars;
31 import static android.view.WindowInsets.Type.systemBars;
32 import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
33 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
34 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
35 
36 import android.annotation.IntDef;
37 import android.annotation.Nullable;
38 import android.graphics.Insets;
39 import android.graphics.Rect;
40 import android.os.Parcel;
41 import android.os.Parcelable;
42 import android.util.ArraySet;
43 import android.util.SparseIntArray;
44 import android.view.WindowInsets.Type;
45 import android.view.WindowInsets.Type.InsetsType;
46 import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
47 
48 import com.android.internal.annotations.VisibleForTesting;
49 
50 import java.io.PrintWriter;
51 import java.lang.annotation.Retention;
52 import java.lang.annotation.RetentionPolicy;
53 import java.util.Arrays;
54 import java.util.Objects;
55 import java.util.StringJoiner;
56 
57 /**
58  * Holder for state of system windows that cause window insets for all other windows in the system.
59  * @hide
60  */
61 public class InsetsState implements Parcelable {
62 
63     public static final InsetsState EMPTY = new InsetsState();
64 
65     /**
66      * Internal representation of inset source types. This is different from the public API in
67      * {@link WindowInsets.Type} as one type from the public API might indicate multiple windows
68      * at the same time.
69      */
70     @Retention(RetentionPolicy.SOURCE)
71     @IntDef(prefix = "ITYPE", value = {
72             ITYPE_STATUS_BAR,
73             ITYPE_NAVIGATION_BAR,
74             ITYPE_CAPTION_BAR,
75             ITYPE_TOP_GESTURES,
76             ITYPE_BOTTOM_GESTURES,
77             ITYPE_LEFT_GESTURES,
78             ITYPE_RIGHT_GESTURES,
79             ITYPE_TOP_MANDATORY_GESTURES,
80             ITYPE_BOTTOM_MANDATORY_GESTURES,
81             ITYPE_LEFT_MANDATORY_GESTURES,
82             ITYPE_RIGHT_MANDATORY_GESTURES,
83             ITYPE_TOP_TAPPABLE_ELEMENT,
84             ITYPE_BOTTOM_TAPPABLE_ELEMENT,
85             ITYPE_LEFT_DISPLAY_CUTOUT,
86             ITYPE_TOP_DISPLAY_CUTOUT,
87             ITYPE_RIGHT_DISPLAY_CUTOUT,
88             ITYPE_BOTTOM_DISPLAY_CUTOUT,
89             ITYPE_IME,
90             ITYPE_CLIMATE_BAR,
91             ITYPE_EXTRA_NAVIGATION_BAR
92     })
93     public @interface InternalInsetsType {}
94 
95     /**
96      * Special value to be used to by methods returning an {@link InternalInsetsType} to indicate
97      * that the objects/parameters aren't associated with an {@link InternalInsetsType}
98      */
99     public static final int ITYPE_INVALID = -1;
100 
101     static final int FIRST_TYPE = 0;
102 
103     public static final int ITYPE_STATUS_BAR = FIRST_TYPE;
104     public static final int ITYPE_NAVIGATION_BAR = 1;
105     public static final int ITYPE_CAPTION_BAR = 2;
106 
107     public static final int ITYPE_TOP_GESTURES = 3;
108     public static final int ITYPE_BOTTOM_GESTURES = 4;
109     public static final int ITYPE_LEFT_GESTURES = 5;
110     public static final int ITYPE_RIGHT_GESTURES = 6;
111 
112     /** Additional gesture inset types that map into {@link Type.MANDATORY_SYSTEM_GESTURES}. */
113     public static final int ITYPE_TOP_MANDATORY_GESTURES = 7;
114     public static final int ITYPE_BOTTOM_MANDATORY_GESTURES = 8;
115     public static final int ITYPE_LEFT_MANDATORY_GESTURES = 9;
116     public static final int ITYPE_RIGHT_MANDATORY_GESTURES = 10;
117 
118     public static final int ITYPE_TOP_TAPPABLE_ELEMENT = 11;
119     public static final int ITYPE_BOTTOM_TAPPABLE_ELEMENT = 12;
120 
121     public static final int ITYPE_LEFT_DISPLAY_CUTOUT = 13;
122     public static final int ITYPE_TOP_DISPLAY_CUTOUT = 14;
123     public static final int ITYPE_RIGHT_DISPLAY_CUTOUT = 15;
124     public static final int ITYPE_BOTTOM_DISPLAY_CUTOUT = 16;
125 
126     /** Input method window. */
127     public static final int ITYPE_IME = 17;
128 
129     /** Additional system decorations inset type. */
130     public static final int ITYPE_CLIMATE_BAR = 18;
131     public static final int ITYPE_EXTRA_NAVIGATION_BAR = 19;
132 
133     static final int LAST_TYPE = ITYPE_EXTRA_NAVIGATION_BAR;
134     public static final int SIZE = LAST_TYPE + 1;
135 
136     // Derived types
137 
138     /** A shelf is the same as the navigation bar. */
139     public static final int ITYPE_SHELF = ITYPE_NAVIGATION_BAR;
140 
141     @Retention(RetentionPolicy.SOURCE)
142     @IntDef(prefix = "IINSETS_SIDE", value = {
143             ISIDE_LEFT,
144             ISIDE_TOP,
145             ISIDE_RIGHT,
146             ISIDE_BOTTOM,
147             ISIDE_FLOATING,
148             ISIDE_UNKNOWN
149     })
150     public @interface InternalInsetsSide {}
151     static final int ISIDE_LEFT = 0;
152     static final int ISIDE_TOP = 1;
153     static final int ISIDE_RIGHT = 2;
154     static final int ISIDE_BOTTOM = 3;
155     static final int ISIDE_FLOATING = 4;
156     static final int ISIDE_UNKNOWN = 5;
157 
158     private InsetsSource[] mSources = new InsetsSource[SIZE];
159 
160     /**
161      * The frame of the display these sources are relative to.
162      */
163     private final Rect mDisplayFrame = new Rect();
164 
InsetsState()165     public InsetsState() {
166     }
167 
InsetsState(InsetsState copy)168     public InsetsState(InsetsState copy) {
169         set(copy);
170     }
171 
InsetsState(InsetsState copy, boolean copySources)172     public InsetsState(InsetsState copy, boolean copySources) {
173         set(copy, copySources);
174     }
175 
176     /**
177      * Calculates {@link WindowInsets} based on the current source configuration.
178      *
179      * @param frame The frame to calculate the insets relative to.
180      * @param ignoringVisibilityState {@link InsetsState} used to calculate
181      *        {@link WindowInsets#getInsetsIgnoringVisibility(int)} information, or pass
182      *        {@code null} to use this state to calculate that information.
183      * @return The calculated insets.
184      */
calculateInsets(Rect frame, @Nullable InsetsState ignoringVisibilityState, boolean isScreenRound, boolean alwaysConsumeSystemBars, DisplayCutout cutout, int legacySoftInputMode, int legacyWindowFlags, int legacySystemUiFlags, @Nullable @InternalInsetsSide SparseIntArray typeSideMap)185     public WindowInsets calculateInsets(Rect frame, @Nullable InsetsState ignoringVisibilityState,
186             boolean isScreenRound, boolean alwaysConsumeSystemBars, DisplayCutout cutout,
187             int legacySoftInputMode, int legacyWindowFlags, int legacySystemUiFlags,
188             @Nullable @InternalInsetsSide SparseIntArray typeSideMap) {
189         Insets[] typeInsetsMap = new Insets[Type.SIZE];
190         Insets[] typeMaxInsetsMap = new Insets[Type.SIZE];
191         boolean[] typeVisibilityMap = new boolean[SIZE];
192         final Rect relativeFrame = new Rect(frame);
193         final Rect relativeFrameMax = new Rect(frame);
194         for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
195             InsetsSource source = mSources[type];
196             if (source == null) {
197                 int index = indexOf(toPublicType(type));
198                 if (typeInsetsMap[index] == null) {
199                     typeInsetsMap[index] = Insets.NONE;
200                 }
201                 continue;
202             }
203 
204             boolean skipNonImeInImeMode = ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_IME
205                     && source.getType() != ITYPE_IME;
206             boolean skipSystemBars = ViewRootImpl.sNewInsetsMode != NEW_INSETS_MODE_FULL
207                     && (type == ITYPE_STATUS_BAR || type == ITYPE_NAVIGATION_BAR);
208             boolean skipLegacyTypes = ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_NONE
209                     && (type == ITYPE_STATUS_BAR || type == ITYPE_NAVIGATION_BAR
210                             || type == ITYPE_IME);
211             if (skipSystemBars || skipLegacyTypes || skipNonImeInImeMode) {
212                 typeVisibilityMap[indexOf(toPublicType(type))] = source.isVisible();
213                 continue;
214             }
215 
216             processSource(source, relativeFrame, false /* ignoreVisibility */, typeInsetsMap,
217                     typeSideMap, typeVisibilityMap);
218 
219             // IME won't be reported in max insets as the size depends on the EditorInfo of the IME
220             // target.
221             if (source.getType() != ITYPE_IME) {
222                 InsetsSource ignoringVisibilitySource = ignoringVisibilityState != null
223                         ? ignoringVisibilityState.getSource(type)
224                         : source;
225                 if (ignoringVisibilitySource == null) {
226                     continue;
227                 }
228                 processSource(ignoringVisibilitySource, relativeFrameMax,
229                         true /* ignoreVisibility */, typeMaxInsetsMap, null /* typeSideMap */,
230                         null /* typeVisibilityMap */);
231             }
232         }
233         final int softInputAdjustMode = legacySoftInputMode & SOFT_INPUT_MASK_ADJUST;
234 
235         @InsetsType int compatInsetsTypes = systemBars() | displayCutout();
236         if (softInputAdjustMode == SOFT_INPUT_ADJUST_RESIZE) {
237             compatInsetsTypes |= ime();
238         }
239         if ((legacyWindowFlags & FLAG_FULLSCREEN) != 0) {
240             compatInsetsTypes &= ~statusBars();
241         }
242 
243         return new WindowInsets(typeInsetsMap, typeMaxInsetsMap, typeVisibilityMap, isScreenRound,
244                 alwaysConsumeSystemBars, cutout, compatInsetsTypes,
245                 sNewInsetsMode == NEW_INSETS_MODE_FULL
246                         && (legacySystemUiFlags & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0);
247     }
248 
calculateVisibleInsets(Rect frame, @SoftInputModeFlags int softInputMode)249     public Rect calculateVisibleInsets(Rect frame, @SoftInputModeFlags int softInputMode) {
250         Insets insets = Insets.NONE;
251         for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
252             InsetsSource source = mSources[type];
253             if (source == null) {
254                 continue;
255             }
256             if (sNewInsetsMode != NEW_INSETS_MODE_FULL && type != ITYPE_IME) {
257                 continue;
258             }
259 
260             // Ignore everything that's not a system bar or IME.
261             int publicType = InsetsState.toPublicType(type);
262             if (!isVisibleInsetsType(publicType, softInputMode)) {
263                 continue;
264             }
265             insets = Insets.max(source.calculateVisibleInsets(frame), insets);
266         }
267         return insets.toRect();
268     }
269 
270     /**
271      * Calculate which insets *cannot* be controlled, because the frame does not cover the
272      * respective side of the inset.
273      *
274      * If the frame of our window doesn't cover the entire inset, the control API makes very
275      * little sense, as we don't deal with negative insets.
276      */
277     @InsetsType
calculateUncontrollableInsetsFromFrame(Rect frame)278     public int calculateUncontrollableInsetsFromFrame(Rect frame) {
279         int blocked = 0;
280         for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
281             InsetsSource source = mSources[type];
282             if (source == null) {
283                 continue;
284             }
285             if (!canControlSide(frame, getInsetSide(
286                     source.calculateInsets(frame, true /* ignoreVisibility */)))) {
287                 blocked |= toPublicType(type);
288             }
289         }
290         return blocked;
291     }
292 
canControlSide(Rect frame, int side)293     private boolean canControlSide(Rect frame, int side) {
294         switch (side) {
295             case ISIDE_LEFT:
296             case ISIDE_RIGHT:
297                 return frame.left == mDisplayFrame.left && frame.right == mDisplayFrame.right;
298             case ISIDE_TOP:
299             case ISIDE_BOTTOM:
300                 return frame.top == mDisplayFrame.top && frame.bottom == mDisplayFrame.bottom;
301             case ISIDE_FLOATING:
302                 return true;
303             default:
304                 return false;
305         }
306     }
307 
processSource(InsetsSource source, Rect relativeFrame, boolean ignoreVisibility, Insets[] typeInsetsMap, @Nullable @InternalInsetsSide SparseIntArray typeSideMap, @Nullable boolean[] typeVisibilityMap)308     private void processSource(InsetsSource source, Rect relativeFrame, boolean ignoreVisibility,
309             Insets[] typeInsetsMap, @Nullable @InternalInsetsSide SparseIntArray typeSideMap,
310             @Nullable boolean[] typeVisibilityMap) {
311         Insets insets = source.calculateInsets(relativeFrame, ignoreVisibility);
312 
313         int type = toPublicType(source.getType());
314         processSourceAsPublicType(source, typeInsetsMap, typeSideMap, typeVisibilityMap,
315                 insets, type);
316 
317         if (type == MANDATORY_SYSTEM_GESTURES) {
318             // Mandatory system gestures are also system gestures.
319             // TODO: find a way to express this more generally. One option would be to define
320             //       Type.systemGestureInsets() as NORMAL | MANDATORY, but then we lose the
321             //       ability to set systemGestureInsets() independently from
322             //       mandatorySystemGestureInsets() in the Builder.
323             processSourceAsPublicType(source, typeInsetsMap, typeSideMap, typeVisibilityMap,
324                     insets, SYSTEM_GESTURES);
325         }
326     }
327 
processSourceAsPublicType(InsetsSource source, Insets[] typeInsetsMap, @InternalInsetsSide @Nullable SparseIntArray typeSideMap, @Nullable boolean[] typeVisibilityMap, Insets insets, int type)328     private void processSourceAsPublicType(InsetsSource source, Insets[] typeInsetsMap,
329             @InternalInsetsSide @Nullable SparseIntArray typeSideMap,
330             @Nullable boolean[] typeVisibilityMap, Insets insets, int type) {
331         int index = indexOf(type);
332         Insets existing = typeInsetsMap[index];
333         if (existing == null) {
334             typeInsetsMap[index] = insets;
335         } else {
336             typeInsetsMap[index] = Insets.max(existing, insets);
337         }
338 
339         if (typeVisibilityMap != null) {
340             typeVisibilityMap[index] = source.isVisible();
341         }
342 
343         if (typeSideMap != null) {
344             @InternalInsetsSide int insetSide = getInsetSide(insets);
345             if (insetSide != ISIDE_UNKNOWN) {
346                 typeSideMap.put(source.getType(), insetSide);
347             }
348         }
349     }
350 
351     /**
352      * Retrieves the side for a certain {@code insets}. It is required that only one field l/t/r/b
353      * is set in order that this method returns a meaningful result.
354      */
getInsetSide(Insets insets)355     private @InternalInsetsSide int getInsetSide(Insets insets) {
356         if (Insets.NONE.equals(insets)) {
357             return ISIDE_FLOATING;
358         }
359         if (insets.left != 0) {
360             return ISIDE_LEFT;
361         }
362         if (insets.top != 0) {
363             return ISIDE_TOP;
364         }
365         if (insets.right != 0) {
366             return ISIDE_RIGHT;
367         }
368         if (insets.bottom != 0) {
369             return ISIDE_BOTTOM;
370         }
371         return ISIDE_UNKNOWN;
372     }
373 
getSource(@nternalInsetsType int type)374     public InsetsSource getSource(@InternalInsetsType int type) {
375         InsetsSource source = mSources[type];
376         if (source != null) {
377             return source;
378         }
379         source = new InsetsSource(type);
380         mSources[type] = source;
381         return source;
382     }
383 
peekSource(@nternalInsetsType int type)384     public @Nullable InsetsSource peekSource(@InternalInsetsType int type) {
385         return mSources[type];
386     }
387 
hasSources()388     public boolean hasSources() {
389         for (int i = 0; i < SIZE; i++) {
390             if (mSources[i] != null) {
391                 return true;
392             }
393         }
394         return false;
395     }
396 
397     /**
398      * Returns the source visibility or the default visibility if the source doesn't exist. This is
399      * useful if when treating this object as a request.
400      *
401      * @param type The {@link InternalInsetsType} to query.
402      * @return {@code true} if the source is visible or the type is default visible and the source
403      *         doesn't exist.
404      */
getSourceOrDefaultVisibility(@nternalInsetsType int type)405     public boolean getSourceOrDefaultVisibility(@InternalInsetsType int type) {
406         final InsetsSource source = mSources[type];
407         return source != null ? source.isVisible() : getDefaultVisibility(type);
408     }
409 
setDisplayFrame(Rect frame)410     public void setDisplayFrame(Rect frame) {
411         mDisplayFrame.set(frame);
412     }
413 
getDisplayFrame()414     public Rect getDisplayFrame() {
415         return mDisplayFrame;
416     }
417 
418     /**
419      * Modifies the state of this class to exclude a certain type to make it ready for dispatching
420      * to the client.
421      *
422      * @param type The {@link InternalInsetsType} of the source to remove
423      */
removeSource(@nternalInsetsType int type)424     public void removeSource(@InternalInsetsType int type) {
425         mSources[type] = null;
426     }
427 
428     /**
429      * A shortcut for setting the visibility of the source.
430      *
431      * @param type The {@link InternalInsetsType} of the source to set the visibility
432      * @param visible {@code true} for visible
433      */
setSourceVisible(@nternalInsetsType int type, boolean visible)434     public void setSourceVisible(@InternalInsetsType int type, boolean visible) {
435         InsetsSource source = mSources[type];
436         if (source != null) {
437             source.setVisible(visible);
438         }
439     }
440 
set(InsetsState other)441     public void set(InsetsState other) {
442         set(other, false /* copySources */);
443     }
444 
set(InsetsState other, boolean copySources)445     public void set(InsetsState other, boolean copySources) {
446         mDisplayFrame.set(other.mDisplayFrame);
447         if (copySources) {
448             for (int i = 0; i < SIZE; i++) {
449                 InsetsSource source = other.mSources[i];
450                 mSources[i] = source != null ? new InsetsSource(source) : null;
451             }
452         } else {
453             for (int i = 0; i < SIZE; i++) {
454                 mSources[i] = other.mSources[i];
455             }
456         }
457     }
458 
addSource(InsetsSource source)459     public void addSource(InsetsSource source) {
460         mSources[source.getType()] = source;
461     }
462 
toInternalType(@nsetsType int types)463     public static @InternalInsetsType ArraySet<Integer> toInternalType(@InsetsType int types) {
464         final ArraySet<Integer> result = new ArraySet<>();
465         if ((types & Type.STATUS_BARS) != 0) {
466             result.add(ITYPE_STATUS_BAR);
467             result.add(ITYPE_CLIMATE_BAR);
468         }
469         if ((types & Type.NAVIGATION_BARS) != 0) {
470             result.add(ITYPE_NAVIGATION_BAR);
471             result.add(ITYPE_EXTRA_NAVIGATION_BAR);
472         }
473         if ((types & Type.CAPTION_BAR) != 0) {
474             result.add(ITYPE_CAPTION_BAR);
475         }
476         if ((types & Type.DISPLAY_CUTOUT) != 0) {
477             result.add(ITYPE_LEFT_DISPLAY_CUTOUT);
478             result.add(ITYPE_TOP_DISPLAY_CUTOUT);
479             result.add(ITYPE_RIGHT_DISPLAY_CUTOUT);
480             result.add(ITYPE_BOTTOM_DISPLAY_CUTOUT);
481         }
482         if ((types & Type.IME) != 0) {
483             result.add(ITYPE_IME);
484         }
485         return result;
486     }
487 
488     /**
489      * Converting a internal type to the public type.
490      * @param type internal insets type, {@code InternalInsetsType}.
491      * @return public insets type, {@code Type.InsetsType}.
492      */
toPublicType(@nternalInsetsType int type)493     public static @Type.InsetsType int toPublicType(@InternalInsetsType int type) {
494         switch (type) {
495             case ITYPE_STATUS_BAR:
496             case ITYPE_CLIMATE_BAR:
497                 return Type.STATUS_BARS;
498             case ITYPE_NAVIGATION_BAR:
499             case ITYPE_EXTRA_NAVIGATION_BAR:
500                 return Type.NAVIGATION_BARS;
501             case ITYPE_CAPTION_BAR:
502                 return Type.CAPTION_BAR;
503             case ITYPE_IME:
504                 return Type.IME;
505             case ITYPE_TOP_GESTURES:
506             case ITYPE_BOTTOM_GESTURES:
507             case ITYPE_TOP_MANDATORY_GESTURES:
508             case ITYPE_BOTTOM_MANDATORY_GESTURES:
509             case ITYPE_LEFT_MANDATORY_GESTURES:
510             case ITYPE_RIGHT_MANDATORY_GESTURES:
511                 return Type.MANDATORY_SYSTEM_GESTURES;
512             case ITYPE_LEFT_GESTURES:
513             case ITYPE_RIGHT_GESTURES:
514                 return Type.SYSTEM_GESTURES;
515             case ITYPE_TOP_TAPPABLE_ELEMENT:
516             case ITYPE_BOTTOM_TAPPABLE_ELEMENT:
517                 return Type.TAPPABLE_ELEMENT;
518             case ITYPE_LEFT_DISPLAY_CUTOUT:
519             case ITYPE_TOP_DISPLAY_CUTOUT:
520             case ITYPE_RIGHT_DISPLAY_CUTOUT:
521             case ITYPE_BOTTOM_DISPLAY_CUTOUT:
522                 return Type.DISPLAY_CUTOUT;
523             default:
524                 throw new IllegalArgumentException("Unknown type: " + type);
525         }
526     }
527 
getDefaultVisibility(@nternalInsetsType int type)528     public static boolean getDefaultVisibility(@InternalInsetsType int type) {
529         return type != ITYPE_IME;
530     }
531 
containsType(@nternalInsetsType int[] types, @InternalInsetsType int type)532     public static boolean containsType(@InternalInsetsType int[] types,
533             @InternalInsetsType int type) {
534         if (types == null) {
535             return false;
536         }
537         for (int t : types) {
538             if (t == type) {
539                 return true;
540             }
541         }
542         return false;
543     }
544 
dump(String prefix, PrintWriter pw)545     public void dump(String prefix, PrintWriter pw) {
546         pw.println(prefix + "InsetsState");
547         for (int i = 0; i < SIZE; i++) {
548             InsetsSource source = mSources[i];
549             if (source == null) continue;
550             source.dump(prefix + "  ", pw);
551         }
552     }
553 
typeToString(@nternalInsetsType int type)554     public static String typeToString(@InternalInsetsType int type) {
555         switch (type) {
556             case ITYPE_STATUS_BAR:
557                 return "ITYPE_STATUS_BAR";
558             case ITYPE_NAVIGATION_BAR:
559                 return "ITYPE_NAVIGATION_BAR";
560             case ITYPE_CAPTION_BAR:
561                 return "ITYPE_CAPTION_BAR";
562             case ITYPE_TOP_GESTURES:
563                 return "ITYPE_TOP_GESTURES";
564             case ITYPE_BOTTOM_GESTURES:
565                 return "ITYPE_BOTTOM_GESTURES";
566             case ITYPE_LEFT_GESTURES:
567                 return "ITYPE_LEFT_GESTURES";
568             case ITYPE_RIGHT_GESTURES:
569                 return "ITYPE_RIGHT_GESTURES";
570             case ITYPE_TOP_MANDATORY_GESTURES:
571                 return "ITYPE_TOP_MANDATORY_GESTURES";
572             case ITYPE_BOTTOM_MANDATORY_GESTURES:
573                 return "ITYPE_BOTTOM_MANDATORY_GESTURES";
574             case ITYPE_LEFT_MANDATORY_GESTURES:
575                 return "ITYPE_LEFT_MANDATORY_GESTURES";
576             case ITYPE_RIGHT_MANDATORY_GESTURES:
577                 return "ITYPE_RIGHT_MANDATORY_GESTURES";
578             case ITYPE_TOP_TAPPABLE_ELEMENT:
579                 return "ITYPE_TOP_TAPPABLE_ELEMENT";
580             case ITYPE_BOTTOM_TAPPABLE_ELEMENT:
581                 return "ITYPE_BOTTOM_TAPPABLE_ELEMENT";
582             case ITYPE_LEFT_DISPLAY_CUTOUT:
583                 return "ITYPE_LEFT_DISPLAY_CUTOUT";
584             case ITYPE_TOP_DISPLAY_CUTOUT:
585                 return "ITYPE_TOP_DISPLAY_CUTOUT";
586             case ITYPE_RIGHT_DISPLAY_CUTOUT:
587                 return "ITYPE_RIGHT_DISPLAY_CUTOUT";
588             case ITYPE_BOTTOM_DISPLAY_CUTOUT:
589                 return "ITYPE_BOTTOM_DISPLAY_CUTOUT";
590             case ITYPE_IME:
591                 return "ITYPE_IME";
592             case ITYPE_CLIMATE_BAR:
593                 return "ITYPE_CLIMATE_BAR";
594             case ITYPE_EXTRA_NAVIGATION_BAR:
595                 return "ITYPE_EXTRA_NAVIGATION_BAR";
596             default:
597                 return "ITYPE_UNKNOWN_" + type;
598         }
599     }
600 
601     @Override
equals(Object o)602     public boolean equals(Object o) {
603         return equals(o, false, false);
604     }
605 
606     /**
607      * An equals method can exclude the caption insets. This is useful because we assemble the
608      * caption insets information on the client side, and when we communicate with server, it's
609      * excluded.
610      * @param excludingCaptionInsets {@code true} if we want to compare two InsetsState objects but
611      *                                           ignore the caption insets source value.
612      * @param excludeInvisibleImeFrames If {@link #ITYPE_IME} frames should be ignored when IME is
613      *                                  not visible.
614      * @return {@code true} if the two InsetsState objects are equal, {@code false} otherwise.
615      */
616     @VisibleForTesting
equals(Object o, boolean excludingCaptionInsets, boolean excludeInvisibleImeFrames)617     public boolean equals(Object o, boolean excludingCaptionInsets,
618             boolean excludeInvisibleImeFrames) {
619         if (this == o) { return true; }
620         if (o == null || getClass() != o.getClass()) { return false; }
621 
622         InsetsState state = (InsetsState) o;
623 
624         if (!mDisplayFrame.equals(state.mDisplayFrame)) {
625             return false;
626         }
627         for (int i = 0; i < SIZE; i++) {
628             if (excludingCaptionInsets) {
629                 if (i == ITYPE_CAPTION_BAR) continue;
630             }
631             InsetsSource source = mSources[i];
632             InsetsSource otherSource = state.mSources[i];
633             if (source == null && otherSource == null) {
634                 continue;
635             }
636             if (source != null && otherSource == null || source == null && otherSource != null) {
637                 return false;
638             }
639             if (!otherSource.equals(source, excludeInvisibleImeFrames)) {
640                 return false;
641             }
642         }
643         return true;
644     }
645 
646     @Override
hashCode()647     public int hashCode() {
648         return Objects.hash(mDisplayFrame, Arrays.hashCode(mSources));
649     }
650 
InsetsState(Parcel in)651     public InsetsState(Parcel in) {
652         readFromParcel(in);
653     }
654 
655     @Override
describeContents()656     public int describeContents() {
657         return 0;
658     }
659 
660     @Override
writeToParcel(Parcel dest, int flags)661     public void writeToParcel(Parcel dest, int flags) {
662         dest.writeParcelable(mDisplayFrame, flags);
663         dest.writeParcelableArray(mSources, 0);
664     }
665 
666     public static final @android.annotation.NonNull Creator<InsetsState> CREATOR = new Creator<InsetsState>() {
667 
668         public InsetsState createFromParcel(Parcel in) {
669             return new InsetsState(in);
670         }
671 
672         public InsetsState[] newArray(int size) {
673             return new InsetsState[size];
674         }
675     };
676 
readFromParcel(Parcel in)677     public void readFromParcel(Parcel in) {
678         mDisplayFrame.set(in.readParcelable(null /* loader */));
679         mSources = in.readParcelableArray(null, InsetsSource.class);
680     }
681 
682     @Override
toString()683     public String toString() {
684         StringJoiner joiner = new StringJoiner(", ");
685         for (int i = 0; i < SIZE; i++) {
686             InsetsSource source = mSources[i];
687             if (source != null) {
688                 joiner.add(source.toString());
689             }
690         }
691         return "InsetsState: {"
692                 + "mDisplayFrame=" + mDisplayFrame
693                 + ", mSources= { " + joiner
694                 + " }";
695     }
696 }
697 
698