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