Provides apps the rounded corners info
- Created a new @hide class RoundedCorners
- The class is used to create and manager all the rounded corners.
- Created a new public class RoundedCorner
- Represent each rounded corner.
- Created a new public API in Display to get the original rounded
corners.
- Created a new public API in WindowInsets to get the rounded corners
relative to the window bounds.
Bug: 161808676
Test: atest RoundedCornerTest RoundedCornersTest
DisplayPolicyLayoutTests DisplayPolicyTests
WallpaperControllerTests
Change-Id: I58c671b0e9a077cdf26764c6302537c72db0f667
This commit is contained in:
@@ -46120,6 +46120,7 @@ package android.view {
|
||||
method @Deprecated public void getRectSize(android.graphics.Rect);
|
||||
method public float getRefreshRate();
|
||||
method public int getRotation();
|
||||
method @Nullable public android.view.RoundedCorner getRoundedCorner(int);
|
||||
method @Deprecated public void getSize(android.graphics.Point);
|
||||
method public int getState();
|
||||
method public android.view.Display.Mode[] getSupportedModes();
|
||||
@@ -47373,6 +47374,20 @@ package android.view {
|
||||
field public static final int TYPE_ZOOM_OUT = 1019; // 0x3fb
|
||||
}
|
||||
|
||||
public final class RoundedCorner implements android.os.Parcelable {
|
||||
ctor public RoundedCorner(int, int, int, int);
|
||||
method public int describeContents();
|
||||
method @NonNull public android.graphics.Point getCenter();
|
||||
method public int getPosition();
|
||||
method public int getRadius();
|
||||
method public void writeToParcel(@NonNull android.os.Parcel, int);
|
||||
field @NonNull public static final android.os.Parcelable.Creator<android.view.RoundedCorner> CREATOR;
|
||||
field public static final int POSITION_BOTTOM_LEFT = 3; // 0x3
|
||||
field public static final int POSITION_BOTTOM_RIGHT = 2; // 0x2
|
||||
field public static final int POSITION_TOP_LEFT = 0; // 0x0
|
||||
field public static final int POSITION_TOP_RIGHT = 1; // 0x1
|
||||
}
|
||||
|
||||
public class ScaleGestureDetector {
|
||||
ctor public ScaleGestureDetector(android.content.Context, android.view.ScaleGestureDetector.OnScaleGestureListener);
|
||||
ctor public ScaleGestureDetector(android.content.Context, android.view.ScaleGestureDetector.OnScaleGestureListener, android.os.Handler);
|
||||
@@ -49382,6 +49397,7 @@ package android.view {
|
||||
method @NonNull public android.graphics.Insets getInsets(int);
|
||||
method @NonNull public android.graphics.Insets getInsetsIgnoringVisibility(int);
|
||||
method @Deprecated @NonNull public android.graphics.Insets getMandatorySystemGestureInsets();
|
||||
method @Nullable public android.view.RoundedCorner getRoundedCorner(int);
|
||||
method @Deprecated public int getStableInsetBottom();
|
||||
method @Deprecated public int getStableInsetLeft();
|
||||
method @Deprecated public int getStableInsetRight();
|
||||
@@ -49415,6 +49431,7 @@ package android.view {
|
||||
method @NonNull public android.view.WindowInsets.Builder setInsets(int, @NonNull android.graphics.Insets);
|
||||
method @NonNull public android.view.WindowInsets.Builder setInsetsIgnoringVisibility(int, @NonNull android.graphics.Insets) throws java.lang.IllegalArgumentException;
|
||||
method @Deprecated @NonNull public android.view.WindowInsets.Builder setMandatorySystemGestureInsets(@NonNull android.graphics.Insets);
|
||||
method @NonNull public android.view.WindowInsets.Builder setRoundedCorner(int, @Nullable android.view.RoundedCorner);
|
||||
method @Deprecated @NonNull public android.view.WindowInsets.Builder setStableInsets(@NonNull android.graphics.Insets);
|
||||
method @Deprecated @NonNull public android.view.WindowInsets.Builder setSystemGestureInsets(@NonNull android.graphics.Insets);
|
||||
method @Deprecated @NonNull public android.view.WindowInsets.Builder setSystemWindowInsets(@NonNull android.graphics.Insets);
|
||||
|
||||
@@ -899,6 +899,32 @@ public final class Display {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link RoundedCorner} of the given position if there is one.
|
||||
*
|
||||
* @param position the position of the rounded corner on the display.
|
||||
*
|
||||
* @return the rounded corner of the given position. Returns {@code null} if there is none.
|
||||
*/
|
||||
@SuppressLint("VisiblySynchronized")
|
||||
@Nullable
|
||||
public RoundedCorner getRoundedCorner(@RoundedCorner.Position int position) {
|
||||
synchronized (this) {
|
||||
updateDisplayInfoLocked();
|
||||
RoundedCorners roundedCorners;
|
||||
if (mMayAdjustByFixedRotation) {
|
||||
roundedCorners = getDisplayAdjustments().adjustRoundedCorner(
|
||||
mDisplayInfo.roundedCorners,
|
||||
mDisplayInfo.rotation,
|
||||
mDisplayInfo.logicalWidth,
|
||||
mDisplayInfo.logicalHeight);
|
||||
} else {
|
||||
roundedCorners = mDisplayInfo.roundedCorners;
|
||||
}
|
||||
return roundedCorners == null ? null : roundedCorners.getRoundedCorner(position);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the pixel format of the display.
|
||||
* @return One of the constants defined in {@link android.graphics.PixelFormat}.
|
||||
|
||||
@@ -151,6 +151,23 @@ public class DisplayAdjustments {
|
||||
: realCutout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the adjusted {@link RoundedCorners} if available. Otherwise the original
|
||||
* {@link RoundedCorners} is returned.
|
||||
*/
|
||||
@Nullable
|
||||
public RoundedCorners adjustRoundedCorner(@Nullable RoundedCorners realRoundedCorners,
|
||||
@Surface.Rotation int realRotation, int displayWidth, int displayHeight) {
|
||||
final FixedRotationAdjustments rotationAdjustments = mFixedRotationAdjustments;
|
||||
if (realRoundedCorners == null || rotationAdjustments == null
|
||||
|| rotationAdjustments.mRotation == realRotation) {
|
||||
return realRoundedCorners;
|
||||
}
|
||||
|
||||
return realRoundedCorners.rotate(
|
||||
rotationAdjustments.mRotation, displayWidth, displayHeight);
|
||||
}
|
||||
|
||||
/** Returns the adjusted rotation if available. Otherwise the original rotation is returned. */
|
||||
@Surface.Rotation
|
||||
public int getRotation(@Surface.Rotation int realRotation) {
|
||||
|
||||
@@ -301,6 +301,12 @@ public final class DisplayInfo implements Parcelable {
|
||||
*/
|
||||
public float brightnessDefault;
|
||||
|
||||
/**
|
||||
* The {@link RoundedCorners} if present, otherwise {@code null}.
|
||||
*/
|
||||
@Nullable
|
||||
public RoundedCorners roundedCorners;
|
||||
|
||||
public static final @android.annotation.NonNull Creator<DisplayInfo> CREATOR = new Creator<DisplayInfo>() {
|
||||
@Override
|
||||
public DisplayInfo createFromParcel(Parcel source) {
|
||||
@@ -369,7 +375,8 @@ public final class DisplayInfo implements Parcelable {
|
||||
&& refreshRateOverride == other.refreshRateOverride
|
||||
&& brightnessMinimum == other.brightnessMinimum
|
||||
&& brightnessMaximum == other.brightnessMaximum
|
||||
&& brightnessDefault == other.brightnessDefault;
|
||||
&& brightnessDefault == other.brightnessDefault
|
||||
&& Objects.equals(roundedCorners, other.roundedCorners);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -418,6 +425,7 @@ public final class DisplayInfo implements Parcelable {
|
||||
brightnessMinimum = other.brightnessMinimum;
|
||||
brightnessMaximum = other.brightnessMaximum;
|
||||
brightnessDefault = other.brightnessDefault;
|
||||
roundedCorners = other.roundedCorners;
|
||||
}
|
||||
|
||||
public void readFromParcel(Parcel source) {
|
||||
@@ -468,6 +476,7 @@ public final class DisplayInfo implements Parcelable {
|
||||
brightnessMinimum = source.readFloat();
|
||||
brightnessMaximum = source.readFloat();
|
||||
brightnessDefault = source.readFloat();
|
||||
roundedCorners = source.readTypedObject(RoundedCorners.CREATOR);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -517,6 +526,7 @@ public final class DisplayInfo implements Parcelable {
|
||||
dest.writeFloat(brightnessMinimum);
|
||||
dest.writeFloat(brightnessMaximum);
|
||||
dest.writeFloat(brightnessDefault);
|
||||
dest.writeTypedObject(roundedCorners, flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -653,6 +653,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
private void updateState(InsetsState newState) {
|
||||
mState.setDisplayFrame(newState.getDisplayFrame());
|
||||
mState.setDisplayCutout(newState.getDisplayCutout());
|
||||
mState.setRoundedCorners(newState.getRoundedCorners());
|
||||
@InsetsType int disabledUserAnimationTypes = 0;
|
||||
@InsetsType int[] cancelledUserAnimationTypes = {0};
|
||||
for (@InternalInsetsType int type = 0; type < InsetsState.SIZE; type++) {
|
||||
|
||||
@@ -173,6 +173,9 @@ public class InsetsState implements Parcelable {
|
||||
private final DisplayCutout.ParcelableWrapper mDisplayCutout =
|
||||
new DisplayCutout.ParcelableWrapper();
|
||||
|
||||
/** The rounded corners on the display */
|
||||
private RoundedCorners mRoundedCorners = RoundedCorners.NO_ROUNDED_CORNERS;
|
||||
|
||||
public InsetsState() {
|
||||
}
|
||||
|
||||
@@ -256,7 +259,8 @@ public class InsetsState implements Parcelable {
|
||||
}
|
||||
|
||||
return new WindowInsets(typeInsetsMap, typeMaxInsetsMap, typeVisibilityMap, isScreenRound,
|
||||
alwaysConsumeSystemBars, calculateRelativeCutout(frame), compatInsetsTypes,
|
||||
alwaysConsumeSystemBars, calculateRelativeCutout(frame),
|
||||
calculateRelativeRoundedCorners(frame), compatInsetsTypes,
|
||||
(legacySystemUiFlags & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0);
|
||||
}
|
||||
|
||||
@@ -281,6 +285,20 @@ public class InsetsState implements Parcelable {
|
||||
return raw.inset(insetLeft, insetTop, insetRight, insetBottom);
|
||||
}
|
||||
|
||||
private RoundedCorners calculateRelativeRoundedCorners(Rect frame) {
|
||||
if (mDisplayFrame.equals(frame)) {
|
||||
return mRoundedCorners;
|
||||
}
|
||||
if (frame == null) {
|
||||
return RoundedCorners.NO_ROUNDED_CORNERS;
|
||||
}
|
||||
final int insetLeft = frame.left - mDisplayFrame.left;
|
||||
final int insetTop = frame.top - mDisplayFrame.top;
|
||||
final int insetRight = mDisplayFrame.right - frame.right;
|
||||
final int insetBottom = mDisplayFrame.bottom - frame.bottom;
|
||||
return mRoundedCorners.inset(insetLeft, insetTop, insetRight, insetBottom);
|
||||
}
|
||||
|
||||
public Rect calculateInsets(Rect frame, @InsetsType int types, boolean ignoreVisibility) {
|
||||
Insets insets = Insets.NONE;
|
||||
for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
|
||||
@@ -462,6 +480,14 @@ public class InsetsState implements Parcelable {
|
||||
return mDisplayCutout.get();
|
||||
}
|
||||
|
||||
public void setRoundedCorners(RoundedCorners roundedCorners) {
|
||||
mRoundedCorners = roundedCorners;
|
||||
}
|
||||
|
||||
public RoundedCorners getRoundedCorners() {
|
||||
return mRoundedCorners;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies the state of this class to exclude a certain type to make it ready for dispatching
|
||||
* to the client.
|
||||
@@ -493,6 +519,7 @@ public class InsetsState implements Parcelable {
|
||||
public void scale(float scale) {
|
||||
mDisplayFrame.scale(scale);
|
||||
mDisplayCutout.scale(scale);
|
||||
mRoundedCorners = mRoundedCorners.scale(scale);
|
||||
for (int i = 0; i < SIZE; i++) {
|
||||
final InsetsSource source = mSources[i];
|
||||
if (source != null) {
|
||||
@@ -512,6 +539,7 @@ public class InsetsState implements Parcelable {
|
||||
public void set(InsetsState other, boolean copySources) {
|
||||
mDisplayFrame.set(other.mDisplayFrame);
|
||||
mDisplayCutout.set(other.mDisplayCutout);
|
||||
mRoundedCorners = other.getRoundedCorners();
|
||||
if (copySources) {
|
||||
for (int i = 0; i < SIZE; i++) {
|
||||
InsetsSource source = other.mSources[i];
|
||||
@@ -619,11 +647,15 @@ public class InsetsState implements Parcelable {
|
||||
}
|
||||
|
||||
public void dump(String prefix, PrintWriter pw) {
|
||||
final String newPrefix = prefix + " ";
|
||||
pw.println(prefix + "InsetsState");
|
||||
pw.println(newPrefix + "mDisplayFrame=" + mDisplayFrame);
|
||||
pw.println(newPrefix + "mDisplayCutout=" + mDisplayCutout.get());
|
||||
pw.println(newPrefix + "mRoundedCorners=" + mRoundedCorners);
|
||||
for (int i = 0; i < SIZE; i++) {
|
||||
InsetsSource source = mSources[i];
|
||||
if (source == null) continue;
|
||||
source.dump(prefix + " ", pw);
|
||||
source.dump(newPrefix + " ", pw);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -713,7 +745,8 @@ public class InsetsState implements Parcelable {
|
||||
InsetsState state = (InsetsState) o;
|
||||
|
||||
if (!mDisplayFrame.equals(state.mDisplayFrame)
|
||||
|| !mDisplayCutout.equals(state.mDisplayCutout)) {
|
||||
|| !mDisplayCutout.equals(state.mDisplayCutout)
|
||||
|| !mRoundedCorners.equals(state.mRoundedCorners)) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < SIZE; i++) {
|
||||
@@ -737,7 +770,8 @@ public class InsetsState implements Parcelable {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(mDisplayFrame, mDisplayCutout, Arrays.hashCode(mSources));
|
||||
return Objects.hash(mDisplayFrame, mDisplayCutout, Arrays.hashCode(mSources),
|
||||
mRoundedCorners);
|
||||
}
|
||||
|
||||
public InsetsState(Parcel in) {
|
||||
@@ -754,6 +788,7 @@ public class InsetsState implements Parcelable {
|
||||
mDisplayFrame.writeToParcel(dest, flags);
|
||||
mDisplayCutout.writeToParcel(dest, flags);
|
||||
dest.writeParcelableArray(mSources, 0);
|
||||
dest.writeTypedObject(mRoundedCorners, flags);
|
||||
}
|
||||
|
||||
public static final @android.annotation.NonNull Creator<InsetsState> CREATOR = new Creator<InsetsState>() {
|
||||
@@ -771,6 +806,7 @@ public class InsetsState implements Parcelable {
|
||||
mDisplayFrame.set(Rect.CREATOR.createFromParcel(in));
|
||||
mDisplayCutout.set(DisplayCutout.ParcelableWrapper.CREATOR.createFromParcel(in));
|
||||
mSources = in.readParcelableArray(null, InsetsSource.class);
|
||||
mRoundedCorners = in.readTypedObject(RoundedCorners.CREATOR);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -785,6 +821,7 @@ public class InsetsState implements Parcelable {
|
||||
return "InsetsState: {"
|
||||
+ "mDisplayFrame=" + mDisplayFrame
|
||||
+ ", mDisplayCutout=" + mDisplayCutout
|
||||
+ ", mRoundedCorners=" + mRoundedCorners
|
||||
+ ", mSources= { " + joiner
|
||||
+ " }";
|
||||
}
|
||||
|
||||
239
core/java/android/view/RoundedCorner.java
Normal file
239
core/java/android/view/RoundedCorner.java
Normal file
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.graphics.Point;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* Represents a rounded corner of the display.
|
||||
*
|
||||
* <code>
|
||||
* ________
|
||||
* / ^
|
||||
* / | Radius
|
||||
* | v
|
||||
* | X <- Center point
|
||||
* |<----->
|
||||
* Radius
|
||||
* </code>
|
||||
*
|
||||
* <p>Note: The rounded corner formed by the radius and the center is an approximation.</p>
|
||||
*
|
||||
* <p>{@link RoundedCorner} is immutable.</p>
|
||||
*/
|
||||
public final class RoundedCorner implements Parcelable {
|
||||
|
||||
/**
|
||||
* The rounded corner is at the top-left of the screen.
|
||||
*/
|
||||
public static final int POSITION_TOP_LEFT = 0;
|
||||
/**
|
||||
* The rounded corner is at the top-right of the screen.
|
||||
*/
|
||||
public static final int POSITION_TOP_RIGHT = 1;
|
||||
/**
|
||||
* The rounded corner is at the bottom-right of the screen.
|
||||
*/
|
||||
public static final int POSITION_BOTTOM_RIGHT = 2;
|
||||
/**
|
||||
* The rounded corner is at the bottom-left of the screen.
|
||||
*/
|
||||
public static final int POSITION_BOTTOM_LEFT = 3;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@IntDef(prefix = { "POSITION_" }, value = {
|
||||
POSITION_TOP_LEFT,
|
||||
POSITION_TOP_RIGHT,
|
||||
POSITION_BOTTOM_RIGHT,
|
||||
POSITION_BOTTOM_LEFT
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface Position {}
|
||||
|
||||
private final @Position int mPosition;
|
||||
private final int mRadius;
|
||||
@NonNull
|
||||
private final Point mCenter;
|
||||
|
||||
/**
|
||||
* Creates an empty {@link RoundedCorner} on the given position.
|
||||
* @hide
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public RoundedCorner(@Position int position) {
|
||||
mPosition = position;
|
||||
mRadius = 0;
|
||||
mCenter = new Point(0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link RoundedCorner}.
|
||||
*
|
||||
* <p>Note that this is only useful for tests. For production code, developers should always
|
||||
* use a {@link RoundedCorner} obtained from the system via
|
||||
* {@link WindowInsets#getRoundedCorner} or {@link Display#getRoundedCorner}.</p>
|
||||
*
|
||||
* @param position the position of the rounded corner.
|
||||
* @param radius the radius of the rounded corner.
|
||||
* @param centerX the x of center point of the rounded corner.
|
||||
* @param centerY the y of center point of the rounded corner.
|
||||
*
|
||||
*/
|
||||
public RoundedCorner(@Position int position, int radius, int centerX,
|
||||
int centerY) {
|
||||
mPosition = position;
|
||||
mRadius = radius;
|
||||
mCenter = new Point(centerX, centerY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link RoundedCorner} from a passed in {@link RoundedCorner}.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
RoundedCorner(RoundedCorner rc) {
|
||||
mPosition = rc.getPosition();
|
||||
mRadius = rc.getRadius();
|
||||
mCenter = new Point(rc.getCenter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the position of this {@link RoundedCorner}.
|
||||
*
|
||||
* @see #POSITION_TOP_LEFT
|
||||
* @see #POSITION_TOP_RIGHT
|
||||
* @see #POSITION_BOTTOM_RIGHT
|
||||
* @see #POSITION_BOTTOM_LEFT
|
||||
*/
|
||||
public @Position int getPosition() {
|
||||
return mPosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the radius of a quarter circle approximation of this {@link RoundedCorner}.
|
||||
*
|
||||
* @return the rounded corner radius of this {@link RoundedCorner}. Returns 0 if there is no
|
||||
* rounded corner.
|
||||
*/
|
||||
public int getRadius() {
|
||||
return mRadius;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the circle center of a quarter circle approximation of this {@link RoundedCorner}.
|
||||
*
|
||||
* @return the center point of this {@link RoundedCorner} in the application's coordinate.
|
||||
*/
|
||||
@NonNull
|
||||
public Point getCenter() {
|
||||
return new Point(mCenter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether this {@link RoundedCorner} exists and is inside the application's bounds.
|
||||
*
|
||||
* @return {@code false} if there is a rounded corner and is contained in the application's
|
||||
* bounds. Otherwise return {@code true}.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return mRadius == 0 || mCenter.x == 0 || mCenter.y == 0;
|
||||
}
|
||||
|
||||
private String getPositionString(@Position int position) {
|
||||
switch (position) {
|
||||
case POSITION_TOP_LEFT:
|
||||
return "TopLeft";
|
||||
case POSITION_TOP_RIGHT:
|
||||
return "TopRight";
|
||||
case POSITION_BOTTOM_RIGHT:
|
||||
return "BottomRight";
|
||||
case POSITION_BOTTOM_LEFT:
|
||||
return "BottomLeft";
|
||||
default:
|
||||
return "Invalid";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this) {
|
||||
return true;
|
||||
}
|
||||
if (o instanceof RoundedCorner) {
|
||||
RoundedCorner r = (RoundedCorner) o;
|
||||
return mPosition == r.mPosition && mRadius == r.mRadius
|
||||
&& mCenter.equals(r.mCenter);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = 0;
|
||||
result = 31 * result + mPosition;
|
||||
result = 31 * result + mRadius;
|
||||
result = 31 * result + mCenter.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RoundedCorner{"
|
||||
+ "position=" + getPositionString(mPosition)
|
||||
+ ", radius=" + mRadius
|
||||
+ ", center=" + mCenter
|
||||
+ '}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel out, int flags) {
|
||||
out.writeInt(mPosition);
|
||||
out.writeInt(mRadius);
|
||||
out.writeInt(mCenter.x);
|
||||
out.writeInt(mCenter.y);
|
||||
}
|
||||
|
||||
public static final @NonNull Creator<RoundedCorner> CREATOR = new Creator<RoundedCorner>() {
|
||||
@Override
|
||||
public RoundedCorner createFromParcel(Parcel in) {
|
||||
return new RoundedCorner(in.readInt(), in.readInt(), in.readInt(), in.readInt());
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoundedCorner[] newArray(int size) {
|
||||
return new RoundedCorner[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
19
core/java/android/view/RoundedCorners.aidl
Normal file
19
core/java/android/view/RoundedCorners.aidl
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Copyright (c) 2021, 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;
|
||||
|
||||
parcelable RoundedCorners;
|
||||
380
core/java/android/view/RoundedCorners.java
Normal file
380
core/java/android/view/RoundedCorners.java
Normal file
@@ -0,0 +1,380 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.RoundedCorner.POSITION_BOTTOM_LEFT;
|
||||
import static android.view.RoundedCorner.POSITION_BOTTOM_RIGHT;
|
||||
import static android.view.RoundedCorner.POSITION_TOP_LEFT;
|
||||
import static android.view.RoundedCorner.POSITION_TOP_RIGHT;
|
||||
import static android.view.Surface.ROTATION_0;
|
||||
import static android.view.Surface.ROTATION_270;
|
||||
import static android.view.Surface.ROTATION_90;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Point;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.Pair;
|
||||
import android.view.RoundedCorner.Position;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* A class to create & manage all the {@link RoundedCorner} on the display.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class RoundedCorners implements Parcelable {
|
||||
|
||||
public static final RoundedCorners NO_ROUNDED_CORNERS = new RoundedCorners(
|
||||
new RoundedCorner(POSITION_TOP_LEFT), new RoundedCorner(POSITION_TOP_RIGHT),
|
||||
new RoundedCorner(POSITION_BOTTOM_RIGHT), new RoundedCorner(POSITION_BOTTOM_LEFT));
|
||||
|
||||
/**
|
||||
* The number of possible positions at which rounded corners can be located.
|
||||
*/
|
||||
public static final int ROUNDED_CORNER_POSITION_LENGTH = 4;
|
||||
|
||||
private static final Object CACHE_LOCK = new Object();
|
||||
|
||||
@GuardedBy("CACHE_LOCK")
|
||||
private static int sCachedDisplayWidth;
|
||||
@GuardedBy("CACHE_LOCK")
|
||||
private static int sCachedDisplayHeight;
|
||||
@GuardedBy("CACHE_LOCK")
|
||||
private static Pair<Integer, Integer> sCachedRadii;
|
||||
@GuardedBy("CACHE_LOCK")
|
||||
private static RoundedCorners sCachedRoundedCorners;
|
||||
|
||||
@VisibleForTesting
|
||||
public final RoundedCorner[] mRoundedCorners;
|
||||
|
||||
public RoundedCorners(RoundedCorner[] roundedCorners) {
|
||||
mRoundedCorners = roundedCorners;
|
||||
}
|
||||
|
||||
public RoundedCorners(RoundedCorner topLeft, RoundedCorner topRight, RoundedCorner bottomRight,
|
||||
RoundedCorner bottomLeft) {
|
||||
mRoundedCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH];
|
||||
mRoundedCorners[POSITION_TOP_LEFT] = topLeft;
|
||||
mRoundedCorners[POSITION_TOP_RIGHT] = topRight;
|
||||
mRoundedCorners[POSITION_BOTTOM_RIGHT] = bottomRight;
|
||||
mRoundedCorners[POSITION_BOTTOM_LEFT] = bottomLeft;
|
||||
}
|
||||
|
||||
public RoundedCorners(RoundedCorners roundedCorners) {
|
||||
mRoundedCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH];
|
||||
for (int i = 0; i < ROUNDED_CORNER_POSITION_LENGTH; ++i) {
|
||||
mRoundedCorners[i] = new RoundedCorner(roundedCorners.mRoundedCorners[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the rounded corners according to @android:dimen/rounded_corner_radius,
|
||||
* @android:dimen/rounded_corner_radius_top and @android:dimen/rounded_corner_radius_bottom
|
||||
*/
|
||||
public static RoundedCorners fromResources(
|
||||
Resources res, int displayWidth, int displayHeight) {
|
||||
return fromRadii(loadRoundedCornerRadii(res), displayWidth, displayHeight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the rounded corners from radius
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public static RoundedCorners fromRadii(Pair<Integer, Integer> radii, int displayWidth,
|
||||
int displayHeight) {
|
||||
if (radii == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
synchronized (CACHE_LOCK) {
|
||||
if (radii.equals(sCachedRadii) && sCachedDisplayWidth == displayWidth
|
||||
&& sCachedDisplayHeight == displayHeight) {
|
||||
return sCachedRoundedCorners;
|
||||
}
|
||||
}
|
||||
|
||||
final RoundedCorner[] roundedCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH];
|
||||
final int topRadius = radii.first > 0 ? radii.first : 0;
|
||||
final int bottomRadius = radii.second > 0 ? radii.second : 0;
|
||||
for (int i = 0; i < ROUNDED_CORNER_POSITION_LENGTH; i++) {
|
||||
roundedCorners[i] = createRoundedCorner(
|
||||
i,
|
||||
i <= POSITION_TOP_RIGHT ? topRadius : bottomRadius,
|
||||
displayWidth,
|
||||
displayHeight);
|
||||
}
|
||||
|
||||
final RoundedCorners result = new RoundedCorners(roundedCorners);
|
||||
synchronized (CACHE_LOCK) {
|
||||
sCachedDisplayWidth = displayWidth;
|
||||
sCachedDisplayHeight = displayHeight;
|
||||
sCachedRadii = radii;
|
||||
sCachedRoundedCorners = result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the rounded corner radii from resources.
|
||||
*
|
||||
* @param res
|
||||
* @return a Pair of radius. The first is the top rounded corner radius and second is the
|
||||
* bottom corner radius.
|
||||
*/
|
||||
@Nullable
|
||||
private static Pair<Integer, Integer> loadRoundedCornerRadii(Resources res) {
|
||||
final int radiusDefault = res.getDimensionPixelSize(R.dimen.rounded_corner_radius);
|
||||
final int radiusTop = res.getDimensionPixelSize(R.dimen.rounded_corner_radius_top);
|
||||
final int radiusBottom = res.getDimensionPixelSize(R.dimen.rounded_corner_radius_bottom);
|
||||
if (radiusDefault == 0 && radiusTop == 0 && radiusBottom == 0) {
|
||||
return null;
|
||||
}
|
||||
final Pair<Integer, Integer> radii = new Pair<>(
|
||||
radiusTop > 0 ? radiusTop : radiusDefault,
|
||||
radiusBottom > 0 ? radiusBottom : radiusDefault);
|
||||
return radii;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insets the reference frame of the rounded corners.
|
||||
*
|
||||
* @return a copy of this instance which has been inset
|
||||
*/
|
||||
public RoundedCorners inset(int insetLeft, int insetTop, int insetRight, int insetBottom) {
|
||||
final RoundedCorner[] roundedCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH];
|
||||
for (int i = 0; i < ROUNDED_CORNER_POSITION_LENGTH; i++) {
|
||||
roundedCorners[i] = insetRoundedCorner(i, insetLeft, insetTop, insetRight, insetBottom);
|
||||
}
|
||||
return new RoundedCorners(roundedCorners);
|
||||
}
|
||||
|
||||
private RoundedCorner insetRoundedCorner(@Position int position, int insetLeft,
|
||||
int insetTop, int insetRight, int insetBottom) {
|
||||
if (mRoundedCorners[position].isEmpty()) {
|
||||
return new RoundedCorner(position);
|
||||
}
|
||||
|
||||
final int radius = mRoundedCorners[position].getRadius();
|
||||
final Point center = mRoundedCorners[position].getCenter();
|
||||
boolean hasRoundedCorner;
|
||||
switch (position) {
|
||||
case POSITION_TOP_LEFT:
|
||||
hasRoundedCorner = radius > insetTop || radius > insetLeft;
|
||||
break;
|
||||
case POSITION_TOP_RIGHT:
|
||||
hasRoundedCorner = radius > insetTop || radius > insetRight;
|
||||
break;
|
||||
case POSITION_BOTTOM_RIGHT:
|
||||
hasRoundedCorner = radius > insetBottom || radius > insetRight;
|
||||
break;
|
||||
case POSITION_BOTTOM_LEFT:
|
||||
hasRoundedCorner = radius > insetBottom || radius > insetLeft;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException(
|
||||
"The position is not one of the RoundedCornerPosition =" + position);
|
||||
}
|
||||
return new RoundedCorner(
|
||||
position, radius,
|
||||
hasRoundedCorner ? center.x - insetLeft : 0,
|
||||
hasRoundedCorner ? center.y - insetTop : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link RoundedCorner} of the given position if there is one.
|
||||
*
|
||||
* @param position the position of the rounded corner on the display.
|
||||
* @return the rounded corner of the given position. Returns {@code null} if
|
||||
* {@link RoundedCorner#isEmpty()} is {@code true}.
|
||||
*/
|
||||
@Nullable
|
||||
public RoundedCorner getRoundedCorner(@Position int position) {
|
||||
return mRoundedCorners[position].isEmpty()
|
||||
? null : new RoundedCorner(mRoundedCorners[position]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the rounded corner of given position.
|
||||
*
|
||||
* @param position the position of this rounded corner
|
||||
* @param roundedCorner the rounded corner or null if there is none
|
||||
*/
|
||||
public void setRoundedCorner(@Position int position, @Nullable RoundedCorner roundedCorner) {
|
||||
mRoundedCorners[position] = roundedCorner == null
|
||||
? new RoundedCorner(position) : roundedCorner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of {@link RoundedCorner}s. Ordinal value of RoundedCornerPosition is used
|
||||
* as an index of the array.
|
||||
*
|
||||
* @return an array of {@link RoundedCorner}s, one for each rounded corner area.
|
||||
*/
|
||||
public RoundedCorner[] getAllRoundedCorners() {
|
||||
RoundedCorner[] roundedCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH];
|
||||
for (int i = 0; i < ROUNDED_CORNER_POSITION_LENGTH; ++i) {
|
||||
roundedCorners[i] = new RoundedCorner(roundedCorners[i]);
|
||||
}
|
||||
return roundedCorners;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a scaled RoundedCorners.
|
||||
*/
|
||||
public RoundedCorners scale(float scale) {
|
||||
if (scale == 1f) {
|
||||
return this;
|
||||
}
|
||||
|
||||
RoundedCorner[] roundedCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH];
|
||||
for (int i = 0; i < ROUNDED_CORNER_POSITION_LENGTH; ++i) {
|
||||
final RoundedCorner roundedCorner = mRoundedCorners[i];
|
||||
roundedCorners[i] = new RoundedCorner(
|
||||
i,
|
||||
(int) (roundedCorner.getRadius() * scale),
|
||||
(int) (roundedCorner.getCenter().x * scale),
|
||||
(int) (roundedCorner.getCenter().y * scale));
|
||||
}
|
||||
return new RoundedCorners(roundedCorners);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a rotated RoundedCorners.
|
||||
*/
|
||||
public RoundedCorners rotate(@Surface.Rotation int rotation, int initialDisplayWidth,
|
||||
int initialDisplayHeight) {
|
||||
if (rotation == ROTATION_0) {
|
||||
return this;
|
||||
}
|
||||
final boolean isSizeFlipped = rotation == ROTATION_90 || rotation == ROTATION_270;
|
||||
RoundedCorner[] newCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH];
|
||||
int newPosistion;
|
||||
for (int i = 0; i < mRoundedCorners.length; i++) {
|
||||
newPosistion = getRotatedIndex(i, rotation);
|
||||
newCorners[newPosistion] = createRoundedCorner(
|
||||
newPosistion,
|
||||
mRoundedCorners[i].getRadius(),
|
||||
isSizeFlipped ? initialDisplayHeight : initialDisplayWidth,
|
||||
isSizeFlipped ? initialDisplayWidth : initialDisplayHeight);
|
||||
}
|
||||
return new RoundedCorners(newCorners);
|
||||
}
|
||||
|
||||
private static RoundedCorner createRoundedCorner(@Position int position,
|
||||
int radius, int displayWidth, int displayHeight) {
|
||||
switch (position) {
|
||||
case POSITION_TOP_LEFT:
|
||||
return new RoundedCorner(
|
||||
POSITION_TOP_LEFT,
|
||||
radius,
|
||||
radius > 0 ? radius : 0,
|
||||
radius > 0 ? radius : 0);
|
||||
case POSITION_TOP_RIGHT:
|
||||
return new RoundedCorner(
|
||||
POSITION_TOP_RIGHT,
|
||||
radius,
|
||||
radius > 0 ? displayWidth - radius : 0,
|
||||
radius > 0 ? radius : 0);
|
||||
case POSITION_BOTTOM_RIGHT:
|
||||
return new RoundedCorner(
|
||||
POSITION_BOTTOM_RIGHT,
|
||||
radius,
|
||||
radius > 0 ? displayWidth - radius : 0,
|
||||
radius > 0 ? displayHeight - radius : 0);
|
||||
case POSITION_BOTTOM_LEFT:
|
||||
return new RoundedCorner(
|
||||
POSITION_BOTTOM_LEFT,
|
||||
radius,
|
||||
radius > 0 ? radius : 0,
|
||||
radius > 0 ? displayHeight - radius : 0);
|
||||
default:
|
||||
throw new IllegalArgumentException(
|
||||
"The position is not one of the RoundedCornerPosition =" + position);
|
||||
}
|
||||
}
|
||||
|
||||
private static int getRotatedIndex(int position, int rotation) {
|
||||
return (position - rotation + ROUNDED_CORNER_POSITION_LENGTH) % 4;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = 0;
|
||||
for (RoundedCorner roundedCorner : mRoundedCorners) {
|
||||
result = result * 31 + roundedCorner.hashCode();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this) {
|
||||
return true;
|
||||
}
|
||||
if (o instanceof RoundedCorners) {
|
||||
RoundedCorners r = (RoundedCorners) o;
|
||||
return Arrays.deepEquals(mRoundedCorners, ((RoundedCorners) o).mRoundedCorners);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RoundedCorners{" + Arrays.toString(mRoundedCorners) + "}";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
if (equals(NO_ROUNDED_CORNERS)) {
|
||||
dest.writeInt(0);
|
||||
} else {
|
||||
dest.writeInt(1);
|
||||
dest.writeTypedArray(mRoundedCorners, flags);
|
||||
}
|
||||
}
|
||||
|
||||
public static final @NonNull Creator<RoundedCorners> CREATOR = new Creator<RoundedCorners>() {
|
||||
@Override
|
||||
public RoundedCorners createFromParcel(Parcel in) {
|
||||
int variant = in.readInt();
|
||||
if (variant == 0) {
|
||||
return NO_ROUNDED_CORNERS;
|
||||
}
|
||||
RoundedCorner[] roundedCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH];
|
||||
in.readTypedArray(roundedCorners, RoundedCorner.CREATOR);
|
||||
return new RoundedCorners(roundedCorners);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoundedCorners[] newArray(int size) {
|
||||
return new RoundedCorners[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -82,6 +82,7 @@ public final class WindowInsets {
|
||||
@Nullable private Rect mTempRect;
|
||||
private final boolean mIsRound;
|
||||
@Nullable private final DisplayCutout mDisplayCutout;
|
||||
@Nullable private final RoundedCorners mRoundedCorners;
|
||||
|
||||
/**
|
||||
* In multi-window we force show the navigation bar. Because we don't want that the surface size
|
||||
@@ -125,11 +126,12 @@ public final class WindowInsets {
|
||||
* @hide
|
||||
* @deprecated Use {@link WindowInsets(SparseArray, SparseArray, boolean, boolean, DisplayCutout)}
|
||||
*/
|
||||
public WindowInsets(Rect systemWindowInsetsRect, Rect stableInsetsRect,
|
||||
boolean isRound, boolean alwaysConsumeSystemBars, DisplayCutout displayCutout) {
|
||||
@Deprecated
|
||||
public WindowInsets(Rect systemWindowInsetsRect, Rect stableInsetsRect, boolean isRound,
|
||||
boolean alwaysConsumeSystemBars, DisplayCutout displayCutout) {
|
||||
this(createCompatTypeMap(systemWindowInsetsRect), createCompatTypeMap(stableInsetsRect),
|
||||
createCompatVisibilityMap(createCompatTypeMap(systemWindowInsetsRect)),
|
||||
isRound, alwaysConsumeSystemBars, displayCutout, systemBars(),
|
||||
isRound, alwaysConsumeSystemBars, displayCutout, null, systemBars(),
|
||||
false /* compatIgnoreVisibility */);
|
||||
}
|
||||
|
||||
@@ -150,7 +152,8 @@ public final class WindowInsets {
|
||||
boolean[] typeVisibilityMap,
|
||||
boolean isRound,
|
||||
boolean alwaysConsumeSystemBars, DisplayCutout displayCutout,
|
||||
@InsetsType int compatInsetsTypes, boolean compatIgnoreVisibility) {
|
||||
RoundedCorners roundedCorners, @InsetsType int compatInsetsTypes,
|
||||
boolean compatIgnoreVisibility) {
|
||||
mSystemWindowInsetsConsumed = typeInsetsMap == null;
|
||||
mTypeInsetsMap = mSystemWindowInsetsConsumed
|
||||
? new Insets[SIZE]
|
||||
@@ -170,6 +173,8 @@ public final class WindowInsets {
|
||||
mDisplayCutoutConsumed = displayCutout == null;
|
||||
mDisplayCutout = (mDisplayCutoutConsumed || displayCutout.isEmpty())
|
||||
? null : displayCutout;
|
||||
|
||||
mRoundedCorners = roundedCorners;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -182,6 +187,7 @@ public final class WindowInsets {
|
||||
src.mStableInsetsConsumed ? null : src.mTypeMaxInsetsMap,
|
||||
src.mTypeVisibilityMap, src.mIsRound,
|
||||
src.mAlwaysConsumeSystemBars, displayCutoutCopyConstructorArgument(src),
|
||||
src.mRoundedCorners,
|
||||
src.mCompatInsetsTypes,
|
||||
src.mCompatIgnoreVisibility);
|
||||
}
|
||||
@@ -235,7 +241,7 @@ public final class WindowInsets {
|
||||
@UnsupportedAppUsage
|
||||
public WindowInsets(Rect systemWindowInsets) {
|
||||
this(createCompatTypeMap(systemWindowInsets), null, new boolean[SIZE], false, false, null,
|
||||
systemBars(), false /* compatIgnoreVisibility */);
|
||||
null, systemBars(), false /* compatIgnoreVisibility */);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -466,7 +472,7 @@ public final class WindowInsets {
|
||||
public boolean hasInsets() {
|
||||
return !getInsets(mTypeInsetsMap, all()).equals(Insets.NONE)
|
||||
|| !getInsets(mTypeMaxInsetsMap, all()).equals(Insets.NONE)
|
||||
|| mDisplayCutout != null;
|
||||
|| mDisplayCutout != null || mRoundedCorners != null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -486,6 +492,23 @@ public final class WindowInsets {
|
||||
return mDisplayCutout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link RoundedCorner} of the given position if there is one.
|
||||
*
|
||||
* @param position the position of the rounded corner on the display. The value should be one of
|
||||
* the following:
|
||||
* {@link RoundedCorner#POSITION_TOP_LEFT},
|
||||
* {@link RoundedCorner#POSITION_TOP_RIGHT},
|
||||
* {@link RoundedCorner#POSITION_BOTTOM_RIGHT},
|
||||
* {@link RoundedCorner#POSITION_BOTTOM_LEFT}.
|
||||
* @return the rounded corner of the given position. Returns {@code null} if there is none or
|
||||
* the rounded corner area is not inside the application's bounds.
|
||||
*/
|
||||
@Nullable
|
||||
public RoundedCorner getRoundedCorner(@RoundedCorner.Position int position) {
|
||||
return mRoundedCorners == null ? null : mRoundedCorners.getRoundedCorner(position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy of this WindowInsets with the cutout fully consumed.
|
||||
*
|
||||
@@ -501,7 +524,7 @@ public final class WindowInsets {
|
||||
mStableInsetsConsumed ? null : mTypeMaxInsetsMap,
|
||||
mTypeVisibilityMap,
|
||||
mIsRound, mAlwaysConsumeSystemBars,
|
||||
null /* displayCutout */,
|
||||
null /* displayCutout */, mRoundedCorners,
|
||||
mCompatInsetsTypes, mCompatIgnoreVisibility);
|
||||
}
|
||||
|
||||
@@ -553,7 +576,7 @@ public final class WindowInsets {
|
||||
mTypeVisibilityMap,
|
||||
mIsRound, mAlwaysConsumeSystemBars,
|
||||
displayCutoutCopyConstructorArgument(this),
|
||||
mCompatInsetsTypes, mCompatIgnoreVisibility);
|
||||
mRoundedCorners, mCompatInsetsTypes, mCompatIgnoreVisibility);
|
||||
}
|
||||
|
||||
// TODO(b/119190588): replace @code with @link below
|
||||
@@ -856,6 +879,8 @@ public final class WindowInsets {
|
||||
|
||||
result.append(mDisplayCutout != null ? "cutout=" + mDisplayCutout : "");
|
||||
result.append("\n ");
|
||||
result.append(mRoundedCorners != null ? "roundedCorners=" + mRoundedCorners : "");
|
||||
result.append("\n ");
|
||||
result.append(isRound() ? "round" : "");
|
||||
result.append("}");
|
||||
return result.toString();
|
||||
@@ -947,6 +972,9 @@ public final class WindowInsets {
|
||||
: mDisplayCutout == null
|
||||
? DisplayCutout.NO_CUTOUT
|
||||
: mDisplayCutout.inset(left, top, right, bottom),
|
||||
mRoundedCorners == null
|
||||
? RoundedCorners.NO_ROUNDED_CORNERS
|
||||
: mRoundedCorners.inset(left, top, right, bottom),
|
||||
mCompatInsetsTypes, mCompatIgnoreVisibility);
|
||||
}
|
||||
|
||||
@@ -964,13 +992,14 @@ public final class WindowInsets {
|
||||
&& Arrays.equals(mTypeInsetsMap, that.mTypeInsetsMap)
|
||||
&& Arrays.equals(mTypeMaxInsetsMap, that.mTypeMaxInsetsMap)
|
||||
&& Arrays.equals(mTypeVisibilityMap, that.mTypeVisibilityMap)
|
||||
&& Objects.equals(mDisplayCutout, that.mDisplayCutout);
|
||||
&& Objects.equals(mDisplayCutout, that.mDisplayCutout)
|
||||
&& Objects.equals(mRoundedCorners, that.mRoundedCorners);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(Arrays.hashCode(mTypeInsetsMap), Arrays.hashCode(mTypeMaxInsetsMap),
|
||||
Arrays.hashCode(mTypeVisibilityMap), mIsRound, mDisplayCutout,
|
||||
Arrays.hashCode(mTypeVisibilityMap), mIsRound, mDisplayCutout, mRoundedCorners,
|
||||
mAlwaysConsumeSystemBars, mSystemWindowInsetsConsumed, mStableInsetsConsumed,
|
||||
mDisplayCutoutConsumed);
|
||||
}
|
||||
@@ -1032,6 +1061,7 @@ public final class WindowInsets {
|
||||
private boolean mStableInsetsConsumed = true;
|
||||
|
||||
private DisplayCutout mDisplayCutout;
|
||||
private RoundedCorners mRoundedCorners = RoundedCorners.NO_ROUNDED_CORNERS;
|
||||
|
||||
private boolean mIsRound;
|
||||
private boolean mAlwaysConsumeSystemBars;
|
||||
@@ -1057,6 +1087,7 @@ public final class WindowInsets {
|
||||
mSystemInsetsConsumed = insets.mSystemWindowInsetsConsumed;
|
||||
mStableInsetsConsumed = insets.mStableInsetsConsumed;
|
||||
mDisplayCutout = displayCutoutCopyConstructorArgument(insets);
|
||||
mRoundedCorners = insets.mRoundedCorners;
|
||||
mIsRound = insets.mIsRound;
|
||||
mAlwaysConsumeSystemBars = insets.mAlwaysConsumeSystemBars;
|
||||
}
|
||||
@@ -1260,6 +1291,29 @@ public final class WindowInsets {
|
||||
return this;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@NonNull
|
||||
public Builder setRoundedCorners(RoundedCorners roundedCorners) {
|
||||
mRoundedCorners = roundedCorners != null
|
||||
? roundedCorners : RoundedCorners.NO_ROUNDED_CORNERS;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the rounded corner of given position.
|
||||
*
|
||||
* @see #getRoundedCorner(int)
|
||||
* @param position the position of this rounded corner
|
||||
* @param roundedCorner the rounded corner or null if there is none
|
||||
* @return itself
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setRoundedCorner(@RoundedCorner.Position int position,
|
||||
@Nullable RoundedCorner roundedCorner) {
|
||||
mRoundedCorners.setRoundedCorner(position, roundedCorner);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@NonNull
|
||||
public Builder setRound(boolean round) {
|
||||
@@ -1283,7 +1337,7 @@ public final class WindowInsets {
|
||||
public WindowInsets build() {
|
||||
return new WindowInsets(mSystemInsetsConsumed ? null : mTypeInsetsMap,
|
||||
mStableInsetsConsumed ? null : mTypeMaxInsetsMap, mTypeVisibilityMap,
|
||||
mIsRound, mAlwaysConsumeSystemBars, mDisplayCutout,
|
||||
mIsRound, mAlwaysConsumeSystemBars, mDisplayCutout, mRoundedCorners,
|
||||
systemBars(), false /* compatIgnoreVisibility */);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,10 @@ import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
|
||||
import static android.view.InsetsState.ITYPE_IME;
|
||||
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
|
||||
import static android.view.InsetsState.ITYPE_STATUS_BAR;
|
||||
import static android.view.RoundedCorner.POSITION_BOTTOM_LEFT;
|
||||
import static android.view.RoundedCorner.POSITION_BOTTOM_RIGHT;
|
||||
import static android.view.RoundedCorner.POSITION_TOP_LEFT;
|
||||
import static android.view.RoundedCorner.POSITION_TOP_RIGHT;
|
||||
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
|
||||
import static android.view.WindowInsets.Type.ime;
|
||||
import static android.view.WindowInsets.Type.navigationBars;
|
||||
@@ -427,6 +431,27 @@ public class InsetsStateTest {
|
||||
cutout.getBoundingRectBottom());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalculateRelativeRoundedCorners() {
|
||||
mState.setDisplayFrame(new Rect(0, 0, 200, 400));
|
||||
mState.setRoundedCorners(new RoundedCorners(
|
||||
new RoundedCorner(POSITION_TOP_LEFT, 10, 10, 10),
|
||||
new RoundedCorner(POSITION_TOP_RIGHT, 10, 190, 10),
|
||||
new RoundedCorner(POSITION_BOTTOM_RIGHT, 20, 180, 380),
|
||||
new RoundedCorner(POSITION_BOTTOM_LEFT, 20, 20, 380)));
|
||||
WindowInsets windowInsets = mState.calculateInsets(new Rect(1, 2, 197, 396), null, false,
|
||||
false, SOFT_INPUT_ADJUST_UNSPECIFIED, 0, 0, TYPE_APPLICATION,
|
||||
WINDOWING_MODE_UNDEFINED, new SparseIntArray());
|
||||
assertEquals(new RoundedCorner(POSITION_TOP_LEFT, 10, 9, 8),
|
||||
windowInsets.getRoundedCorner(POSITION_TOP_LEFT));
|
||||
assertEquals(new RoundedCorner(POSITION_TOP_RIGHT, 10, 189, 8),
|
||||
windowInsets.getRoundedCorner(POSITION_TOP_RIGHT));
|
||||
assertEquals(new RoundedCorner(POSITION_BOTTOM_RIGHT, 20, 179, 378),
|
||||
windowInsets.getRoundedCorner(POSITION_BOTTOM_RIGHT));
|
||||
assertEquals(new RoundedCorner(POSITION_BOTTOM_LEFT, 20, 19, 378),
|
||||
windowInsets.getRoundedCorner(POSITION_BOTTOM_LEFT));
|
||||
}
|
||||
|
||||
private void assertEqualsAndHashCode() {
|
||||
assertEquals(mState, mState2);
|
||||
assertEquals(mState.hashCode(), mState2.hashCode());
|
||||
|
||||
72
core/tests/coretests/src/android/view/RoundedCornerTest.java
Normal file
72
core/tests/coretests/src/android/view/RoundedCornerTest.java
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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 org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import android.graphics.Point;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
@Presubmit
|
||||
public class RoundedCornerTest {
|
||||
|
||||
@Test
|
||||
public void testGetPosition() {
|
||||
RoundedCorner roundedCorner = new RoundedCorner(
|
||||
RoundedCorner.POSITION_BOTTOM_LEFT, 2, 3, 4);
|
||||
assertThat(roundedCorner.getPosition(), is(RoundedCorner.POSITION_BOTTOM_LEFT));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRadius() {
|
||||
RoundedCorner roundedCorner = new RoundedCorner(
|
||||
RoundedCorner.POSITION_BOTTOM_LEFT, 2, 3, 4);
|
||||
assertThat(roundedCorner.getRadius(), is(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetCenter() {
|
||||
RoundedCorner roundedCorner = new RoundedCorner(
|
||||
RoundedCorner.POSITION_BOTTOM_LEFT, 2, 3, 4);
|
||||
assertThat(roundedCorner.getCenter(), equalTo(new Point(3, 4)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsEmpty() {
|
||||
RoundedCorner roundedCorner = new RoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT);
|
||||
assertThat(roundedCorner.isEmpty(), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEquals() {
|
||||
RoundedCorner roundedCorner = new RoundedCorner(
|
||||
RoundedCorner.POSITION_BOTTOM_LEFT, 2, 3, 4);
|
||||
RoundedCorner roundedCorner2 = new RoundedCorner(
|
||||
RoundedCorner.POSITION_BOTTOM_LEFT, 2, 3, 4);
|
||||
assertThat(roundedCorner, equalTo(roundedCorner2));
|
||||
}
|
||||
}
|
||||
202
core/tests/coretests/src/android/view/RoundedCornersTest.java
Normal file
202
core/tests/coretests/src/android/view/RoundedCornersTest.java
Normal file
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.RoundedCorner.POSITION_BOTTOM_LEFT;
|
||||
import static android.view.RoundedCorner.POSITION_BOTTOM_RIGHT;
|
||||
import static android.view.RoundedCorner.POSITION_TOP_LEFT;
|
||||
import static android.view.RoundedCorner.POSITION_TOP_RIGHT;
|
||||
import static android.view.Surface.ROTATION_270;
|
||||
import static android.view.Surface.ROTATION_90;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.hamcrest.Matchers.sameInstance;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
@Presubmit
|
||||
public class RoundedCornersTest {
|
||||
|
||||
final RoundedCorners mRoundedCorners = new RoundedCorners(
|
||||
new RoundedCorner(POSITION_TOP_LEFT, 10, 10, 10),
|
||||
new RoundedCorner(POSITION_TOP_RIGHT, 10, 190, 10),
|
||||
new RoundedCorner(POSITION_BOTTOM_RIGHT, 20, 180, 380),
|
||||
new RoundedCorner(POSITION_BOTTOM_LEFT, 20, 20, 380));
|
||||
|
||||
@Test
|
||||
public void testGetRoundedCorner() {
|
||||
final Pair<Integer, Integer> radius = new Pair<>(10, 20);
|
||||
RoundedCorners roundedCorners = RoundedCorners.fromRadii(radius, 200, 400);
|
||||
|
||||
assertThat(roundedCorners.getRoundedCorner(POSITION_TOP_LEFT),
|
||||
equalTo(new RoundedCorner(POSITION_TOP_LEFT, 10, 10, 10)));
|
||||
assertThat(roundedCorners.getRoundedCorner(POSITION_TOP_RIGHT),
|
||||
equalTo(new RoundedCorner(POSITION_TOP_RIGHT, 10, 190, 10)));
|
||||
assertThat(roundedCorners.getRoundedCorner(POSITION_BOTTOM_RIGHT),
|
||||
equalTo(new RoundedCorner(POSITION_BOTTOM_RIGHT, 20, 180, 380)));
|
||||
assertThat(roundedCorners.getRoundedCorner(POSITION_BOTTOM_LEFT),
|
||||
equalTo(new RoundedCorner(POSITION_BOTTOM_LEFT, 20, 20, 380)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRoundedCorner_noRoundedCorners() {
|
||||
RoundedCorners roundedCorners = RoundedCorners.NO_ROUNDED_CORNERS;
|
||||
|
||||
assertThat(roundedCorners.getRoundedCorner(POSITION_TOP_LEFT), nullValue());
|
||||
assertThat(roundedCorners.getRoundedCorner(POSITION_TOP_RIGHT), nullValue());
|
||||
assertThat(roundedCorners.getRoundedCorner(POSITION_BOTTOM_RIGHT), nullValue());
|
||||
assertThat(roundedCorners.getRoundedCorner(POSITION_BOTTOM_LEFT), nullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHashCode() {
|
||||
assertThat(mRoundedCorners.hashCode(),
|
||||
equalTo(RoundedCorners.fromRadii(new Pair<>(10, 20), 200, 400).hashCode()));
|
||||
assertThat(mRoundedCorners.hashCode(),
|
||||
not(equalTo(RoundedCorners.fromRadii(new Pair<>(5, 10), 200, 400).hashCode())));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEquals() {
|
||||
assertThat(mRoundedCorners,
|
||||
equalTo(RoundedCorners.fromRadii(new Pair<>(10, 20), 200, 400)));
|
||||
|
||||
assertThat(mRoundedCorners,
|
||||
not(equalTo(RoundedCorners.fromRadii(new Pair<>(5, 10), 200, 400))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetRoundedCorner() {
|
||||
RoundedCorner roundedCorner = new RoundedCorner(POSITION_BOTTOM_LEFT, 5, 6, 7);
|
||||
mRoundedCorners.setRoundedCorner(POSITION_BOTTOM_LEFT, roundedCorner);
|
||||
|
||||
assertThat(mRoundedCorners.getRoundedCorner(POSITION_BOTTOM_LEFT), equalTo(roundedCorner));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetRoundedCorner_null() {
|
||||
mRoundedCorners.setRoundedCorner(POSITION_BOTTOM_LEFT, null);
|
||||
|
||||
assertThat(mRoundedCorners.mRoundedCorners[POSITION_BOTTOM_LEFT],
|
||||
equalTo(new RoundedCorner(POSITION_BOTTOM_LEFT)));
|
||||
assertThat(mRoundedCorners.getRoundedCorner(POSITION_BOTTOM_LEFT), nullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsetRoundedCorners_partialOverlap() {
|
||||
RoundedCorners roundedCorners = mRoundedCorners.inset(1, 2, 3, 4);
|
||||
|
||||
assertThat(roundedCorners.mRoundedCorners[POSITION_TOP_LEFT],
|
||||
equalTo(new RoundedCorner(POSITION_TOP_LEFT, 10, 9, 8)));
|
||||
assertThat(roundedCorners.mRoundedCorners[POSITION_TOP_RIGHT],
|
||||
equalTo(new RoundedCorner(POSITION_TOP_RIGHT, 10, 189, 8)));
|
||||
assertThat(roundedCorners.mRoundedCorners[POSITION_BOTTOM_RIGHT],
|
||||
equalTo(new RoundedCorner(POSITION_BOTTOM_RIGHT, 20, 179, 378)));
|
||||
assertThat(roundedCorners.mRoundedCorners[POSITION_BOTTOM_LEFT],
|
||||
equalTo(new RoundedCorner(POSITION_BOTTOM_LEFT, 20, 19, 378)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsetRoundedCorners_noOverlap() {
|
||||
RoundedCorners roundedCorners = mRoundedCorners.inset(20, 20, 20, 20);
|
||||
|
||||
assertThat(roundedCorners.mRoundedCorners[POSITION_TOP_LEFT].isEmpty(), is(true));
|
||||
assertThat(roundedCorners.mRoundedCorners[POSITION_TOP_RIGHT].isEmpty(), is(true));
|
||||
assertThat(roundedCorners.mRoundedCorners[POSITION_BOTTOM_RIGHT].isEmpty(), is(true));
|
||||
assertThat(roundedCorners.mRoundedCorners[POSITION_BOTTOM_LEFT].isEmpty(), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRotateRoundedCorners_90() {
|
||||
final Pair<Integer, Integer> radius = new Pair<>(10, 20);
|
||||
RoundedCorners roundedCorners = RoundedCorners.fromRadii(radius, 200, 400)
|
||||
.rotate(ROTATION_90, 200, 400);
|
||||
|
||||
assertThat(roundedCorners.getRoundedCorner(POSITION_TOP_LEFT),
|
||||
equalTo(new RoundedCorner(POSITION_TOP_LEFT, 10, 10, 10)));
|
||||
assertThat(roundedCorners.getRoundedCorner(POSITION_TOP_RIGHT),
|
||||
equalTo(new RoundedCorner(POSITION_TOP_RIGHT, 20, 380, 20)));
|
||||
assertThat(roundedCorners.getRoundedCorner(POSITION_BOTTOM_RIGHT),
|
||||
equalTo(new RoundedCorner(POSITION_BOTTOM_RIGHT, 20, 380, 180)));
|
||||
assertThat(roundedCorners.getRoundedCorner(POSITION_BOTTOM_LEFT),
|
||||
equalTo(new RoundedCorner(POSITION_BOTTOM_LEFT, 10, 10, 190)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRotateRoundedCorners_270() {
|
||||
final Pair<Integer, Integer> radius = new Pair<>(10, 20);
|
||||
RoundedCorners roundedCorners = RoundedCorners.fromRadii(radius, 200, 400)
|
||||
.rotate(ROTATION_270, 200, 400);
|
||||
|
||||
assertThat(roundedCorners.getRoundedCorner(POSITION_TOP_LEFT),
|
||||
equalTo(new RoundedCorner(POSITION_TOP_LEFT, 20, 20, 20)));
|
||||
assertThat(roundedCorners.getRoundedCorner(POSITION_TOP_RIGHT),
|
||||
equalTo(new RoundedCorner(POSITION_TOP_RIGHT, 10, 390, 10)));
|
||||
assertThat(roundedCorners.getRoundedCorner(POSITION_BOTTOM_RIGHT),
|
||||
equalTo(new RoundedCorner(POSITION_BOTTOM_RIGHT, 10, 390, 190)));
|
||||
assertThat(roundedCorners.getRoundedCorner(POSITION_BOTTOM_LEFT),
|
||||
equalTo(new RoundedCorner(POSITION_BOTTOM_LEFT, 20, 20, 180)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromRadius_cache() {
|
||||
final Pair<Integer, Integer> radius = new Pair<>(10, 20);
|
||||
RoundedCorners cached = RoundedCorners.fromRadii(radius, 200, 400);
|
||||
|
||||
assertThat(RoundedCorners.fromRadii(radius, 200, 400), sameInstance(cached));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromRadius_wontCacheIfRadiusChanged() {
|
||||
final Pair<Integer, Integer> radius = new Pair<>(10, 20);
|
||||
RoundedCorners cached = RoundedCorners.fromRadii(radius, 200, 400);
|
||||
|
||||
assertThat(RoundedCorners.fromRadii(new Pair<>(5, 10), 200, 400),
|
||||
not(sameInstance(cached)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromRadius_wontCacheIfDisplayWidthChanged() {
|
||||
final Pair<Integer, Integer> radius = new Pair<>(10, 20);
|
||||
RoundedCorners cached = RoundedCorners.fromRadii(radius, 200, 400);
|
||||
|
||||
assertThat(RoundedCorners.fromRadii(radius, 100, 400),
|
||||
not(sameInstance(cached)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromRadius_wontCacheIfDisplayHeightChanged() {
|
||||
final Pair<Integer, Integer> radius = new Pair<>(10, 20);
|
||||
RoundedCorners cached = RoundedCorners.fromRadii(radius, 200, 400);
|
||||
|
||||
assertThat(RoundedCorners.fromRadii(radius, 200, 300),
|
||||
not(sameInstance(cached)));
|
||||
}
|
||||
}
|
||||
@@ -61,7 +61,7 @@ public class WindowInsetsTest {
|
||||
WindowInsets.assignCompatInsets(maxInsets, new Rect(0, 10, 0, 0));
|
||||
WindowInsets.assignCompatInsets(insets, new Rect(0, 0, 0, 0));
|
||||
WindowInsets windowInsets = new WindowInsets(insets, maxInsets, visible, false, false, null,
|
||||
systemBars(), true /* compatIgnoreVisibility */);
|
||||
null, systemBars(), true /* compatIgnoreVisibility */);
|
||||
assertEquals(Insets.of(0, 10, 0, 0), windowInsets.getSystemWindowInsets());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import android.view.Display;
|
||||
import android.view.DisplayAddress;
|
||||
import android.view.DisplayCutout;
|
||||
import android.view.DisplayEventReceiver;
|
||||
import android.view.RoundedCorners;
|
||||
import android.view.Surface;
|
||||
|
||||
import com.android.internal.BrightnessSynchronizer;
|
||||
@@ -280,6 +281,11 @@ final class DisplayDeviceInfo {
|
||||
*/
|
||||
public DisplayCutout displayCutout;
|
||||
|
||||
/**
|
||||
* The {@link RoundedCorners} if present or {@code null} otherwise.
|
||||
*/
|
||||
public RoundedCorners roundedCorners;
|
||||
|
||||
/**
|
||||
* The touch attachment, per {@link DisplayViewport#touch}.
|
||||
*/
|
||||
@@ -401,7 +407,8 @@ final class DisplayDeviceInfo {
|
||||
|| !BrightnessSynchronizer.floatEquals(brightnessMinimum, other.brightnessMinimum)
|
||||
|| !BrightnessSynchronizer.floatEquals(brightnessMaximum, other.brightnessMaximum)
|
||||
|| !BrightnessSynchronizer.floatEquals(brightnessDefault,
|
||||
other.brightnessDefault)) {
|
||||
other.brightnessDefault)
|
||||
|| !Objects.equals(roundedCorners, other.roundedCorners)) {
|
||||
diff |= DIFF_OTHER;
|
||||
}
|
||||
return diff;
|
||||
@@ -444,6 +451,7 @@ final class DisplayDeviceInfo {
|
||||
brightnessMinimum = other.brightnessMinimum;
|
||||
brightnessMaximum = other.brightnessMaximum;
|
||||
brightnessDefault = other.brightnessDefault;
|
||||
roundedCorners = other.roundedCorners;
|
||||
}
|
||||
|
||||
// For debugging purposes
|
||||
@@ -487,6 +495,9 @@ final class DisplayDeviceInfo {
|
||||
sb.append(", brightnessMinimum ").append(brightnessMinimum);
|
||||
sb.append(", brightnessMaximum ").append(brightnessMaximum);
|
||||
sb.append(", brightnessDefault ").append(brightnessDefault);
|
||||
if (roundedCorners != null) {
|
||||
sb.append(", roundedCorners ").append(roundedCorners);
|
||||
}
|
||||
sb.append(flagsToString(flags));
|
||||
sb.append("}");
|
||||
return sb.toString();
|
||||
|
||||
@@ -35,6 +35,7 @@ import android.view.Display;
|
||||
import android.view.DisplayAddress;
|
||||
import android.view.DisplayCutout;
|
||||
import android.view.DisplayEventReceiver;
|
||||
import android.view.RoundedCorners;
|
||||
import android.view.SurfaceControl;
|
||||
|
||||
import com.android.internal.BrightnessSynchronizer;
|
||||
@@ -604,6 +605,8 @@ final class LocalDisplayAdapter extends DisplayAdapter {
|
||||
}
|
||||
mInfo.displayCutout = DisplayCutout.fromResourcesRectApproximation(res,
|
||||
mInfo.width, mInfo.height);
|
||||
mInfo.roundedCorners = RoundedCorners.fromResources(
|
||||
res, mInfo.width, mInfo.height);
|
||||
} else {
|
||||
if (!res.getBoolean(
|
||||
com.android.internal.R.bool.config_localDisplaysMirrorContent)) {
|
||||
|
||||
@@ -195,6 +195,7 @@ final class LogicalDisplay {
|
||||
info.logicalDensityDpi = mOverrideDisplayInfo.logicalDensityDpi;
|
||||
info.physicalXDpi = mOverrideDisplayInfo.physicalXDpi;
|
||||
info.physicalYDpi = mOverrideDisplayInfo.physicalYDpi;
|
||||
info.roundedCorners = mOverrideDisplayInfo.roundedCorners;
|
||||
}
|
||||
mInfo.set(info);
|
||||
}
|
||||
@@ -386,6 +387,7 @@ final class LogicalDisplay {
|
||||
mBaseDisplayInfo.brightnessMinimum = deviceInfo.brightnessMinimum;
|
||||
mBaseDisplayInfo.brightnessMaximum = deviceInfo.brightnessMaximum;
|
||||
mBaseDisplayInfo.brightnessDefault = deviceInfo.brightnessDefault;
|
||||
mBaseDisplayInfo.roundedCorners = deviceInfo.roundedCorners;
|
||||
mPrimaryDisplayDeviceInfo = deviceInfo;
|
||||
mInfo.set(null);
|
||||
}
|
||||
|
||||
@@ -204,6 +204,7 @@ import android.view.InsetsState;
|
||||
import android.view.InsetsState.InternalInsetsType;
|
||||
import android.view.MagnificationSpec;
|
||||
import android.view.RemoteAnimationDefinition;
|
||||
import android.view.RoundedCorners;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.SurfaceControl.Transaction;
|
||||
@@ -337,6 +338,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
||||
= new RotationCache<>(this::calculateDisplayCutoutForRotationUncached);
|
||||
boolean mIgnoreDisplayCutout;
|
||||
|
||||
RoundedCorners mInitialRoundedCorners;
|
||||
private final RotationCache<RoundedCorners, RoundedCorners> mRoundedCornerCache =
|
||||
new RotationCache<>(this::calculateRoundedCornersForRotationUncached);
|
||||
|
||||
/**
|
||||
* Overridden display size. Initialized with {@link #mInitialDisplayWidth}
|
||||
* and {@link #mInitialDisplayHeight}, but can be set via shell command "adb shell wm size".
|
||||
@@ -983,7 +988,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
||||
isDefaultDisplay = mDisplayId == DEFAULT_DISPLAY;
|
||||
mInsetsStateController = new InsetsStateController(this);
|
||||
mDisplayFrames = new DisplayFrames(mDisplayId, mInsetsStateController.getRawInsetsState(),
|
||||
mDisplayInfo, calculateDisplayCutoutForRotation(mDisplayInfo.rotation));
|
||||
mDisplayInfo, calculateDisplayCutoutForRotation(mDisplayInfo.rotation),
|
||||
calculateRoundedCornersForRotation(mDisplayInfo.rotation));
|
||||
initializeDisplayBaseInfo();
|
||||
|
||||
mAppTransition = new AppTransition(mWmService.mContext, mWmService, this);
|
||||
@@ -1696,8 +1702,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
||||
mTmpConfiguration.unset();
|
||||
final DisplayInfo info = computeScreenConfiguration(mTmpConfiguration, rotation);
|
||||
final WmDisplayCutout cutout = calculateDisplayCutoutForRotation(rotation);
|
||||
final RoundedCorners roundedCorners = calculateRoundedCornersForRotation(rotation);
|
||||
final DisplayFrames displayFrames = new DisplayFrames(mDisplayId, new InsetsState(), info,
|
||||
cutout);
|
||||
cutout, roundedCorners);
|
||||
token.applyFixedRotationTransform(info, displayFrames, mTmpConfiguration);
|
||||
}
|
||||
|
||||
@@ -1958,6 +1965,23 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
||||
rotated ? mInitialDisplayWidth : mInitialDisplayHeight);
|
||||
}
|
||||
|
||||
RoundedCorners calculateRoundedCornersForRotation(int rotation) {
|
||||
return mRoundedCornerCache.getOrCompute(mInitialRoundedCorners, rotation);
|
||||
}
|
||||
|
||||
private RoundedCorners calculateRoundedCornersForRotationUncached(
|
||||
RoundedCorners roundedCorners, int rotation) {
|
||||
if (roundedCorners == null || roundedCorners == RoundedCorners.NO_ROUNDED_CORNERS) {
|
||||
return RoundedCorners.NO_ROUNDED_CORNERS;
|
||||
}
|
||||
|
||||
if (rotation == ROTATION_0) {
|
||||
return roundedCorners;
|
||||
}
|
||||
|
||||
return roundedCorners.rotate(rotation, mInitialDisplayWidth, mInitialDisplayHeight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute display info and configuration according to the given rotation without changing
|
||||
* current display.
|
||||
@@ -2485,7 +2509,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
||||
|
||||
void onDisplayInfoChanged() {
|
||||
final DisplayInfo info = mDisplayInfo;
|
||||
mDisplayFrames.onDisplayInfoUpdated(info, calculateDisplayCutoutForRotation(info.rotation));
|
||||
mDisplayFrames.onDisplayInfoUpdated(info, calculateDisplayCutoutForRotation(info.rotation),
|
||||
calculateRoundedCornersForRotation(info.rotation));
|
||||
mInputMonitor.layoutInputConsumers(info.logicalWidth, info.logicalHeight);
|
||||
mDisplayPolicy.onDisplayInfoChanged(info);
|
||||
}
|
||||
@@ -2518,6 +2543,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
||||
mInitialDisplayHeight = mDisplayInfo.logicalHeight;
|
||||
mInitialDisplayDensity = mDisplayInfo.logicalDensityDpi;
|
||||
mInitialDisplayCutout = mDisplayInfo.displayCutout;
|
||||
mInitialRoundedCorners = mDisplayInfo.roundedCorners;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2535,11 +2561,13 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
||||
final DisplayCutout newCutout = mIgnoreDisplayCutout
|
||||
? DisplayCutout.NO_CUTOUT : mDisplayInfo.displayCutout;
|
||||
final String newUniqueId = mDisplayInfo.uniqueId;
|
||||
final RoundedCorners newRoundedCorners = mDisplayInfo.roundedCorners;
|
||||
|
||||
final boolean displayMetricsChanged = mInitialDisplayWidth != newWidth
|
||||
|| mInitialDisplayHeight != newHeight
|
||||
|| mInitialDisplayDensity != mDisplayInfo.logicalDensityDpi
|
||||
|| !Objects.equals(mInitialDisplayCutout, newCutout);
|
||||
|| !Objects.equals(mInitialDisplayCutout, newCutout)
|
||||
|| !Objects.equals(mInitialRoundedCorners, newRoundedCorners);
|
||||
final boolean physicalDisplayChanged = !newUniqueId.equals(mCurrentUniqueDisplayId);
|
||||
|
||||
if (displayMetricsChanged || physicalDisplayChanged) {
|
||||
@@ -2558,6 +2586,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
||||
mInitialDisplayHeight = newHeight;
|
||||
mInitialDisplayDensity = newDensity;
|
||||
mInitialDisplayCutout = newCutout;
|
||||
mInitialRoundedCorners = newRoundedCorners;
|
||||
mCurrentUniqueDisplayId = newUniqueId;
|
||||
reconfigureDisplayLocked();
|
||||
}
|
||||
|
||||
@@ -21,12 +21,12 @@ import static android.view.InsetsState.ITYPE_LEFT_DISPLAY_CUTOUT;
|
||||
import static android.view.InsetsState.ITYPE_RIGHT_DISPLAY_CUTOUT;
|
||||
import static android.view.InsetsState.ITYPE_TOP_DISPLAY_CUTOUT;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.graphics.Rect;
|
||||
import android.util.proto.ProtoOutputStream;
|
||||
import android.view.DisplayCutout;
|
||||
import android.view.DisplayInfo;
|
||||
import android.view.InsetsState;
|
||||
import android.view.RoundedCorners;
|
||||
|
||||
import com.android.server.wm.utils.WmDisplayCutout;
|
||||
|
||||
@@ -47,9 +47,6 @@ public class DisplayFrames {
|
||||
*/
|
||||
public final Rect mUnrestricted = new Rect();
|
||||
|
||||
/** The display cutout used for layout (after rotation) */
|
||||
@NonNull public WmDisplayCutout mDisplayCutout = WmDisplayCutout.NO_CUTOUT;
|
||||
|
||||
/**
|
||||
* During layout, the frame that is display-cutout safe, i.e. that does not intersect with it.
|
||||
*/
|
||||
@@ -61,26 +58,37 @@ public class DisplayFrames {
|
||||
public int mRotation;
|
||||
|
||||
public DisplayFrames(int displayId, InsetsState insetsState, DisplayInfo info,
|
||||
WmDisplayCutout displayCutout) {
|
||||
WmDisplayCutout displayCutout, RoundedCorners roundedCorners) {
|
||||
mDisplayId = displayId;
|
||||
mInsetsState = insetsState;
|
||||
onDisplayInfoUpdated(info, displayCutout);
|
||||
onDisplayInfoUpdated(info, displayCutout, roundedCorners);
|
||||
}
|
||||
|
||||
public void onDisplayInfoUpdated(DisplayInfo info, WmDisplayCutout displayCutout) {
|
||||
/**
|
||||
* Update {@link DisplayFrames} when {@link DisplayInfo} is updated.
|
||||
*
|
||||
* @param info the updated {@link DisplayInfo}.
|
||||
* @param displayCutout the updated {@link DisplayCutout}.
|
||||
* @param roundedCorners the updated {@link RoundedCorners}.
|
||||
*/
|
||||
public void onDisplayInfoUpdated(DisplayInfo info, WmDisplayCutout displayCutout,
|
||||
RoundedCorners roundedCorners) {
|
||||
mDisplayWidth = info.logicalWidth;
|
||||
mDisplayHeight = info.logicalHeight;
|
||||
mRotation = info.rotation;
|
||||
mDisplayCutout = displayCutout != null ? displayCutout : WmDisplayCutout.NO_CUTOUT;
|
||||
final WmDisplayCutout wmDisplayCutout =
|
||||
displayCutout != null ? displayCutout : WmDisplayCutout.NO_CUTOUT;
|
||||
|
||||
final InsetsState state = mInsetsState;
|
||||
final Rect unrestricted = mUnrestricted;
|
||||
final Rect safe = mDisplayCutoutSafe;
|
||||
final DisplayCutout cutout = mDisplayCutout.getDisplayCutout();
|
||||
final DisplayCutout cutout = wmDisplayCutout.getDisplayCutout();
|
||||
unrestricted.set(0, 0, mDisplayWidth, mDisplayHeight);
|
||||
safe.set(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
|
||||
state.setDisplayFrame(unrestricted);
|
||||
state.setDisplayCutout(cutout);
|
||||
state.setRoundedCorners(roundedCorners != null ? roundedCorners
|
||||
: RoundedCorners.NO_ROUNDED_CORNERS);
|
||||
if (!cutout.isEmpty()) {
|
||||
if (cutout.getSafeInsetLeft() > 0) {
|
||||
safe.left = unrestricted.left + cutout.getSafeInsetLeft();
|
||||
@@ -118,12 +126,5 @@ public class DisplayFrames {
|
||||
public void dump(String prefix, PrintWriter pw) {
|
||||
pw.println(prefix + "DisplayFrames w=" + mDisplayWidth + " h=" + mDisplayHeight
|
||||
+ " r=" + mRotation);
|
||||
final String myPrefix = prefix + " ";
|
||||
dumpFrame(mUnrestricted, "mUnrestricted", myPrefix, pw);
|
||||
pw.println(myPrefix + "mDisplayCutout=" + mDisplayCutout);
|
||||
}
|
||||
|
||||
private void dumpFrame(Rect frame, String name, String prefix, PrintWriter pw) {
|
||||
pw.print(prefix + name + "="); frame.printShortString(pw); pw.println();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@ import android.view.DisplayCutout;
|
||||
import android.view.DisplayInfo;
|
||||
import android.view.Gravity;
|
||||
import android.view.InsetsState;
|
||||
import android.view.RoundedCorners;
|
||||
import android.view.WindowInsets.Side;
|
||||
import android.view.WindowInsets.Type;
|
||||
import android.view.WindowManager;
|
||||
@@ -99,6 +100,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
|
||||
private int mRotation = ROTATION_0;
|
||||
private boolean mHasDisplayCutout;
|
||||
private boolean mIsLongEdgeDisplayCutout;
|
||||
private boolean mHasRoundedCorners;
|
||||
|
||||
private final Rect mDisplayBounds = new Rect();
|
||||
|
||||
@@ -153,6 +155,11 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
|
||||
updateDisplayFrames();
|
||||
}
|
||||
|
||||
public void addRoundedCorners() {
|
||||
mHasRoundedCorners = true;
|
||||
updateDisplayFrames();
|
||||
}
|
||||
|
||||
private void updateDisplayFrames() {
|
||||
mFrames = createDisplayFrames(
|
||||
mDisplayContent.getInsetsStateController().getRawInsetsState());
|
||||
@@ -166,9 +173,11 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
|
||||
private DisplayFrames createDisplayFrames(InsetsState insetsState) {
|
||||
final Pair<DisplayInfo, WmDisplayCutout> info = displayInfoAndCutoutForRotation(mRotation,
|
||||
mHasDisplayCutout, mIsLongEdgeDisplayCutout);
|
||||
final RoundedCorners roundedCorners = mHasRoundedCorners
|
||||
? mDisplayContent.calculateRoundedCornersForRotation(mRotation)
|
||||
: RoundedCorners.NO_ROUNDED_CORNERS;
|
||||
return new DisplayFrames(mDisplayContent.getDisplayId(),
|
||||
insetsState,
|
||||
info.first, info.second);
|
||||
insetsState, info.first, info.second, roundedCorners);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -753,6 +762,8 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
|
||||
assertSimulateLayoutSameDisplayFrames();
|
||||
addDisplayCutout();
|
||||
assertSimulateLayoutSameDisplayFrames();
|
||||
addRoundedCorners();
|
||||
assertSimulateLayoutSameDisplayFrames();
|
||||
}
|
||||
|
||||
private void assertSimulateLayoutSameDisplayFrames() {
|
||||
|
||||
@@ -302,7 +302,7 @@ public class DisplayPolicyTests extends WindowTestsBase {
|
||||
final InsetsState state = mDisplayContent.getInsetsStateController().getRawInsetsState();
|
||||
mImeWindow.mAboveInsetsState = state;
|
||||
mDisplayContent.mDisplayFrames = new DisplayFrames(mDisplayContent.getDisplayId(),
|
||||
state, displayInfo, null /* displayCutout */);
|
||||
state, displayInfo, null /* displayCutout */, null /* roundedCorners*/);
|
||||
|
||||
mDisplayContent.setInputMethodWindowLocked(mImeWindow);
|
||||
mImeWindow.mAttrs.setFitInsetsSides(Side.all() & ~Side.BOTTOM);
|
||||
|
||||
@@ -47,6 +47,7 @@ import android.platform.test.annotations.Presubmit;
|
||||
import android.view.DisplayInfo;
|
||||
import android.view.Gravity;
|
||||
import android.view.InsetsState;
|
||||
import android.view.RoundedCorners;
|
||||
import android.view.Surface;
|
||||
import android.view.WindowManager;
|
||||
|
||||
@@ -146,7 +147,7 @@ public class WallpaperControllerTests extends WindowTestsBase {
|
||||
final DisplayInfo info = dc.computeScreenConfiguration(config, Surface.ROTATION_0);
|
||||
final WmDisplayCutout cutout = dc.calculateDisplayCutoutForRotation(Surface.ROTATION_0);
|
||||
final DisplayFrames displayFrames = new DisplayFrames(dc.getDisplayId(), new InsetsState(),
|
||||
info, cutout);
|
||||
info, cutout, RoundedCorners.NO_ROUNDED_CORNERS);
|
||||
wallpaperWindowToken.applyFixedRotationTransform(info, displayFrames, config);
|
||||
|
||||
// Check that the wallpaper has the same frame in landscape than in portrait
|
||||
|
||||
Reference in New Issue
Block a user