1 /*
2  * Copyright (C) 2022 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 android.annotation.IntRange;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.graphics.Insets;
23 import android.graphics.Rect;
24 import android.os.IBinder;
25 import android.os.Parcel;
26 import android.os.Parcelable;
27 import android.view.InsetsSource.Flags;
28 import android.view.WindowInsets.Type.InsetsType;
29 
30 import java.util.Arrays;
31 import java.util.Objects;
32 
33 /**
34  * Insets provided by a window.
35  *
36  * The insets frame will by default as the window frame size. If the providers are set, the
37  * calculation result based on the source size will be used as the insets frame.
38  *
39  * The InsetsFrameProvider should be self-contained. Nothing describing the window itself, such as
40  * contentInsets, visibleInsets, etc. won't affect the insets providing to other windows when this
41  * is set.
42  * @hide
43  */
44 public class InsetsFrameProvider implements Parcelable {
45 
46     /**
47      * Uses the display frame as the source.
48      */
49     public static final int SOURCE_DISPLAY = 0;
50 
51     /**
52      * Uses the window bounds as the source.
53      */
54     public static final int SOURCE_CONTAINER_BOUNDS = 1;
55 
56     /**
57      * Uses the window frame as the source.
58      */
59     public static final int SOURCE_FRAME = 2;
60 
61     /**
62      * Uses {@link #mArbitraryRectangle} as the source.
63      */
64     public static final int SOURCE_ARBITRARY_RECTANGLE = 3;
65 
66     private final int mId;
67 
68     /**
69      * The selection of the starting rectangle to be converted into source frame.
70      */
71     private int mSource = SOURCE_FRAME;
72 
73     /**
74      * This is used as the source frame only if SOURCE_ARBITRARY_RECTANGLE is applied.
75      */
76     private Rect mArbitraryRectangle;
77 
78     /**
79      * Modifies the starting rectangle selected by {@link #mSource}.
80      *
81      * For example, when the given source frame is (0, 0) - (100, 200), and the insetsSize is null,
82      * the source frame will be directly used as the final insets frame. If the insetsSize is set to
83      * (0, 0, 0, 50) instead, the insets frame will be a frame starting from the bottom side of the
84      * source frame with height of 50, i.e., (0, 150) - (100, 200).
85      */
86     private Insets mInsetsSize = null;
87 
88     /**
89      * Various behavioral options/flags. Default is none.
90      *
91      * @see Flags
92      */
93     private @Flags int mFlags;
94 
95     /**
96      * If null, the size set in insetsSize will be applied to all window types. If it contains
97      * element of some types, the insets reported to the window with that types will be overridden.
98      */
99     private InsetsSizeOverride[] mInsetsSizeOverrides = null;
100 
101     /**
102      * This field, if set, is indicating the insets needs to be at least the given size inside the
103      * display cutout safe area. This will be compared to the insets size calculated based on other
104      * attributes, and will be applied when this is larger. This is independent of the
105      * PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT in LayoutParams, as this is not going to change
106      * the layout of the window, but only change the insets frame. This can be applied to insets
107      * calculated based on all three source frames.
108      *
109      * Be cautious, this will not be in effect for the window types whose insets size is overridden.
110      */
111     private Insets mMinimalInsetsSizeInDisplayCutoutSafe = null;
112 
113     /**
114      * Indicates the bounding rectangles within the provided insets frame, in relative coordinates
115      * to the source frame.
116      */
117     private Rect[] mBoundingRects = null;
118 
119     /**
120      * Creates an InsetsFrameProvider which describes what frame an insets source should have.
121      *
122      * @param owner the owner of this provider. We might have multiple sources with the same type on
123      *              a display, this is used to identify them.
124      * @param index the index of this provider. An owner might provide multiple sources with the
125      *              same type, this is used to identify them.
126      *              The value must be in a range of [0, 2047].
127      * @param type the {@link InsetsType}.
128      * @see InsetsSource#createId(Object, int, int)
129      */
InsetsFrameProvider(Object owner, @IntRange(from = 0, to = 2047) int index, @InsetsType int type)130     public InsetsFrameProvider(Object owner, @IntRange(from = 0, to = 2047) int index,
131             @InsetsType int type) {
132         mId = InsetsSource.createId(owner, index, type);
133     }
134 
135     /**
136      * Returns an unique integer which identifies the insets source.
137      */
getId()138     public int getId() {
139         return mId;
140     }
141 
142     /**
143      * Returns the index specified in {@link #InsetsFrameProvider(IBinder, int, int)}.
144      */
getIndex()145     public int getIndex() {
146         return InsetsSource.getIndex(mId);
147     }
148 
149     /**
150      * Returns the {@link InsetsType} specified in {@link #InsetsFrameProvider(IBinder, int, int)}.
151      */
getType()152     public int getType() {
153         return InsetsSource.getType(mId);
154     }
155 
setSource(int source)156     public InsetsFrameProvider setSource(int source) {
157         mSource = source;
158         return this;
159     }
160 
getSource()161     public int getSource() {
162         return mSource;
163     }
164 
setFlags(@lags int flags, @Flags int mask)165     public InsetsFrameProvider setFlags(@Flags int flags, @Flags int mask) {
166         mFlags = (mFlags & ~mask) | (flags & mask);
167         return this;
168     }
169 
getFlags()170     public @Flags int getFlags() {
171         return mFlags;
172     }
173 
hasFlags(@lags int mask)174     public boolean hasFlags(@Flags int mask) {
175         return (mFlags & mask) == mask;
176     }
177 
setInsetsSize(Insets insetsSize)178     public InsetsFrameProvider setInsetsSize(Insets insetsSize) {
179         mInsetsSize = insetsSize;
180         return this;
181     }
182 
getInsetsSize()183     public Insets getInsetsSize() {
184         return mInsetsSize;
185     }
186 
setArbitraryRectangle(Rect rect)187     public InsetsFrameProvider setArbitraryRectangle(Rect rect) {
188         mArbitraryRectangle = new Rect(rect);
189         return this;
190     }
191 
getArbitraryRectangle()192     public Rect getArbitraryRectangle() {
193         return mArbitraryRectangle;
194     }
195 
setInsetsSizeOverrides(InsetsSizeOverride[] insetsSizeOverrides)196     public InsetsFrameProvider setInsetsSizeOverrides(InsetsSizeOverride[] insetsSizeOverrides) {
197         mInsetsSizeOverrides = insetsSizeOverrides;
198         return this;
199     }
200 
getInsetsSizeOverrides()201     public InsetsSizeOverride[] getInsetsSizeOverrides() {
202         return mInsetsSizeOverrides;
203     }
204 
setMinimalInsetsSizeInDisplayCutoutSafe( Insets minimalInsetsSizeInDisplayCutoutSafe)205     public InsetsFrameProvider setMinimalInsetsSizeInDisplayCutoutSafe(
206             Insets minimalInsetsSizeInDisplayCutoutSafe) {
207         mMinimalInsetsSizeInDisplayCutoutSafe = minimalInsetsSizeInDisplayCutoutSafe;
208         return this;
209     }
210 
getMinimalInsetsSizeInDisplayCutoutSafe()211     public Insets getMinimalInsetsSizeInDisplayCutoutSafe() {
212         return mMinimalInsetsSizeInDisplayCutoutSafe;
213     }
214 
215     /**
216      * Sets the bounding rectangles within and relative to the source frame.
217      */
setBoundingRects(@ullable Rect[] boundingRects)218     public InsetsFrameProvider setBoundingRects(@Nullable Rect[] boundingRects) {
219         mBoundingRects = boundingRects == null ? null : boundingRects.clone();
220         return this;
221     }
222 
223     /**
224      * Returns the arbitrary bounding rects, or null if none were set.
225      */
226     @Nullable
getBoundingRects()227     public Rect[] getBoundingRects() {
228         return mBoundingRects;
229     }
230 
231     @Override
describeContents()232     public int describeContents() {
233         return 0;
234     }
235 
236     @Override
toString()237     public String toString() {
238         final StringBuilder sb = new StringBuilder("InsetsFrameProvider: {");
239         sb.append("id=#").append(Integer.toHexString(mId));
240         sb.append(", index=").append(getIndex());
241         sb.append(", type=").append(WindowInsets.Type.toString(getType()));
242         sb.append(", source=").append(sourceToString(mSource));
243         sb.append(", flags=[").append(InsetsSource.flagsToString(mFlags)).append("]");
244         if (mInsetsSize != null) {
245             sb.append(", insetsSize=").append(mInsetsSize);
246         }
247         if (mInsetsSizeOverrides != null) {
248             sb.append(", insetsSizeOverrides=").append(Arrays.toString(mInsetsSizeOverrides));
249         }
250         if (mArbitraryRectangle != null) {
251             sb.append(", mArbitraryRectangle=").append(mArbitraryRectangle.toShortString());
252         }
253         if (mMinimalInsetsSizeInDisplayCutoutSafe != null) {
254             sb.append(", mMinimalInsetsSizeInDisplayCutoutSafe=")
255                     .append(mMinimalInsetsSizeInDisplayCutoutSafe);
256         }
257         if (mBoundingRects != null) {
258             sb.append(", mBoundingRects=").append(Arrays.toString(mBoundingRects));
259         }
260         sb.append("}");
261         return sb.toString();
262     }
263 
sourceToString(int source)264     private static String sourceToString(int source) {
265         switch (source) {
266             case SOURCE_DISPLAY:
267                 return "DISPLAY";
268             case SOURCE_CONTAINER_BOUNDS:
269                 return "CONTAINER_BOUNDS";
270             case SOURCE_FRAME:
271                 return "FRAME";
272             case SOURCE_ARBITRARY_RECTANGLE:
273                 return "ARBITRARY_RECTANGLE";
274         }
275         return "UNDEFINED";
276     }
277 
InsetsFrameProvider(Parcel in)278     public InsetsFrameProvider(Parcel in) {
279         mId = in.readInt();
280         mSource = in.readInt();
281         mFlags = in.readInt();
282         mInsetsSize = in.readTypedObject(Insets.CREATOR);
283         mInsetsSizeOverrides = in.createTypedArray(InsetsSizeOverride.CREATOR);
284         mArbitraryRectangle = in.readTypedObject(Rect.CREATOR);
285         mMinimalInsetsSizeInDisplayCutoutSafe = in.readTypedObject(Insets.CREATOR);
286         mBoundingRects = in.createTypedArray(Rect.CREATOR);
287     }
288 
289     @Override
writeToParcel(Parcel out, int flags)290     public void writeToParcel(Parcel out, int flags) {
291         out.writeInt(mId);
292         out.writeInt(mSource);
293         out.writeInt(mFlags);
294         out.writeTypedObject(mInsetsSize, flags);
295         out.writeTypedArray(mInsetsSizeOverrides, flags);
296         out.writeTypedObject(mArbitraryRectangle, flags);
297         out.writeTypedObject(mMinimalInsetsSizeInDisplayCutoutSafe, flags);
298         out.writeTypedArray(mBoundingRects, flags);
299     }
300 
idEquals(InsetsFrameProvider o)301     public boolean idEquals(InsetsFrameProvider o) {
302         return mId == o.mId;
303     }
304 
305     @Override
equals(Object o)306     public boolean equals(Object o) {
307         if (this == o) {
308             return true;
309         }
310         if (o == null || getClass() != o.getClass()) {
311             return false;
312         }
313         final InsetsFrameProvider other = (InsetsFrameProvider) o;
314         return mId == other.mId && mSource == other.mSource && mFlags == other.mFlags
315                 && Objects.equals(mInsetsSize, other.mInsetsSize)
316                 && Arrays.equals(mInsetsSizeOverrides, other.mInsetsSizeOverrides)
317                 && Objects.equals(mArbitraryRectangle, other.mArbitraryRectangle)
318                 && Objects.equals(mMinimalInsetsSizeInDisplayCutoutSafe,
319                         other.mMinimalInsetsSizeInDisplayCutoutSafe)
320                 && Arrays.equals(mBoundingRects, other.mBoundingRects);
321     }
322 
323     @Override
hashCode()324     public int hashCode() {
325         return Objects.hash(mId, mSource, mFlags, mInsetsSize,
326                 Arrays.hashCode(mInsetsSizeOverrides), mArbitraryRectangle,
327                 mMinimalInsetsSizeInDisplayCutoutSafe, Arrays.hashCode(mBoundingRects));
328     }
329 
330     public static final @NonNull Parcelable.Creator<InsetsFrameProvider> CREATOR =
331             new Parcelable.Creator<>() {
332                 @Override
333                 public InsetsFrameProvider createFromParcel(Parcel in) {
334                     return new InsetsFrameProvider(in);
335                 }
336 
337                 @Override
338                 public InsetsFrameProvider[] newArray(int size) {
339                     return new InsetsFrameProvider[size];
340                 }
341             };
342 
343     /**
344      * Class to describe the insets size to be provided to window with specific window type. If not
345      * used, same insets size will be sent as instructed in the insetsSize and source.
346      *
347      * If the insetsSize of given type is set to {@code null}, the insets source frame will be used
348      * directly for that window type.
349      */
350     public static class InsetsSizeOverride implements Parcelable {
351 
352         private final int mWindowType;
353         private final Insets mInsetsSize;
354 
InsetsSizeOverride(Parcel in)355         protected InsetsSizeOverride(Parcel in) {
356             mWindowType = in.readInt();
357             mInsetsSize = in.readTypedObject(Insets.CREATOR);
358         }
359 
InsetsSizeOverride(int windowType, Insets insetsSize)360         public InsetsSizeOverride(int windowType, Insets insetsSize) {
361             mWindowType = windowType;
362             mInsetsSize = insetsSize;
363         }
getWindowType()364         public int getWindowType() {
365             return mWindowType;
366         }
367 
getInsetsSize()368         public Insets getInsetsSize() {
369             return mInsetsSize;
370         }
371 
372         public static final Creator<InsetsSizeOverride> CREATOR = new Creator<>() {
373             @Override
374             public InsetsSizeOverride createFromParcel(Parcel in) {
375                 return new InsetsSizeOverride(in);
376             }
377 
378             @Override
379             public InsetsSizeOverride[] newArray(int size) {
380                 return new InsetsSizeOverride[size];
381             }
382         };
383 
384         @Override
describeContents()385         public int describeContents() {
386             return 0;
387         }
388 
389         @Override
writeToParcel(Parcel out, int flags)390         public void writeToParcel(Parcel out, int flags) {
391             out.writeInt(mWindowType);
392             out.writeTypedObject(mInsetsSize, flags);
393         }
394 
395         @Override
toString()396         public String toString() {
397             StringBuilder sb = new StringBuilder(32);
398             sb.append("TypedInsetsSize: {");
399             sb.append("windowType=").append(ViewDebug.intToString(
400                     WindowManager.LayoutParams.class, "type", mWindowType));
401             sb.append(", insetsSize=").append(mInsetsSize);
402             sb.append("}");
403             return sb.toString();
404         }
405 
406         @Override
hashCode()407         public int hashCode() {
408             return Objects.hash(mWindowType, mInsetsSize);
409         }
410     }
411 }
412 
413