• 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.InsetsStateProto.DISPLAY_CUTOUT;
20 import static android.view.InsetsStateProto.DISPLAY_FRAME;
21 import static android.view.InsetsStateProto.SOURCES;
22 import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
23 import static android.view.WindowInsets.Type.displayCutout;
24 import static android.view.WindowInsets.Type.ime;
25 import static android.view.WindowInsets.Type.indexOf;
26 import static android.view.WindowInsets.Type.isVisibleInsetsType;
27 import static android.view.WindowInsets.Type.statusBars;
28 import static android.view.WindowInsets.Type.systemBars;
29 import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
30 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
31 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
32 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
33 import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
34 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
35 
36 import android.annotation.IntDef;
37 import android.annotation.NonNull;
38 import android.annotation.Nullable;
39 import android.app.WindowConfiguration;
40 import android.graphics.Insets;
41 import android.graphics.Rect;
42 import android.os.Parcel;
43 import android.os.Parcelable;
44 import android.util.ArraySet;
45 import android.util.SparseIntArray;
46 import android.util.proto.ProtoOutputStream;
47 import android.view.WindowInsets.Type;
48 import android.view.WindowInsets.Type.InsetsType;
49 import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
50 
51 import com.android.internal.annotations.VisibleForTesting;
52 
53 import java.io.PrintWriter;
54 import java.lang.annotation.Retention;
55 import java.lang.annotation.RetentionPolicy;
56 import java.util.Arrays;
57 import java.util.Objects;
58 import java.util.StringJoiner;
59 
60 /**
61  * Holder for state of system windows that cause window insets for all other windows in the system.
62  * @hide
63  */
64 public class InsetsState implements Parcelable {
65 
66     /**
67      * Internal representation of inset source types. This is different from the public API in
68      * {@link WindowInsets.Type} as one type from the public API might indicate multiple windows
69      * at the same time.
70      */
71     @Retention(RetentionPolicy.SOURCE)
72     @IntDef(prefix = "ITYPE", value = {
73             ITYPE_STATUS_BAR,
74             ITYPE_NAVIGATION_BAR,
75             ITYPE_CAPTION_BAR,
76             ITYPE_TOP_GESTURES,
77             ITYPE_BOTTOM_GESTURES,
78             ITYPE_LEFT_GESTURES,
79             ITYPE_RIGHT_GESTURES,
80             ITYPE_TOP_MANDATORY_GESTURES,
81             ITYPE_BOTTOM_MANDATORY_GESTURES,
82             ITYPE_LEFT_MANDATORY_GESTURES,
83             ITYPE_RIGHT_MANDATORY_GESTURES,
84             ITYPE_TOP_TAPPABLE_ELEMENT,
85             ITYPE_BOTTOM_TAPPABLE_ELEMENT,
86             ITYPE_LEFT_DISPLAY_CUTOUT,
87             ITYPE_TOP_DISPLAY_CUTOUT,
88             ITYPE_RIGHT_DISPLAY_CUTOUT,
89             ITYPE_BOTTOM_DISPLAY_CUTOUT,
90             ITYPE_IME,
91             ITYPE_CLIMATE_BAR,
92             ITYPE_EXTRA_NAVIGATION_BAR
93     })
94     public @interface InternalInsetsType {}
95 
96     /**
97      * Special value to be used to by methods returning an {@link InternalInsetsType} to indicate
98      * that the objects/parameters aren't associated with an {@link InternalInsetsType}
99      */
100     public static final int ITYPE_INVALID = -1;
101 
102     static final int FIRST_TYPE = 0;
103 
104     public static final int ITYPE_STATUS_BAR = FIRST_TYPE;
105     public static final int ITYPE_NAVIGATION_BAR = 1;
106     public static final int ITYPE_CAPTION_BAR = 2;
107 
108     public static final int ITYPE_TOP_GESTURES = 3;
109     public static final int ITYPE_BOTTOM_GESTURES = 4;
110     public static final int ITYPE_LEFT_GESTURES = 5;
111     public static final int ITYPE_RIGHT_GESTURES = 6;
112 
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_LEFT_DISPLAY_CUTOUT = 11;
119     public static final int ITYPE_TOP_DISPLAY_CUTOUT = 12;
120     public static final int ITYPE_RIGHT_DISPLAY_CUTOUT = 13;
121     public static final int ITYPE_BOTTOM_DISPLAY_CUTOUT = 14;
122 
123     public static final int ITYPE_LEFT_TAPPABLE_ELEMENT = 15;
124     public static final int ITYPE_TOP_TAPPABLE_ELEMENT = 16;
125     public static final int ITYPE_RIGHT_TAPPABLE_ELEMENT = 17;
126     public static final int ITYPE_BOTTOM_TAPPABLE_ELEMENT = 18;
127 
128     /** Input method window. */
129     public static final int ITYPE_IME = 19;
130 
131     /** Additional system decorations inset type. */
132     public static final int ITYPE_CLIMATE_BAR = 20;
133     public static final int ITYPE_EXTRA_NAVIGATION_BAR = 21;
134 
135     static final int LAST_TYPE = ITYPE_EXTRA_NAVIGATION_BAR;
136     public static final int SIZE = LAST_TYPE + 1;
137 
138     // Derived types
139 
140     /** A shelf is the same as the navigation bar. */
141     public static final int ITYPE_SHELF = ITYPE_NAVIGATION_BAR;
142 
143     @Retention(RetentionPolicy.SOURCE)
144     @IntDef(prefix = "IINSETS_SIDE", value = {
145             ISIDE_LEFT,
146             ISIDE_TOP,
147             ISIDE_RIGHT,
148             ISIDE_BOTTOM,
149             ISIDE_FLOATING,
150             ISIDE_UNKNOWN
151     })
152     public @interface InternalInsetsSide {}
153     static final int ISIDE_LEFT = 0;
154     static final int ISIDE_TOP = 1;
155     static final int ISIDE_RIGHT = 2;
156     static final int ISIDE_BOTTOM = 3;
157     static final int ISIDE_FLOATING = 4;
158     static final int ISIDE_UNKNOWN = 5;
159 
160     private final InsetsSource[] mSources = new InsetsSource[SIZE];
161 
162     /**
163      * The frame of the display these sources are relative to.
164      */
165     private final Rect mDisplayFrame = new Rect();
166 
167     /** The area cut from the display. */
168     private final DisplayCutout.ParcelableWrapper mDisplayCutout =
169             new DisplayCutout.ParcelableWrapper();
170 
171     /** The rounded corners on the display */
172     private RoundedCorners mRoundedCorners = RoundedCorners.NO_ROUNDED_CORNERS;
173 
174     /** The bounds of the Privacy Indicator */
175     private PrivacyIndicatorBounds mPrivacyIndicatorBounds =
176             new PrivacyIndicatorBounds();
177 
InsetsState()178     public InsetsState() {
179     }
180 
InsetsState(InsetsState copy)181     public InsetsState(InsetsState copy) {
182         set(copy);
183     }
184 
InsetsState(InsetsState copy, boolean copySources)185     public InsetsState(InsetsState copy, boolean copySources) {
186         set(copy, copySources);
187     }
188 
189     /**
190      * Calculates {@link WindowInsets} based on the current source configuration.
191      *
192      * @param frame The frame to calculate the insets relative to.
193      * @param ignoringVisibilityState {@link InsetsState} used to calculate
194      *        {@link WindowInsets#getInsetsIgnoringVisibility(int)} information, or pass
195      *        {@code null} to use this state to calculate that information.
196      * @return The calculated insets.
197      */
calculateInsets(Rect frame, @Nullable InsetsState ignoringVisibilityState, boolean isScreenRound, boolean alwaysConsumeSystemBars, int legacySoftInputMode, int legacyWindowFlags, int legacySystemUiFlags, int windowType, @WindowConfiguration.WindowingMode int windowingMode, @Nullable @InternalInsetsSide SparseIntArray typeSideMap)198     public WindowInsets calculateInsets(Rect frame, @Nullable InsetsState ignoringVisibilityState,
199             boolean isScreenRound, boolean alwaysConsumeSystemBars,
200             int legacySoftInputMode, int legacyWindowFlags, int legacySystemUiFlags,
201             int windowType, @WindowConfiguration.WindowingMode int windowingMode,
202             @Nullable @InternalInsetsSide SparseIntArray typeSideMap) {
203         Insets[] typeInsetsMap = new Insets[Type.SIZE];
204         Insets[] typeMaxInsetsMap = new Insets[Type.SIZE];
205         boolean[] typeVisibilityMap = new boolean[SIZE];
206         final Rect relativeFrame = new Rect(frame);
207         final Rect relativeFrameMax = new Rect(frame);
208         for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
209             InsetsSource source = mSources[type];
210             if (source == null) {
211                 int index = indexOf(toPublicType(type));
212                 if (typeInsetsMap[index] == null) {
213                     typeInsetsMap[index] = Insets.NONE;
214                 }
215                 continue;
216             }
217 
218             processSource(source, relativeFrame, false /* ignoreVisibility */, typeInsetsMap,
219                     typeSideMap, typeVisibilityMap);
220 
221             // IME won't be reported in max insets as the size depends on the EditorInfo of the IME
222             // target.
223             if (source.getType() != ITYPE_IME) {
224                 InsetsSource ignoringVisibilitySource = ignoringVisibilityState != null
225                         ? ignoringVisibilityState.getSource(type)
226                         : source;
227                 if (ignoringVisibilitySource == null) {
228                     continue;
229                 }
230                 processSource(ignoringVisibilitySource, relativeFrameMax,
231                         true /* ignoreVisibility */, typeMaxInsetsMap, null /* typeSideMap */,
232                         null /* typeVisibilityMap */);
233             }
234         }
235         final int softInputAdjustMode = legacySoftInputMode & SOFT_INPUT_MASK_ADJUST;
236 
237         @InsetsType int compatInsetsTypes = systemBars() | displayCutout();
238         if (softInputAdjustMode == SOFT_INPUT_ADJUST_RESIZE) {
239             compatInsetsTypes |= ime();
240         }
241         if ((legacyWindowFlags & FLAG_FULLSCREEN) != 0) {
242             compatInsetsTypes &= ~statusBars();
243         }
244         if (clearCompatInsets(windowType, legacyWindowFlags, windowingMode)) {
245             compatInsetsTypes = 0;
246         }
247 
248         return new WindowInsets(typeInsetsMap, typeMaxInsetsMap, typeVisibilityMap, isScreenRound,
249                 alwaysConsumeSystemBars, calculateRelativeCutout(frame),
250                 calculateRelativeRoundedCorners(frame),
251                 calculateRelativePrivacyIndicatorBounds(frame),
252                 compatInsetsTypes, (legacySystemUiFlags & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0);
253     }
254 
calculateRelativeCutout(Rect frame)255     private DisplayCutout calculateRelativeCutout(Rect frame) {
256         final DisplayCutout raw = mDisplayCutout.get();
257         if (mDisplayFrame.equals(frame)) {
258             return raw;
259         }
260         if (frame == null) {
261             return DisplayCutout.NO_CUTOUT;
262         }
263         final int insetLeft = frame.left - mDisplayFrame.left;
264         final int insetTop = frame.top - mDisplayFrame.top;
265         final int insetRight = mDisplayFrame.right - frame.right;
266         final int insetBottom = mDisplayFrame.bottom - frame.bottom;
267         if (insetLeft >= raw.getSafeInsetLeft()
268                 && insetTop >= raw.getSafeInsetTop()
269                 && insetRight >= raw.getSafeInsetRight()
270                 && insetBottom >= raw.getSafeInsetBottom()) {
271             return DisplayCutout.NO_CUTOUT;
272         }
273         return raw.inset(insetLeft, insetTop, insetRight, insetBottom);
274     }
275 
calculateRelativeRoundedCorners(Rect frame)276     private RoundedCorners calculateRelativeRoundedCorners(Rect frame) {
277         if (mDisplayFrame.equals(frame)) {
278             return mRoundedCorners;
279         }
280         if (frame == null) {
281             return RoundedCorners.NO_ROUNDED_CORNERS;
282         }
283         final int insetLeft = frame.left - mDisplayFrame.left;
284         final int insetTop = frame.top - mDisplayFrame.top;
285         final int insetRight = mDisplayFrame.right - frame.right;
286         final int insetBottom = mDisplayFrame.bottom - frame.bottom;
287         return mRoundedCorners.inset(insetLeft, insetTop, insetRight, insetBottom);
288     }
289 
calculateRelativePrivacyIndicatorBounds(Rect frame)290     private PrivacyIndicatorBounds calculateRelativePrivacyIndicatorBounds(Rect frame) {
291         if (mDisplayFrame.equals(frame)) {
292             return mPrivacyIndicatorBounds;
293         }
294         if (frame == null) {
295             return null;
296         }
297         final int insetLeft = frame.left - mDisplayFrame.left;
298         final int insetTop = frame.top - mDisplayFrame.top;
299         final int insetRight = mDisplayFrame.right - frame.right;
300         final int insetBottom = mDisplayFrame.bottom - frame.bottom;
301         return mPrivacyIndicatorBounds.inset(insetLeft, insetTop, insetRight, insetBottom);
302     }
303 
calculateInsets(Rect frame, @InsetsType int types, boolean ignoreVisibility)304     public Rect calculateInsets(Rect frame, @InsetsType int types, boolean ignoreVisibility) {
305         Insets insets = Insets.NONE;
306         for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
307             InsetsSource source = mSources[type];
308             if (source == null) {
309                 continue;
310             }
311             int publicType = InsetsState.toPublicType(type);
312             if ((publicType & types) == 0) {
313                 continue;
314             }
315             insets = Insets.max(source.calculateInsets(frame, ignoreVisibility), insets);
316         }
317         return insets.toRect();
318     }
319 
calculateVisibleInsets(Rect frame, @SoftInputModeFlags int softInputMode)320     public Rect calculateVisibleInsets(Rect frame, @SoftInputModeFlags int softInputMode) {
321         Insets insets = Insets.NONE;
322         for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
323             InsetsSource source = mSources[type];
324             if (source == null) {
325                 continue;
326             }
327 
328             // Ignore everything that's not a system bar or IME.
329             int publicType = InsetsState.toPublicType(type);
330             if (!isVisibleInsetsType(publicType, softInputMode)) {
331                 continue;
332             }
333             insets = Insets.max(source.calculateVisibleInsets(frame), insets);
334         }
335         return insets.toRect();
336     }
337 
338     /**
339      * Calculate which insets *cannot* be controlled, because the frame does not cover the
340      * respective side of the inset.
341      *
342      * If the frame of our window doesn't cover the entire inset, the control API makes very
343      * little sense, as we don't deal with negative insets.
344      */
345     @InsetsType
calculateUncontrollableInsetsFromFrame(Rect frame)346     public int calculateUncontrollableInsetsFromFrame(Rect frame) {
347         int blocked = 0;
348         for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
349             InsetsSource source = mSources[type];
350             if (source == null) {
351                 continue;
352             }
353             if (!canControlSide(frame, getInsetSide(
354                     source.calculateInsets(frame, true /* ignoreVisibility */)))) {
355                 blocked |= toPublicType(type);
356             }
357         }
358         return blocked;
359     }
360 
canControlSide(Rect frame, int side)361     private boolean canControlSide(Rect frame, int side) {
362         switch (side) {
363             case ISIDE_LEFT:
364             case ISIDE_RIGHT:
365                 return frame.left == mDisplayFrame.left && frame.right == mDisplayFrame.right;
366             case ISIDE_TOP:
367             case ISIDE_BOTTOM:
368                 return frame.top == mDisplayFrame.top && frame.bottom == mDisplayFrame.bottom;
369             case ISIDE_FLOATING:
370                 return true;
371             default:
372                 return false;
373         }
374     }
375 
processSource(InsetsSource source, Rect relativeFrame, boolean ignoreVisibility, Insets[] typeInsetsMap, @Nullable @InternalInsetsSide SparseIntArray typeSideMap, @Nullable boolean[] typeVisibilityMap)376     private void processSource(InsetsSource source, Rect relativeFrame, boolean ignoreVisibility,
377             Insets[] typeInsetsMap, @Nullable @InternalInsetsSide SparseIntArray typeSideMap,
378             @Nullable boolean[] typeVisibilityMap) {
379         Insets insets = source.calculateInsets(relativeFrame, ignoreVisibility);
380 
381         int type = toPublicType(source.getType());
382         processSourceAsPublicType(source, typeInsetsMap, typeSideMap, typeVisibilityMap,
383                 insets, type);
384 
385         if (type == Type.MANDATORY_SYSTEM_GESTURES) {
386             // Mandatory system gestures are also system gestures.
387             // TODO: find a way to express this more generally. One option would be to define
388             //       Type.systemGestureInsets() as NORMAL | MANDATORY, but then we lose the
389             //       ability to set systemGestureInsets() independently from
390             //       mandatorySystemGestureInsets() in the Builder.
391             processSourceAsPublicType(source, typeInsetsMap, typeSideMap, typeVisibilityMap,
392                     insets, Type.SYSTEM_GESTURES);
393         }
394     }
395 
processSourceAsPublicType(InsetsSource source, Insets[] typeInsetsMap, @InternalInsetsSide @Nullable SparseIntArray typeSideMap, @Nullable boolean[] typeVisibilityMap, Insets insets, int type)396     private void processSourceAsPublicType(InsetsSource source, Insets[] typeInsetsMap,
397             @InternalInsetsSide @Nullable SparseIntArray typeSideMap,
398             @Nullable boolean[] typeVisibilityMap, Insets insets, int type) {
399         int index = indexOf(type);
400         Insets existing = typeInsetsMap[index];
401         if (existing == null) {
402             typeInsetsMap[index] = insets;
403         } else {
404             typeInsetsMap[index] = Insets.max(existing, insets);
405         }
406 
407         if (typeVisibilityMap != null) {
408             typeVisibilityMap[index] = source.isVisible();
409         }
410 
411         if (typeSideMap != null) {
412             @InternalInsetsSide int insetSide = getInsetSide(insets);
413             if (insetSide != ISIDE_UNKNOWN) {
414                 typeSideMap.put(source.getType(), insetSide);
415             }
416         }
417     }
418 
419     /**
420      * Retrieves the side for a certain {@code insets}. It is required that only one field l/t/r/b
421      * is set in order that this method returns a meaningful result.
422      */
getInsetSide(Insets insets)423     static @InternalInsetsSide int getInsetSide(Insets insets) {
424         if (Insets.NONE.equals(insets)) {
425             return ISIDE_FLOATING;
426         }
427         if (insets.left != 0) {
428             return ISIDE_LEFT;
429         }
430         if (insets.top != 0) {
431             return ISIDE_TOP;
432         }
433         if (insets.right != 0) {
434             return ISIDE_RIGHT;
435         }
436         if (insets.bottom != 0) {
437             return ISIDE_BOTTOM;
438         }
439         return ISIDE_UNKNOWN;
440     }
441 
getSource(@nternalInsetsType int type)442     public InsetsSource getSource(@InternalInsetsType int type) {
443         InsetsSource source = mSources[type];
444         if (source != null) {
445             return source;
446         }
447         source = new InsetsSource(type);
448         mSources[type] = source;
449         return source;
450     }
451 
peekSource(@nternalInsetsType int type)452     public @Nullable InsetsSource peekSource(@InternalInsetsType int type) {
453         return mSources[type];
454     }
455 
456     /**
457      * Returns the source visibility or the default visibility if the source doesn't exist. This is
458      * useful if when treating this object as a request.
459      *
460      * @param type The {@link InternalInsetsType} to query.
461      * @return {@code true} if the source is visible or the type is default visible and the source
462      *         doesn't exist.
463      */
getSourceOrDefaultVisibility(@nternalInsetsType int type)464     public boolean getSourceOrDefaultVisibility(@InternalInsetsType int type) {
465         final InsetsSource source = mSources[type];
466         return source != null ? source.isVisible() : getDefaultVisibility(type);
467     }
468 
setDisplayFrame(Rect frame)469     public void setDisplayFrame(Rect frame) {
470         mDisplayFrame.set(frame);
471     }
472 
getDisplayFrame()473     public Rect getDisplayFrame() {
474         return mDisplayFrame;
475     }
476 
setDisplayCutout(DisplayCutout cutout)477     public void setDisplayCutout(DisplayCutout cutout) {
478         mDisplayCutout.set(cutout);
479     }
480 
getDisplayCutout()481     public DisplayCutout getDisplayCutout() {
482         return mDisplayCutout.get();
483     }
484 
setRoundedCorners(RoundedCorners roundedCorners)485     public void setRoundedCorners(RoundedCorners roundedCorners) {
486         mRoundedCorners = roundedCorners;
487     }
488 
getRoundedCorners()489     public RoundedCorners getRoundedCorners() {
490         return mRoundedCorners;
491     }
492 
setPrivacyIndicatorBounds(PrivacyIndicatorBounds bounds)493     public void setPrivacyIndicatorBounds(PrivacyIndicatorBounds bounds) {
494         mPrivacyIndicatorBounds = bounds;
495     }
496 
getPrivacyIndicatorBounds()497     public PrivacyIndicatorBounds getPrivacyIndicatorBounds() {
498         return mPrivacyIndicatorBounds;
499     }
500 
501     /**
502      * Modifies the state of this class to exclude a certain type to make it ready for dispatching
503      * to the client.
504      *
505      * @param type The {@link InternalInsetsType} of the source to remove
506      * @return {@code true} if this InsetsState was modified; {@code false} otherwise.
507      */
removeSource(@nternalInsetsType int type)508     public boolean removeSource(@InternalInsetsType int type) {
509         if (mSources[type] == null) {
510             return false;
511         }
512         mSources[type] = null;
513         return true;
514     }
515 
516     /**
517      * A shortcut for setting the visibility of the source.
518      *
519      * @param type The {@link InternalInsetsType} of the source to set the visibility
520      * @param visible {@code true} for visible
521      */
setSourceVisible(@nternalInsetsType int type, boolean visible)522     public void setSourceVisible(@InternalInsetsType int type, boolean visible) {
523         InsetsSource source = mSources[type];
524         if (source != null) {
525             source.setVisible(visible);
526         }
527     }
528 
529     /**
530      * Scales the frame and the visible frame (if there is one) of each source.
531      *
532      * @param scale the scale to be applied
533      */
scale(float scale)534     public void scale(float scale) {
535         mDisplayFrame.scale(scale);
536         mDisplayCutout.scale(scale);
537         mRoundedCorners = mRoundedCorners.scale(scale);
538         mPrivacyIndicatorBounds = mPrivacyIndicatorBounds.scale(scale);
539         for (int i = 0; i < SIZE; i++) {
540             final InsetsSource source = mSources[i];
541             if (source != null) {
542                 source.getFrame().scale(scale);
543                 final Rect visibleFrame = source.getVisibleFrame();
544                 if (visibleFrame != null) {
545                     visibleFrame.scale(scale);
546                 }
547             }
548         }
549     }
550 
set(InsetsState other)551     public void set(InsetsState other) {
552         set(other, false /* copySources */);
553     }
554 
set(InsetsState other, boolean copySources)555     public void set(InsetsState other, boolean copySources) {
556         mDisplayFrame.set(other.mDisplayFrame);
557         mDisplayCutout.set(other.mDisplayCutout);
558         mRoundedCorners = other.getRoundedCorners();
559         mPrivacyIndicatorBounds = other.getPrivacyIndicatorBounds();
560         if (copySources) {
561             for (int i = 0; i < SIZE; i++) {
562                 InsetsSource source = other.mSources[i];
563                 mSources[i] = source != null ? new InsetsSource(source) : null;
564             }
565         } else {
566             for (int i = 0; i < SIZE; i++) {
567                 mSources[i] = other.mSources[i];
568             }
569         }
570     }
571 
572     /**
573      * Sets the values from the other InsetsState. But for sources, only specific types of source
574      * would be set.
575      *
576      * @param other the other InsetsState.
577      * @param types the only types of sources would be set.
578      */
set(InsetsState other, @InsetsType int types)579     public void set(InsetsState other, @InsetsType int types) {
580         mDisplayFrame.set(other.mDisplayFrame);
581         mDisplayCutout.set(other.mDisplayCutout);
582         mRoundedCorners = other.getRoundedCorners();
583         mPrivacyIndicatorBounds = other.getPrivacyIndicatorBounds();
584         final ArraySet<Integer> t = toInternalType(types);
585         for (int i = t.size() - 1; i >= 0; i--) {
586             final int type = t.valueAt(i);
587             mSources[type] = other.mSources[type];
588         }
589     }
590 
addSource(InsetsSource source)591     public void addSource(InsetsSource source) {
592         mSources[source.getType()] = source;
593     }
594 
clearCompatInsets(int windowType, int windowFlags, int windowingMode)595     public static boolean clearCompatInsets(int windowType, int windowFlags, int windowingMode) {
596         return (windowFlags & FLAG_LAYOUT_NO_LIMITS) != 0
597                 && windowType != TYPE_WALLPAPER && windowType != TYPE_SYSTEM_ERROR
598                 && !WindowConfiguration.inMultiWindowMode(windowingMode);
599     }
600 
toInternalType(@nsetsType int types)601     public static @InternalInsetsType ArraySet<Integer> toInternalType(@InsetsType int types) {
602         final ArraySet<Integer> result = new ArraySet<>();
603         if ((types & Type.STATUS_BARS) != 0) {
604             result.add(ITYPE_STATUS_BAR);
605             result.add(ITYPE_CLIMATE_BAR);
606         }
607         if ((types & Type.NAVIGATION_BARS) != 0) {
608             result.add(ITYPE_NAVIGATION_BAR);
609             result.add(ITYPE_EXTRA_NAVIGATION_BAR);
610         }
611         if ((types & Type.CAPTION_BAR) != 0) {
612             result.add(ITYPE_CAPTION_BAR);
613         }
614         if ((types & Type.SYSTEM_GESTURES) != 0) {
615             result.add(ITYPE_LEFT_GESTURES);
616             result.add(ITYPE_TOP_GESTURES);
617             result.add(ITYPE_RIGHT_GESTURES);
618             result.add(ITYPE_BOTTOM_GESTURES);
619         }
620         if ((types & Type.MANDATORY_SYSTEM_GESTURES) != 0) {
621             result.add(ITYPE_LEFT_MANDATORY_GESTURES);
622             result.add(ITYPE_TOP_MANDATORY_GESTURES);
623             result.add(ITYPE_RIGHT_MANDATORY_GESTURES);
624             result.add(ITYPE_BOTTOM_MANDATORY_GESTURES);
625         }
626         if ((types & Type.DISPLAY_CUTOUT) != 0) {
627             result.add(ITYPE_LEFT_DISPLAY_CUTOUT);
628             result.add(ITYPE_TOP_DISPLAY_CUTOUT);
629             result.add(ITYPE_RIGHT_DISPLAY_CUTOUT);
630             result.add(ITYPE_BOTTOM_DISPLAY_CUTOUT);
631         }
632         if ((types & Type.IME) != 0) {
633             result.add(ITYPE_IME);
634         }
635         return result;
636     }
637 
638     /**
639      * Converting a internal type to the public type.
640      * @param type internal insets type, {@code InternalInsetsType}.
641      * @return public insets type, {@code Type.InsetsType}.
642      */
toPublicType(@nternalInsetsType int type)643     public static @Type.InsetsType int toPublicType(@InternalInsetsType int type) {
644         switch (type) {
645             case ITYPE_STATUS_BAR:
646             case ITYPE_CLIMATE_BAR:
647                 return Type.STATUS_BARS;
648             case ITYPE_NAVIGATION_BAR:
649             case ITYPE_EXTRA_NAVIGATION_BAR:
650                 return Type.NAVIGATION_BARS;
651             case ITYPE_CAPTION_BAR:
652                 return Type.CAPTION_BAR;
653             case ITYPE_IME:
654                 return Type.IME;
655             case ITYPE_TOP_MANDATORY_GESTURES:
656             case ITYPE_BOTTOM_MANDATORY_GESTURES:
657             case ITYPE_LEFT_MANDATORY_GESTURES:
658             case ITYPE_RIGHT_MANDATORY_GESTURES:
659                 return Type.MANDATORY_SYSTEM_GESTURES;
660             case ITYPE_TOP_GESTURES:
661             case ITYPE_BOTTOM_GESTURES:
662             case ITYPE_LEFT_GESTURES:
663             case ITYPE_RIGHT_GESTURES:
664                 return Type.SYSTEM_GESTURES;
665             case ITYPE_LEFT_TAPPABLE_ELEMENT:
666             case ITYPE_TOP_TAPPABLE_ELEMENT:
667             case ITYPE_RIGHT_TAPPABLE_ELEMENT:
668             case ITYPE_BOTTOM_TAPPABLE_ELEMENT:
669                 return Type.TAPPABLE_ELEMENT;
670             case ITYPE_LEFT_DISPLAY_CUTOUT:
671             case ITYPE_TOP_DISPLAY_CUTOUT:
672             case ITYPE_RIGHT_DISPLAY_CUTOUT:
673             case ITYPE_BOTTOM_DISPLAY_CUTOUT:
674                 return Type.DISPLAY_CUTOUT;
675             default:
676                 throw new IllegalArgumentException("Unknown type: " + type);
677         }
678     }
679 
getDefaultVisibility(@nternalInsetsType int type)680     public static boolean getDefaultVisibility(@InternalInsetsType int type) {
681         return type != ITYPE_IME;
682     }
683 
containsType(@nternalInsetsType int[] types, @InternalInsetsType int type)684     public static boolean containsType(@InternalInsetsType int[] types,
685             @InternalInsetsType int type) {
686         if (types == null) {
687             return false;
688         }
689         for (int t : types) {
690             if (t == type) {
691                 return true;
692             }
693         }
694         return false;
695     }
696 
dump(String prefix, PrintWriter pw)697     public void dump(String prefix, PrintWriter pw) {
698         final String newPrefix = prefix + "  ";
699         pw.println(prefix + "InsetsState");
700         pw.println(newPrefix + "mDisplayFrame=" + mDisplayFrame);
701         pw.println(newPrefix + "mDisplayCutout=" + mDisplayCutout.get());
702         pw.println(newPrefix + "mRoundedCorners=" + mRoundedCorners);
703         pw.println(newPrefix + "mPrivacyIndicatorBounds=" + mPrivacyIndicatorBounds);
704         for (int i = 0; i < SIZE; i++) {
705             InsetsSource source = mSources[i];
706             if (source == null) continue;
707             source.dump(newPrefix + "  ", pw);
708         }
709     }
710 
dumpDebug(ProtoOutputStream proto, long fieldId)711     void dumpDebug(ProtoOutputStream proto, long fieldId) {
712         final long token = proto.start(fieldId);
713         InsetsSource source = mSources[ITYPE_IME];
714         if (source != null) {
715             source.dumpDebug(proto, SOURCES);
716         }
717         mDisplayFrame.dumpDebug(proto, DISPLAY_FRAME);
718         mDisplayCutout.get().dumpDebug(proto, DISPLAY_CUTOUT);
719         proto.end(token);
720     }
721 
typeToString(@nternalInsetsType int type)722     public static String typeToString(@InternalInsetsType int type) {
723         switch (type) {
724             case ITYPE_STATUS_BAR:
725                 return "ITYPE_STATUS_BAR";
726             case ITYPE_NAVIGATION_BAR:
727                 return "ITYPE_NAVIGATION_BAR";
728             case ITYPE_CAPTION_BAR:
729                 return "ITYPE_CAPTION_BAR";
730             case ITYPE_TOP_GESTURES:
731                 return "ITYPE_TOP_GESTURES";
732             case ITYPE_BOTTOM_GESTURES:
733                 return "ITYPE_BOTTOM_GESTURES";
734             case ITYPE_LEFT_GESTURES:
735                 return "ITYPE_LEFT_GESTURES";
736             case ITYPE_RIGHT_GESTURES:
737                 return "ITYPE_RIGHT_GESTURES";
738             case ITYPE_TOP_MANDATORY_GESTURES:
739                 return "ITYPE_TOP_MANDATORY_GESTURES";
740             case ITYPE_BOTTOM_MANDATORY_GESTURES:
741                 return "ITYPE_BOTTOM_MANDATORY_GESTURES";
742             case ITYPE_LEFT_MANDATORY_GESTURES:
743                 return "ITYPE_LEFT_MANDATORY_GESTURES";
744             case ITYPE_RIGHT_MANDATORY_GESTURES:
745                 return "ITYPE_RIGHT_MANDATORY_GESTURES";
746             case ITYPE_LEFT_TAPPABLE_ELEMENT:
747                 return "ITYPE_LEFT_TAPPABLE_ELEMENT";
748             case ITYPE_TOP_TAPPABLE_ELEMENT:
749                 return "ITYPE_TOP_TAPPABLE_ELEMENT";
750             case ITYPE_RIGHT_TAPPABLE_ELEMENT:
751                 return "ITYPE_RIGHT_TAPPABLE_ELEMENT";
752             case ITYPE_BOTTOM_TAPPABLE_ELEMENT:
753                 return "ITYPE_BOTTOM_TAPPABLE_ELEMENT";
754             case ITYPE_LEFT_DISPLAY_CUTOUT:
755                 return "ITYPE_LEFT_DISPLAY_CUTOUT";
756             case ITYPE_TOP_DISPLAY_CUTOUT:
757                 return "ITYPE_TOP_DISPLAY_CUTOUT";
758             case ITYPE_RIGHT_DISPLAY_CUTOUT:
759                 return "ITYPE_RIGHT_DISPLAY_CUTOUT";
760             case ITYPE_BOTTOM_DISPLAY_CUTOUT:
761                 return "ITYPE_BOTTOM_DISPLAY_CUTOUT";
762             case ITYPE_IME:
763                 return "ITYPE_IME";
764             case ITYPE_CLIMATE_BAR:
765                 return "ITYPE_CLIMATE_BAR";
766             case ITYPE_EXTRA_NAVIGATION_BAR:
767                 return "ITYPE_EXTRA_NAVIGATION_BAR";
768             default:
769                 return "ITYPE_UNKNOWN_" + type;
770         }
771     }
772 
773     @Override
equals(@ullable Object o)774     public boolean equals(@Nullable Object o) {
775         return equals(o, false, false);
776     }
777 
778     /**
779      * An equals method can exclude the caption insets. This is useful because we assemble the
780      * caption insets information on the client side, and when we communicate with server, it's
781      * excluded.
782      * @param excludingCaptionInsets {@code true} if we want to compare two InsetsState objects but
783      *                                           ignore the caption insets source value.
784      * @param excludeInvisibleImeFrames If {@link #ITYPE_IME} frames should be ignored when IME is
785      *                                  not visible.
786      * @return {@code true} if the two InsetsState objects are equal, {@code false} otherwise.
787      */
788     @VisibleForTesting
equals(@ullable Object o, boolean excludingCaptionInsets, boolean excludeInvisibleImeFrames)789     public boolean equals(@Nullable Object o, boolean excludingCaptionInsets,
790             boolean excludeInvisibleImeFrames) {
791         if (this == o) { return true; }
792         if (o == null || getClass() != o.getClass()) { return false; }
793 
794         InsetsState state = (InsetsState) o;
795 
796         if (!mDisplayFrame.equals(state.mDisplayFrame)
797                 || !mDisplayCutout.equals(state.mDisplayCutout)
798                 || !mRoundedCorners.equals(state.mRoundedCorners)
799                 || !mPrivacyIndicatorBounds.equals(state.mPrivacyIndicatorBounds)) {
800             return false;
801         }
802         for (int i = 0; i < SIZE; i++) {
803             if (excludingCaptionInsets) {
804                 if (i == ITYPE_CAPTION_BAR) continue;
805             }
806             InsetsSource source = mSources[i];
807             InsetsSource otherSource = state.mSources[i];
808             if (source == null && otherSource == null) {
809                 continue;
810             }
811             if (source == null || otherSource == null) {
812                 return false;
813             }
814             if (!otherSource.equals(source, excludeInvisibleImeFrames)) {
815                 return false;
816             }
817         }
818         return true;
819     }
820 
821     @Override
hashCode()822     public int hashCode() {
823         return Objects.hash(mDisplayFrame, mDisplayCutout, Arrays.hashCode(mSources),
824                 mRoundedCorners, mPrivacyIndicatorBounds);
825     }
826 
InsetsState(Parcel in)827     public InsetsState(Parcel in) {
828         readFromParcel(in);
829     }
830 
831     @Override
describeContents()832     public int describeContents() {
833         return 0;
834     }
835 
836     @Override
writeToParcel(Parcel dest, int flags)837     public void writeToParcel(Parcel dest, int flags) {
838         mDisplayFrame.writeToParcel(dest, flags);
839         mDisplayCutout.writeToParcel(dest, flags);
840         dest.writeTypedArray(mSources, 0 /* parcelableFlags */);
841         dest.writeTypedObject(mRoundedCorners, flags);
842         dest.writeTypedObject(mPrivacyIndicatorBounds, flags);
843     }
844 
845     public static final @NonNull Creator<InsetsState> CREATOR = new Creator<InsetsState>() {
846 
847         public InsetsState createFromParcel(Parcel in) {
848             return new InsetsState(in);
849         }
850 
851         public InsetsState[] newArray(int size) {
852             return new InsetsState[size];
853         }
854     };
855 
readFromParcel(Parcel in)856     public void readFromParcel(Parcel in) {
857         mDisplayFrame.readFromParcel(in);
858         mDisplayCutout.readFromParcel(in);
859         in.readTypedArray(mSources, InsetsSource.CREATOR);
860         mRoundedCorners = in.readTypedObject(RoundedCorners.CREATOR);
861         mPrivacyIndicatorBounds = in.readTypedObject(PrivacyIndicatorBounds.CREATOR);
862     }
863 
864     @Override
toString()865     public String toString() {
866         StringJoiner joiner = new StringJoiner(", ");
867         for (int i = 0; i < SIZE; i++) {
868             InsetsSource source = mSources[i];
869             if (source != null) {
870                 joiner.add(source.toString());
871             }
872         }
873         return "InsetsState: {"
874                 + "mDisplayFrame=" + mDisplayFrame
875                 + ", mDisplayCutout=" + mDisplayCutout
876                 + ", mRoundedCorners=" + mRoundedCorners
877                 + ", mPrivacyIndicatorBounds=" + mPrivacyIndicatorBounds
878                 + ", mSources= { " + joiner
879                 + " }";
880     }
881 
toSourceVisibilityString()882     public @NonNull String toSourceVisibilityString() {
883         StringJoiner joiner = new StringJoiner(", ");
884         for (int i = 0; i < SIZE; i++) {
885             InsetsSource source = mSources[i];
886             if (source != null) {
887                 joiner.add(typeToString(i) + ": " + (source.isVisible() ? "visible" : "invisible"));
888             }
889         }
890         return joiner.toString();
891     }
892 }
893 
894