/* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.view; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.Insets; import android.graphics.Rect; import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; import android.view.InsetsSource.Flags; import android.view.WindowInsets.Type.InsetsType; import java.util.Arrays; import java.util.Objects; /** * Insets provided by a window. * * The insets frame will by default as the window frame size. If the providers are set, the * calculation result based on the source size will be used as the insets frame. * * The InsetsFrameProvider should be self-contained. Nothing describing the window itself, such as * contentInsets, visibleInsets, etc. won't affect the insets providing to other windows when this * is set. * @hide */ public class InsetsFrameProvider implements Parcelable { /** * Uses the display frame as the source. */ public static final int SOURCE_DISPLAY = 0; /** * Uses the window bounds as the source. */ public static final int SOURCE_CONTAINER_BOUNDS = 1; /** * Uses the window frame as the source. */ public static final int SOURCE_FRAME = 2; /** * Uses {@link #mArbitraryRectangle} as the source. */ public static final int SOURCE_ARBITRARY_RECTANGLE = 3; private final int mId; /** * The selection of the starting rectangle to be converted into source frame. */ private int mSource = SOURCE_FRAME; /** * This is used as the source frame only if SOURCE_ARBITRARY_RECTANGLE is applied. */ private Rect mArbitraryRectangle; /** * Modifies the starting rectangle selected by {@link #mSource}. * * For example, when the given source frame is (0, 0) - (100, 200), and the insetsSize is null, * the source frame will be directly used as the final insets frame. If the insetsSize is set to * (0, 0, 0, 50) instead, the insets frame will be a frame starting from the bottom side of the * source frame with height of 50, i.e., (0, 150) - (100, 200). */ private Insets mInsetsSize = null; /** * Various behavioral options/flags. Default is none. * * @see Flags */ private @Flags int mFlags; /** * If null, the size set in insetsSize will be applied to all window types. If it contains * element of some types, the insets reported to the window with that types will be overridden. */ private InsetsSizeOverride[] mInsetsSizeOverrides = null; /** * This field, if set, is indicating the insets needs to be at least the given size inside the * display cutout safe area. This will be compared to the insets size calculated based on other * attributes, and will be applied when this is larger. This is independent of the * PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT in LayoutParams, as this is not going to change * the layout of the window, but only change the insets frame. This can be applied to insets * calculated based on all three source frames. * * Be cautious, this will not be in effect for the window types whose insets size is overridden. */ private Insets mMinimalInsetsSizeInDisplayCutoutSafe = null; /** * Indicates the bounding rectangles within the provided insets frame, in relative coordinates * to the source frame. */ private Rect[] mBoundingRects = null; /** * Creates an InsetsFrameProvider which describes what frame an insets source should have. * * @param owner the owner of this provider. We might have multiple sources with the same type on * a display, this is used to identify them. * @param index the index of this provider. An owner might provide multiple sources with the * same type, this is used to identify them. * The value must be in a range of [0, 2047]. * @param type the {@link InsetsType}. * @see InsetsSource#createId(Object, int, int) */ public InsetsFrameProvider(Object owner, @IntRange(from = 0, to = 2047) int index, @InsetsType int type) { mId = InsetsSource.createId(owner, index, type); } /** * Returns an unique integer which identifies the insets source. */ public int getId() { return mId; } /** * Returns the index specified in {@link #InsetsFrameProvider(IBinder, int, int)}. */ public int getIndex() { return InsetsSource.getIndex(mId); } /** * Returns the {@link InsetsType} specified in {@link #InsetsFrameProvider(IBinder, int, int)}. */ public int getType() { return InsetsSource.getType(mId); } public InsetsFrameProvider setSource(int source) { mSource = source; return this; } public int getSource() { return mSource; } public InsetsFrameProvider setFlags(@Flags int flags, @Flags int mask) { mFlags = (mFlags & ~mask) | (flags & mask); return this; } public @Flags int getFlags() { return mFlags; } public boolean hasFlags(@Flags int mask) { return (mFlags & mask) == mask; } public InsetsFrameProvider setInsetsSize(Insets insetsSize) { mInsetsSize = insetsSize; return this; } public Insets getInsetsSize() { return mInsetsSize; } public InsetsFrameProvider setArbitraryRectangle(Rect rect) { mArbitraryRectangle = new Rect(rect); return this; } public Rect getArbitraryRectangle() { return mArbitraryRectangle; } public InsetsFrameProvider setInsetsSizeOverrides(InsetsSizeOverride[] insetsSizeOverrides) { mInsetsSizeOverrides = insetsSizeOverrides; return this; } public InsetsSizeOverride[] getInsetsSizeOverrides() { return mInsetsSizeOverrides; } public InsetsFrameProvider setMinimalInsetsSizeInDisplayCutoutSafe( Insets minimalInsetsSizeInDisplayCutoutSafe) { mMinimalInsetsSizeInDisplayCutoutSafe = minimalInsetsSizeInDisplayCutoutSafe; return this; } public Insets getMinimalInsetsSizeInDisplayCutoutSafe() { return mMinimalInsetsSizeInDisplayCutoutSafe; } /** * Sets the bounding rectangles within and relative to the source frame. */ public InsetsFrameProvider setBoundingRects(@Nullable Rect[] boundingRects) { mBoundingRects = boundingRects == null ? null : boundingRects.clone(); return this; } /** * Returns the arbitrary bounding rects, or null if none were set. */ @Nullable public Rect[] getBoundingRects() { return mBoundingRects; } @Override public int describeContents() { return 0; } @Override public String toString() { final StringBuilder sb = new StringBuilder("InsetsFrameProvider: {"); sb.append("id=#").append(Integer.toHexString(mId)); sb.append(", index=").append(getIndex()); sb.append(", type=").append(WindowInsets.Type.toString(getType())); sb.append(", source=").append(sourceToString(mSource)); sb.append(", flags=[").append(InsetsSource.flagsToString(mFlags)).append("]"); if (mInsetsSize != null) { sb.append(", insetsSize=").append(mInsetsSize); } if (mInsetsSizeOverrides != null) { sb.append(", insetsSizeOverrides=").append(Arrays.toString(mInsetsSizeOverrides)); } if (mArbitraryRectangle != null) { sb.append(", mArbitraryRectangle=").append(mArbitraryRectangle.toShortString()); } if (mMinimalInsetsSizeInDisplayCutoutSafe != null) { sb.append(", mMinimalInsetsSizeInDisplayCutoutSafe=") .append(mMinimalInsetsSizeInDisplayCutoutSafe); } if (mBoundingRects != null) { sb.append(", mBoundingRects=").append(Arrays.toString(mBoundingRects)); } sb.append("}"); return sb.toString(); } private static String sourceToString(int source) { switch (source) { case SOURCE_DISPLAY: return "DISPLAY"; case SOURCE_CONTAINER_BOUNDS: return "CONTAINER_BOUNDS"; case SOURCE_FRAME: return "FRAME"; case SOURCE_ARBITRARY_RECTANGLE: return "ARBITRARY_RECTANGLE"; } return "UNDEFINED"; } public InsetsFrameProvider(Parcel in) { mId = in.readInt(); mSource = in.readInt(); mFlags = in.readInt(); mInsetsSize = in.readTypedObject(Insets.CREATOR); mInsetsSizeOverrides = in.createTypedArray(InsetsSizeOverride.CREATOR); mArbitraryRectangle = in.readTypedObject(Rect.CREATOR); mMinimalInsetsSizeInDisplayCutoutSafe = in.readTypedObject(Insets.CREATOR); mBoundingRects = in.createTypedArray(Rect.CREATOR); } @Override public void writeToParcel(Parcel out, int flags) { out.writeInt(mId); out.writeInt(mSource); out.writeInt(mFlags); out.writeTypedObject(mInsetsSize, flags); out.writeTypedArray(mInsetsSizeOverrides, flags); out.writeTypedObject(mArbitraryRectangle, flags); out.writeTypedObject(mMinimalInsetsSizeInDisplayCutoutSafe, flags); out.writeTypedArray(mBoundingRects, flags); } public boolean idEquals(InsetsFrameProvider o) { return mId == o.mId; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } final InsetsFrameProvider other = (InsetsFrameProvider) o; return mId == other.mId && mSource == other.mSource && mFlags == other.mFlags && Objects.equals(mInsetsSize, other.mInsetsSize) && Arrays.equals(mInsetsSizeOverrides, other.mInsetsSizeOverrides) && Objects.equals(mArbitraryRectangle, other.mArbitraryRectangle) && Objects.equals(mMinimalInsetsSizeInDisplayCutoutSafe, other.mMinimalInsetsSizeInDisplayCutoutSafe) && Arrays.equals(mBoundingRects, other.mBoundingRects); } @Override public int hashCode() { return Objects.hash(mId, mSource, mFlags, mInsetsSize, Arrays.hashCode(mInsetsSizeOverrides), mArbitraryRectangle, mMinimalInsetsSizeInDisplayCutoutSafe, Arrays.hashCode(mBoundingRects)); } public static final @NonNull Parcelable.Creator CREATOR = new Parcelable.Creator<>() { @Override public InsetsFrameProvider createFromParcel(Parcel in) { return new InsetsFrameProvider(in); } @Override public InsetsFrameProvider[] newArray(int size) { return new InsetsFrameProvider[size]; } }; /** * Class to describe the insets size to be provided to window with specific window type. If not * used, same insets size will be sent as instructed in the insetsSize and source. * * If the insetsSize of given type is set to {@code null}, the insets source frame will be used * directly for that window type. */ public static class InsetsSizeOverride implements Parcelable { private final int mWindowType; private final Insets mInsetsSize; protected InsetsSizeOverride(Parcel in) { mWindowType = in.readInt(); mInsetsSize = in.readTypedObject(Insets.CREATOR); } public InsetsSizeOverride(int windowType, Insets insetsSize) { mWindowType = windowType; mInsetsSize = insetsSize; } public int getWindowType() { return mWindowType; } public Insets getInsetsSize() { return mInsetsSize; } public static final Creator CREATOR = new Creator<>() { @Override public InsetsSizeOverride createFromParcel(Parcel in) { return new InsetsSizeOverride(in); } @Override public InsetsSizeOverride[] newArray(int size) { return new InsetsSizeOverride[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel out, int flags) { out.writeInt(mWindowType); out.writeTypedObject(mInsetsSize, flags); } @Override public String toString() { StringBuilder sb = new StringBuilder(32); sb.append("TypedInsetsSize: {"); sb.append("windowType=").append(ViewDebug.intToString( WindowManager.LayoutParams.class, "type", mWindowType)); sb.append(", insetsSize=").append(mInsetsSize); sb.append("}"); return sb.toString(); } @Override public int hashCode() { return Objects.hash(mWindowType, mInsetsSize); } } }