mirror of
https://github.com/LawnchairLauncher/lawnchair.git
synced 2026-03-01 00:06:47 +00:00
fixed crashes NPE's below 12
This commit is contained in:
692
compatLib/src/main/java/android/view/InsetsSource.java
Normal file
692
compatLib/src/main/java/android/view/InsetsSource.java
Normal file
@@ -0,0 +1,692 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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 static android.view.InsetsSourceProto.FRAME;
|
||||
import static android.view.InsetsSourceProto.TYPE;
|
||||
import static android.view.InsetsSourceProto.TYPE_NUMBER;
|
||||
import static android.view.InsetsSourceProto.VISIBLE;
|
||||
import static android.view.InsetsSourceProto.VISIBLE_FRAME;
|
||||
import static android.view.WindowInsets.Type.captionBar;
|
||||
import static android.view.WindowInsets.Type.ime;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.IntRange;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.graphics.Insets;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.proto.ProtoOutputStream;
|
||||
import android.view.WindowInsets.Type.InsetsType;
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
/**
|
||||
* Represents the state of a single entity generating insets for clients.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class InsetsSource implements Parcelable {
|
||||
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef(
|
||||
prefix = "SIDE_",
|
||||
value = {SIDE_NONE, SIDE_LEFT, SIDE_TOP, SIDE_RIGHT, SIDE_BOTTOM, SIDE_UNKNOWN})
|
||||
public @interface InternalInsetsSide {}
|
||||
|
||||
static final int SIDE_NONE = 0;
|
||||
static final int SIDE_LEFT = 1;
|
||||
static final int SIDE_TOP = 2;
|
||||
static final int SIDE_RIGHT = 3;
|
||||
static final int SIDE_BOTTOM = 4;
|
||||
static final int SIDE_UNKNOWN = 5;
|
||||
|
||||
/** The insets source ID of IME */
|
||||
public static final int ID_IME = createId(null, 0, ime());
|
||||
|
||||
/** The insets source ID of the IME caption bar ("fake" IME navigation bar). */
|
||||
public static final int ID_IME_CAPTION_BAR =
|
||||
InsetsSource.createId(null /* owner */, 1 /* index */, captionBar());
|
||||
|
||||
/**
|
||||
* Controls whether this source suppresses the scrim. If the scrim is ignored, the system won't
|
||||
* draw a semi-transparent scrim behind the system bar area even when the bar contrast is
|
||||
* enforced.
|
||||
*
|
||||
* @see android.R.styleable#Window_enforceStatusBarContrast
|
||||
* @see android.R.styleable#Window_enforceNavigationBarContrast
|
||||
*/
|
||||
public static final int FLAG_SUPPRESS_SCRIM = 1;
|
||||
|
||||
/**
|
||||
* Controls whether the insets frame will be used to move {@link RoundedCorner} inward with the
|
||||
* insets frame size when calculating the rounded corner insets to other windows.
|
||||
*
|
||||
* <p>For example, task bar will draw fake rounded corners above itself, so we need to move the
|
||||
* rounded corner up by the task bar insets size to make other windows see a rounded corner
|
||||
* above the task bar.
|
||||
*/
|
||||
public static final int FLAG_INSETS_ROUNDED_CORNER = 1 << 1;
|
||||
|
||||
/** Controls whether the insets provided by this source should be forcibly consumed. */
|
||||
public static final int FLAG_FORCE_CONSUMING = 1 << 2;
|
||||
|
||||
/** Controls whether the insets source will play an animation when resizing. */
|
||||
public static final int FLAG_ANIMATE_RESIZING = 1 << 3;
|
||||
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef(
|
||||
flag = true,
|
||||
prefix = "FLAG_",
|
||||
value = {
|
||||
FLAG_SUPPRESS_SCRIM,
|
||||
FLAG_INSETS_ROUNDED_CORNER,
|
||||
FLAG_FORCE_CONSUMING,
|
||||
FLAG_ANIMATE_RESIZING,
|
||||
})
|
||||
public @interface Flags {}
|
||||
|
||||
/**
|
||||
* Used when there are no bounding rects to describe an inset, which is only possible when the
|
||||
* insets itself is {@link Insets#NONE}.
|
||||
*/
|
||||
private static final Rect[] NO_BOUNDING_RECTS = new Rect[0];
|
||||
|
||||
private @Flags int mFlags;
|
||||
|
||||
/** An unique integer to identify this source across processes. */
|
||||
private final int mId;
|
||||
|
||||
private final @InsetsType int mType;
|
||||
|
||||
/** Frame of the source in screen coordinate space */
|
||||
private final Rect mFrame;
|
||||
|
||||
private @Nullable Rect mVisibleFrame;
|
||||
private @Nullable Rect[] mBoundingRects;
|
||||
|
||||
private boolean mVisible;
|
||||
|
||||
/**
|
||||
* Used to decide which side of the relative frame should receive insets when the frame fully
|
||||
* covers the relative frame.
|
||||
*/
|
||||
private @InternalInsetsSide int mSideHint = SIDE_NONE;
|
||||
|
||||
private final Rect mTmpFrame = new Rect();
|
||||
private final Rect mTmpBoundingRect = new Rect();
|
||||
|
||||
public InsetsSource(int id, @InsetsType int type) {
|
||||
mId = id;
|
||||
mType = type;
|
||||
mFrame = new Rect();
|
||||
mVisible = (WindowInsets.Type.defaultVisible() & type) != 0;
|
||||
}
|
||||
|
||||
public InsetsSource(InsetsSource other) {
|
||||
mId = other.mId;
|
||||
mType = other.mType;
|
||||
mFrame = new Rect(other.mFrame);
|
||||
mVisible = other.mVisible;
|
||||
mVisibleFrame = other.mVisibleFrame != null ? new Rect(other.mVisibleFrame) : null;
|
||||
mFlags = other.mFlags;
|
||||
mSideHint = other.mSideHint;
|
||||
mBoundingRects = other.mBoundingRects != null ? other.mBoundingRects.clone() : null;
|
||||
}
|
||||
|
||||
public void set(InsetsSource other) {
|
||||
mFrame.set(other.mFrame);
|
||||
mVisible = other.mVisible;
|
||||
mVisibleFrame = other.mVisibleFrame != null ? new Rect(other.mVisibleFrame) : null;
|
||||
mFlags = other.mFlags;
|
||||
mSideHint = other.mSideHint;
|
||||
mBoundingRects = other.mBoundingRects != null ? other.mBoundingRects.clone() : null;
|
||||
}
|
||||
|
||||
public InsetsSource setFrame(int left, int top, int right, int bottom) {
|
||||
mFrame.set(left, top, right, bottom);
|
||||
return this;
|
||||
}
|
||||
|
||||
public InsetsSource setFrame(Rect frame) {
|
||||
mFrame.set(frame);
|
||||
return this;
|
||||
}
|
||||
|
||||
public InsetsSource setVisibleFrame(@Nullable Rect visibleFrame) {
|
||||
mVisibleFrame = visibleFrame != null ? new Rect(visibleFrame) : null;
|
||||
return this;
|
||||
}
|
||||
|
||||
public InsetsSource setVisible(boolean visible) {
|
||||
mVisible = visible;
|
||||
return this;
|
||||
}
|
||||
|
||||
public InsetsSource setFlags(@Flags int flags) {
|
||||
mFlags = flags;
|
||||
return this;
|
||||
}
|
||||
|
||||
public InsetsSource setFlags(@Flags int flags, @Flags int mask) {
|
||||
mFlags = (mFlags & ~mask) | (flags & mask);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the side hint which is used to decide which side of the relative frame should receive
|
||||
* insets when the frame fully covers the relative frame.
|
||||
*
|
||||
* @param bounds A rectangle which contains the frame. It will be used to calculate the hint.
|
||||
*/
|
||||
public InsetsSource updateSideHint(Rect bounds) {
|
||||
mSideHint = getInsetSide(calculateInsets(bounds, mFrame, true /* ignoreVisibility */));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the bounding rectangles of this source. They are expected to be relative to the source
|
||||
* frame.
|
||||
*/
|
||||
public InsetsSource setBoundingRects(@Nullable Rect[] rects) {
|
||||
mBoundingRects = rects != null ? rects.clone() : null;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return mId;
|
||||
}
|
||||
|
||||
public @InsetsType int getType() {
|
||||
return mType;
|
||||
}
|
||||
|
||||
public Rect getFrame() {
|
||||
return mFrame;
|
||||
}
|
||||
|
||||
public @Nullable Rect getVisibleFrame() {
|
||||
return mVisibleFrame;
|
||||
}
|
||||
|
||||
public boolean isVisible() {
|
||||
return mVisible;
|
||||
}
|
||||
|
||||
public @Flags int getFlags() {
|
||||
return mFlags;
|
||||
}
|
||||
|
||||
public boolean hasFlags(int flags) {
|
||||
return (mFlags & flags) == flags;
|
||||
}
|
||||
|
||||
/** Returns the bounding rectangles of this source. */
|
||||
public @Nullable Rect[] getBoundingRects() {
|
||||
return mBoundingRects;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the insets this source will cause to a client window.
|
||||
*
|
||||
* @param relativeFrame The frame to calculate the insets relative to.
|
||||
* @param ignoreVisibility If true, always reports back insets even if source isn't visible.
|
||||
* @return The resulting insets. The contract is that only one side will be occupied by a
|
||||
* source.
|
||||
*/
|
||||
public Insets calculateInsets(Rect relativeFrame, boolean ignoreVisibility) {
|
||||
return calculateInsets(relativeFrame, mFrame, ignoreVisibility);
|
||||
}
|
||||
|
||||
/** Like {@link #calculateInsets(Rect, boolean)}, but will return visible insets. */
|
||||
public Insets calculateVisibleInsets(Rect relativeFrame) {
|
||||
return calculateInsets(
|
||||
relativeFrame,
|
||||
mVisibleFrame != null ? mVisibleFrame : mFrame,
|
||||
false /* ignoreVisibility */);
|
||||
}
|
||||
|
||||
private Insets calculateInsets(Rect relativeFrame, Rect frame, boolean ignoreVisibility) {
|
||||
if (!ignoreVisibility && !mVisible) {
|
||||
return Insets.NONE;
|
||||
}
|
||||
// During drag-move and drag-resizing, the caption insets position may not get updated
|
||||
// before the app frame get updated. To layout the app content correctly during drag events,
|
||||
// we always return the insets with the corresponding height covering the top.
|
||||
// However, with the "fake" IME navigation bar treated as a caption bar, we return the
|
||||
// insets with the corresponding height the bottom.
|
||||
if (getType() == WindowInsets.Type.captionBar()) {
|
||||
return getId() == ID_IME_CAPTION_BAR
|
||||
? Insets.of(0, 0, 0, frame.height())
|
||||
: Insets.of(0, frame.height(), 0, 0);
|
||||
}
|
||||
// Checks for whether there is shared edge with insets for 0-width/height window.
|
||||
final boolean hasIntersection =
|
||||
relativeFrame.isEmpty()
|
||||
? getIntersection(frame, relativeFrame, mTmpFrame)
|
||||
: mTmpFrame.setIntersect(frame, relativeFrame);
|
||||
if (!hasIntersection) {
|
||||
return Insets.NONE;
|
||||
}
|
||||
|
||||
// TODO: Currently, non-floating IME always intersects at bottom due to issues with cutout.
|
||||
// However, we should let the policy decide from the server.
|
||||
if (getType() == WindowInsets.Type.ime()) {
|
||||
return Insets.of(0, 0, 0, mTmpFrame.height());
|
||||
}
|
||||
|
||||
if (mTmpFrame.equals(relativeFrame)) {
|
||||
// Covering all sides
|
||||
switch (mSideHint) {
|
||||
default:
|
||||
case SIDE_LEFT:
|
||||
return Insets.of(mTmpFrame.width(), 0, 0, 0);
|
||||
case SIDE_TOP:
|
||||
return Insets.of(0, mTmpFrame.height(), 0, 0);
|
||||
case SIDE_RIGHT:
|
||||
return Insets.of(0, 0, mTmpFrame.width(), 0);
|
||||
case SIDE_BOTTOM:
|
||||
return Insets.of(0, 0, 0, mTmpFrame.height());
|
||||
}
|
||||
} else if (mTmpFrame.width() == relativeFrame.width()) {
|
||||
// Intersecting at top/bottom
|
||||
if (mTmpFrame.top == relativeFrame.top) {
|
||||
return Insets.of(0, mTmpFrame.height(), 0, 0);
|
||||
} else if (mTmpFrame.bottom == relativeFrame.bottom) {
|
||||
return Insets.of(0, 0, 0, mTmpFrame.height());
|
||||
}
|
||||
// TODO: remove when insets are shell-customizable.
|
||||
// This is a hack that says "if this is a top-inset (eg statusbar), always apply it
|
||||
// to the top". It is used when adjusting primary split for IME.
|
||||
if (mTmpFrame.top == 0) {
|
||||
return Insets.of(0, mTmpFrame.height(), 0, 0);
|
||||
}
|
||||
} else if (mTmpFrame.height() == relativeFrame.height()) {
|
||||
// Intersecting at left/right
|
||||
if (mTmpFrame.left == relativeFrame.left) {
|
||||
return Insets.of(mTmpFrame.width(), 0, 0, 0);
|
||||
} else if (mTmpFrame.right == relativeFrame.right) {
|
||||
return Insets.of(0, 0, mTmpFrame.width(), 0);
|
||||
}
|
||||
}
|
||||
return Insets.NONE;
|
||||
}
|
||||
|
||||
/** Calculates the bounding rects the source will cause to a client window. */
|
||||
public @NonNull Rect[] calculateBoundingRects(Rect relativeFrame, boolean ignoreVisibility) {
|
||||
if (!ignoreVisibility && !mVisible) {
|
||||
return NO_BOUNDING_RECTS;
|
||||
}
|
||||
|
||||
final Rect frame = getFrame();
|
||||
if (mBoundingRects == null) {
|
||||
// No bounding rects set, make a single bounding rect that covers the intersection of
|
||||
// the |frame| and the |relativeFrame|. Also make it relative to the window origin.
|
||||
return mTmpBoundingRect.setIntersect(frame, relativeFrame)
|
||||
? new Rect[] {
|
||||
new Rect(
|
||||
mTmpBoundingRect.left - relativeFrame.left,
|
||||
mTmpBoundingRect.top - relativeFrame.top,
|
||||
mTmpBoundingRect.right - relativeFrame.left,
|
||||
mTmpBoundingRect.bottom - relativeFrame.top)
|
||||
}
|
||||
: NO_BOUNDING_RECTS;
|
||||
}
|
||||
|
||||
// Special treatment for captionBar inset type. During drag-resizing, the |frame| and
|
||||
// |boundingRects| may not get updated as quickly as |relativeFrame|, so just assume the
|
||||
// |frame| will always be either at the top or bottom of |relativeFrame|. This means some
|
||||
// calculations to make |boundingRects| relative to |relativeFrame| can be skipped or
|
||||
// simplified.
|
||||
// TODO(b/254128050): remove special treatment.
|
||||
if (getType() == WindowInsets.Type.captionBar()) {
|
||||
final ArrayList<Rect> validBoundingRects = new ArrayList<>();
|
||||
for (final Rect boundingRect : mBoundingRects) {
|
||||
// Assume that the caption |frame| and |relativeFrame| perfectly align at the top
|
||||
// or bottom, meaning that the provided |boundingRect|, which is relative to the
|
||||
// |frame| either is already relative to |relativeFrame| (for top captionBar()), or
|
||||
// just needs to be made relative to |relativeFrame| for bottom bars.
|
||||
final int frameHeight = frame.height();
|
||||
mTmpBoundingRect.set(boundingRect);
|
||||
if (getId() == ID_IME_CAPTION_BAR) {
|
||||
mTmpBoundingRect.offset(0, relativeFrame.height() - frameHeight);
|
||||
}
|
||||
validBoundingRects.add(new Rect(mTmpBoundingRect));
|
||||
}
|
||||
return validBoundingRects.toArray(new Rect[validBoundingRects.size()]);
|
||||
}
|
||||
|
||||
// Regular treatment for non-captionBar inset types.
|
||||
final ArrayList<Rect> validBoundingRects = new ArrayList<>();
|
||||
for (final Rect boundingRect : mBoundingRects) {
|
||||
// |boundingRect| was provided relative to |frame|. Make it absolute to be in the same
|
||||
// coordinate system as |frame|.
|
||||
final Rect absBoundingRect =
|
||||
new Rect(
|
||||
boundingRect.left + frame.left,
|
||||
boundingRect.top + frame.top,
|
||||
boundingRect.right + frame.left,
|
||||
boundingRect.bottom + frame.top);
|
||||
// Now find the intersection of that |absBoundingRect| with |relativeFrame|. In other
|
||||
// words, whichever part of the bounding rect is inside the window frame.
|
||||
if (!mTmpBoundingRect.setIntersect(absBoundingRect, relativeFrame)) {
|
||||
// It's possible for this to be empty if the frame and bounding rects were larger
|
||||
// than the |relativeFrame|, such as when a system window is wider than the app
|
||||
// window width. Just ignore that rect since it will have no effect on the
|
||||
// window insets.
|
||||
continue;
|
||||
}
|
||||
// At this point, |mTmpBoundingRect| is a valid bounding rect located fully inside the
|
||||
// window, convert it to be relative to the window so that apps don't need to know the
|
||||
// location of the window to understand bounding rects.
|
||||
validBoundingRects.add(
|
||||
new Rect(
|
||||
mTmpBoundingRect.left - relativeFrame.left,
|
||||
mTmpBoundingRect.top - relativeFrame.top,
|
||||
mTmpBoundingRect.right - relativeFrame.left,
|
||||
mTmpBoundingRect.bottom - relativeFrame.top));
|
||||
}
|
||||
if (validBoundingRects.isEmpty()) {
|
||||
return NO_BOUNDING_RECTS;
|
||||
}
|
||||
return validBoundingRects.toArray(new Rect[validBoundingRects.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the intersection of two rectangles. The shared edges will also be counted in the
|
||||
* intersection.
|
||||
*
|
||||
* @param a The first rectangle being intersected with.
|
||||
* @param b The second rectangle being intersected with.
|
||||
* @param out The rectangle which represents the intersection.
|
||||
* @return {@code true} if there is any intersection.
|
||||
*/
|
||||
private static boolean getIntersection(@NonNull Rect a, @NonNull Rect b, @NonNull Rect out) {
|
||||
if (a.left <= b.right && b.left <= a.right && a.top <= b.bottom && b.top <= a.bottom) {
|
||||
out.left = Math.max(a.left, b.left);
|
||||
out.top = Math.max(a.top, b.top);
|
||||
out.right = Math.min(a.right, b.right);
|
||||
out.bottom = Math.min(a.bottom, b.bottom);
|
||||
return true;
|
||||
}
|
||||
out.setEmpty();
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the side for a certain {@code insets}. It is required that only one field l/t/r/b
|
||||
* is set in order that this method returns a meaningful result.
|
||||
*/
|
||||
static @InternalInsetsSide int getInsetSide(Insets insets) {
|
||||
if (Insets.NONE.equals(insets)) {
|
||||
return SIDE_NONE;
|
||||
}
|
||||
if (insets.left != 0) {
|
||||
return SIDE_LEFT;
|
||||
}
|
||||
if (insets.top != 0) {
|
||||
return SIDE_TOP;
|
||||
}
|
||||
if (insets.right != 0) {
|
||||
return SIDE_RIGHT;
|
||||
}
|
||||
if (insets.bottom != 0) {
|
||||
return SIDE_BOTTOM;
|
||||
}
|
||||
return SIDE_UNKNOWN;
|
||||
}
|
||||
|
||||
static String sideToString(@InternalInsetsSide int side) {
|
||||
switch (side) {
|
||||
case SIDE_NONE:
|
||||
return "NONE";
|
||||
case SIDE_LEFT:
|
||||
return "LEFT";
|
||||
case SIDE_TOP:
|
||||
return "TOP";
|
||||
case SIDE_RIGHT:
|
||||
return "RIGHT";
|
||||
case SIDE_BOTTOM:
|
||||
return "BOTTOM";
|
||||
default:
|
||||
return "UNKNOWN:" + side;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an identifier of an {@link InsetsSource}.
|
||||
*
|
||||
* @param owner An object owned by the owner. Only the owner can modify its own sources.
|
||||
* @param index An owner may have multiple sources with the same type. For example, the system
|
||||
* server might have multiple display cutout sources. This is used to identify which one is
|
||||
* which. The value must be in a range of [0, 2047].
|
||||
* @param type The {@link InsetsType type} of the source.
|
||||
* @return a unique integer as the identifier.
|
||||
*/
|
||||
public static int createId(
|
||||
Object owner, @IntRange(from = 0, to = 2047) int index, @InsetsType int type) {
|
||||
if (index < 0 || index >= 2048) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
// owner takes top 16 bits;
|
||||
// index takes 11 bits since the 6th bit;
|
||||
// type takes bottom 5 bits.
|
||||
return ((System.identityHashCode(owner) % (1 << 16)) << 16)
|
||||
+ (index << 5)
|
||||
+ WindowInsets.Type.indexOf(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index from the ID.
|
||||
*
|
||||
* @see #createId(Object, int, int)
|
||||
*/
|
||||
public static int getIndex(int id) {
|
||||
// start: ????????????????***********?????
|
||||
// & 65535: 0000000000000000***********?????
|
||||
// >> 5: 000000000000000000000***********
|
||||
return (id & 65535) >> 5;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link InsetsType} from the ID.
|
||||
*
|
||||
* @see #createId(Object, int, int)
|
||||
* @see WindowInsets.Type#indexOf(int)
|
||||
*/
|
||||
public static int getType(int id) {
|
||||
// start: ???????????????????????????*****
|
||||
// & 31: 000000000000000000000000000*****
|
||||
// 1 <<: See WindowInsets.Type#indexOf
|
||||
return 1 << (id & 31);
|
||||
}
|
||||
|
||||
public static String flagsToString(@Flags int flags) {
|
||||
final StringJoiner joiner = new StringJoiner("|");
|
||||
if ((flags & FLAG_SUPPRESS_SCRIM) != 0) {
|
||||
joiner.add("SUPPRESS_SCRIM");
|
||||
}
|
||||
if ((flags & FLAG_INSETS_ROUNDED_CORNER) != 0) {
|
||||
joiner.add("INSETS_ROUNDED_CORNER");
|
||||
}
|
||||
if ((flags & FLAG_FORCE_CONSUMING) != 0) {
|
||||
joiner.add("FORCE_CONSUMING");
|
||||
}
|
||||
if ((flags & FLAG_ANIMATE_RESIZING) != 0) {
|
||||
joiner.add("ANIMATE_RESIZING");
|
||||
}
|
||||
return joiner.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Export the state of {@link InsetsSource} into a protocol buffer output stream.
|
||||
*
|
||||
* @param proto Stream to write the state to
|
||||
* @param fieldId FieldId of InsetsSource as defined in the parent message
|
||||
*/
|
||||
public void dumpDebug(ProtoOutputStream proto, long fieldId) {
|
||||
final long token = proto.start(fieldId);
|
||||
if (!android.os.Flags.androidOsBuildVanillaIceCream()) {
|
||||
// Deprecated since V.
|
||||
proto.write(TYPE, WindowInsets.Type.toString(mType));
|
||||
}
|
||||
mFrame.dumpDebug(proto, FRAME);
|
||||
if (mVisibleFrame != null) {
|
||||
mVisibleFrame.dumpDebug(proto, VISIBLE_FRAME);
|
||||
}
|
||||
proto.write(VISIBLE, mVisible);
|
||||
proto.write(TYPE_NUMBER, mType);
|
||||
proto.end(token);
|
||||
}
|
||||
|
||||
public void dump(String prefix, PrintWriter pw) {
|
||||
pw.print(prefix);
|
||||
pw.print("InsetsSource id=");
|
||||
pw.print(Integer.toHexString(mId));
|
||||
pw.print(" type=");
|
||||
pw.print(WindowInsets.Type.toString(mType));
|
||||
pw.print(" frame=");
|
||||
pw.print(mFrame.toShortString());
|
||||
if (mVisibleFrame != null) {
|
||||
pw.print(" visibleFrame=");
|
||||
pw.print(mVisibleFrame.toShortString());
|
||||
}
|
||||
pw.print(" visible=");
|
||||
pw.print(mVisible);
|
||||
pw.print(" flags=");
|
||||
pw.print(flagsToString(mFlags));
|
||||
pw.print(" sideHint=");
|
||||
pw.print(sideToString(mSideHint));
|
||||
pw.print(" boundingRects=");
|
||||
pw.print(Arrays.toString(mBoundingRects));
|
||||
pw.println();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object o) {
|
||||
return equals(o, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param excludeInvisibleImeFrames If {@link WindowInsets.Type#ime()} frames should be ignored
|
||||
* when IME is not visible.
|
||||
*/
|
||||
public boolean equals(@Nullable Object o, boolean excludeInvisibleImeFrames) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
InsetsSource that = (InsetsSource) o;
|
||||
|
||||
if (mId != that.mId) return false;
|
||||
if (mType != that.mType) return false;
|
||||
if (mVisible != that.mVisible) return false;
|
||||
if (mFlags != that.mFlags) return false;
|
||||
if (mSideHint != that.mSideHint) return false;
|
||||
if (excludeInvisibleImeFrames && !mVisible && mType == WindowInsets.Type.ime()) return true;
|
||||
if (!Objects.equals(mVisibleFrame, that.mVisibleFrame)) return false;
|
||||
if (!mFrame.equals(that.mFrame)) return false;
|
||||
return Arrays.equals(mBoundingRects, that.mBoundingRects);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(
|
||||
mId,
|
||||
mType,
|
||||
mFrame,
|
||||
mVisibleFrame,
|
||||
mVisible,
|
||||
mFlags,
|
||||
mSideHint,
|
||||
Arrays.hashCode(mBoundingRects));
|
||||
}
|
||||
|
||||
public InsetsSource(Parcel in) {
|
||||
mId = in.readInt();
|
||||
mType = in.readInt();
|
||||
mFrame = Rect.CREATOR.createFromParcel(in);
|
||||
if (in.readInt() != 0) {
|
||||
mVisibleFrame = Rect.CREATOR.createFromParcel(in);
|
||||
} else {
|
||||
mVisibleFrame = null;
|
||||
}
|
||||
mVisible = in.readBoolean();
|
||||
mFlags = in.readInt();
|
||||
mSideHint = in.readInt();
|
||||
mBoundingRects = in.createTypedArray(Rect.CREATOR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeInt(mId);
|
||||
dest.writeInt(mType);
|
||||
mFrame.writeToParcel(dest, 0);
|
||||
if (mVisibleFrame != null) {
|
||||
dest.writeInt(1);
|
||||
mVisibleFrame.writeToParcel(dest, 0);
|
||||
} else {
|
||||
dest.writeInt(0);
|
||||
}
|
||||
dest.writeBoolean(mVisible);
|
||||
dest.writeInt(mFlags);
|
||||
dest.writeInt(mSideHint);
|
||||
dest.writeTypedArray(mBoundingRects, flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "InsetsSource: {"
|
||||
+ Integer.toHexString(mId)
|
||||
+ " mType="
|
||||
+ WindowInsets.Type.toString(mType)
|
||||
+ " mFrame="
|
||||
+ mFrame.toShortString()
|
||||
+ " mVisible="
|
||||
+ mVisible
|
||||
+ " mFlags="
|
||||
+ flagsToString(mFlags)
|
||||
+ " mSideHint="
|
||||
+ sideToString(mSideHint)
|
||||
+ " mBoundingRects="
|
||||
+ Arrays.toString(mBoundingRects)
|
||||
+ "}";
|
||||
}
|
||||
|
||||
public static final @NonNull Creator<InsetsSource> CREATOR =
|
||||
new Creator<>() {
|
||||
|
||||
public InsetsSource createFromParcel(Parcel in) {
|
||||
return new InsetsSource(in);
|
||||
}
|
||||
|
||||
public InsetsSource[] newArray(int size) {
|
||||
return new InsetsSource[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.window;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
|
||||
/**
|
||||
* Interface for applications to register back animation callbacks along their custom back handling.
|
||||
*
|
||||
* <p>This allows the client to customize various back behaviors by overriding the corresponding
|
||||
* callback methods.
|
||||
*
|
||||
* <p>Callback instances can be added to and removed from {@link OnBackInvokedDispatcher}, which is
|
||||
* held at window level and accessible through {@link Activity#getOnBackInvokedDispatcher()}, {@link
|
||||
* Dialog#getOnBackInvokedDispatcher()}, {@link Window#getOnBackInvokedDispatcher()} and {@link
|
||||
* View#findOnBackInvokedDispatcher()}.
|
||||
*
|
||||
* <p>When back is triggered, callbacks on the in-focus window are invoked in reverse order in which
|
||||
* they are added within the same priority. Between different priorities, callbacks with higher
|
||||
* priority are invoked first.
|
||||
*
|
||||
* <p>
|
||||
*
|
||||
* @see OnBackInvokedCallback
|
||||
*/
|
||||
public interface OnBackAnimationCallback extends OnBackInvokedCallback {
|
||||
/**
|
||||
* Called when a back gesture has been started, or back button has been pressed down.
|
||||
*
|
||||
* @param backEvent The {@link BackEvent} containing information about the touch or button
|
||||
* press.
|
||||
* @see BackEvent
|
||||
*/
|
||||
default void onBackStarted(@NonNull BackEvent backEvent) {}
|
||||
|
||||
/**
|
||||
* Called when a back gesture progresses.
|
||||
*
|
||||
* @param backEvent An {@link BackEvent} object describing the progress event.
|
||||
* @see BackEvent
|
||||
*/
|
||||
default void onBackProgressed(@NonNull BackEvent backEvent) {}
|
||||
|
||||
/** Called when a back gesture or back button press has been cancelled. */
|
||||
default void onBackCancelled() {}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.window;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
|
||||
/**
|
||||
* Callback allowing applications to handle back events in place of the system.
|
||||
*
|
||||
* <p>Callback instances can be added to and removed from {@link OnBackInvokedDispatcher}, which is
|
||||
* held at window level and accessible through {@link Activity#getOnBackInvokedDispatcher()}, {@link
|
||||
* Dialog#getOnBackInvokedDispatcher()}, {@link Window#getOnBackInvokedDispatcher()} and {@link
|
||||
* View#findOnBackInvokedDispatcher()}.
|
||||
*
|
||||
* <p>When back is triggered, callbacks on the in-focus window are invoked in reverse order in which
|
||||
* they are added within the same priority. Between different priorities, callbacks with higher
|
||||
* priority are invoked first.
|
||||
*
|
||||
* <p>This replaces {@link Activity#onBackPressed()}, {@link Dialog#onBackPressed()} and {@link
|
||||
* android.view.KeyEvent#KEYCODE_BACK}
|
||||
*
|
||||
* <p>If you want to customize back animation behaviors, in addition to handling back invocations,
|
||||
* register its subclass instances {@link OnBackAnimationCallback} instead.
|
||||
*
|
||||
* <p>
|
||||
*
|
||||
* @see OnBackInvokedDispatcher#registerOnBackInvokedCallback(int, OnBackInvokedCallback)
|
||||
* registerOnBackInvokedCallback(priority, OnBackInvokedCallback) to specify callback priority.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public interface OnBackInvokedCallback {
|
||||
/**
|
||||
* Called when a back gesture has been completed and committed, or back button pressed has been
|
||||
* released and committed.
|
||||
*/
|
||||
void onBackInvoked();
|
||||
}
|
||||
Reference in New Issue
Block a user