Support scaled cutout & roundedcorners when resolution is overridden

In current desgin, the confgis for cutout and rounded corner are based
on the original display size. If the display resolution is overridden,
cutous and rounded corners will be drawn in incorrect places with
incorrect size.

We should load the cutout & rounded corners with the orignal display
size and then scale the results with the display ratio which is
calculated by dividing current size with original size.

Bug: 209592558
Test: manual:
      1. Go Settings-> Display-> Screen Resolution
      2. Switch between FHD+ and QHD+
      3. Check if the cutout and rounded corner looks the same as they
	 are in the original display size.
Test: manual:
      1. adb shell wm size 1080x2340
      2. Check if the cutout and rounded corner looks the same as they
         are in the original display size.
Test: atest DisplayCutoutTest LocalDisplayAdapterTest
          ScreenDecorationsTest RoundedCornerResDelegateTest
Change-Id: Iea42b73c0276b82983c1ddbce9455355afdc164d
This commit is contained in:
shawnlin
2022-03-17 16:18:41 +08:00
parent b45f0e8ac6
commit bcea9fd89e
14 changed files with 320 additions and 102 deletions

View File

@@ -21,7 +21,7 @@ import android.content.res.Resources;
import com.android.internal.R;
/**
* Utils for loading resources for multi-display.
* Utils for loading display related resources and calculations.
*
* @hide
*/
@@ -51,4 +51,17 @@ public class DisplayUtils {
}
return index;
}
/**
* Get the display size ratio based on the stable display size.
*/
public static float getPhysicalPixelDisplaySizeRatio(
int stableWidth, int stableHeight, int currentWidth, int currentHeight) {
if (stableWidth == currentWidth && stableHeight == currentHeight) {
return 1f;
}
final float widthRatio = (float) currentWidth / stableWidth;
final float heightRatio = (float) currentHeight / stableHeight;
return Math.min(widthRatio, heightRatio);
}
}

View File

@@ -94,7 +94,7 @@ public class CutoutSpecification {
private final Rect mTopBound;
private final Rect mRightBound;
private final Rect mBottomBound;
private final Insets mInsets;
private Insets mInsets;
private CutoutSpecification(@NonNull Parser parser) {
mPath = parser.mPath;
@@ -104,6 +104,8 @@ public class CutoutSpecification {
mBottomBound = parser.mBottomBound;
mInsets = parser.mInsets;
applyPhysicalPixelDisplaySizeRatio(parser.mPhysicalPixelDisplaySizeRatio);
if (DEBUG) {
Log.d(TAG, String.format(Locale.ENGLISH,
"left cutout = %s, top cutout = %s, right cutout = %s, bottom cutout = %s",
@@ -114,6 +116,38 @@ public class CutoutSpecification {
}
}
private void applyPhysicalPixelDisplaySizeRatio(float physicalPixelDisplaySizeRatio) {
if (physicalPixelDisplaySizeRatio == 1f) {
return;
}
if (mPath != null && !mPath.isEmpty()) {
final Matrix matrix = new Matrix();
matrix.postScale(physicalPixelDisplaySizeRatio, physicalPixelDisplaySizeRatio);
mPath.transform(matrix);
}
scaleBounds(mLeftBound, physicalPixelDisplaySizeRatio);
scaleBounds(mTopBound, physicalPixelDisplaySizeRatio);
scaleBounds(mRightBound, physicalPixelDisplaySizeRatio);
scaleBounds(mBottomBound, physicalPixelDisplaySizeRatio);
mInsets = scaleInsets(mInsets, physicalPixelDisplaySizeRatio);
}
private void scaleBounds(Rect r, float ratio) {
if (r != null && !r.isEmpty()) {
r.scale(ratio);
}
}
private Insets scaleInsets(Insets insets, float ratio) {
return Insets.of(
(int) (insets.left * ratio + 0.5f),
(int) (insets.top * ratio + 0.5f),
(int) (insets.right * ratio + 0.5f),
(int) (insets.bottom * ratio + 0.5f));
}
@VisibleForTesting(visibility = PACKAGE)
@Nullable
public Path getPath() {
@@ -168,9 +202,10 @@ public class CutoutSpecification {
@VisibleForTesting(visibility = PACKAGE)
public static class Parser {
private final boolean mIsShortEdgeOnTop;
private final float mDensity;
private final int mDisplayWidth;
private final int mDisplayHeight;
private final float mStableDensity;
private final int mStableDisplayWidth;
private final int mStableDisplayHeight;
private final float mPhysicalPixelDisplaySizeRatio;
private final Matrix mMatrix;
private Insets mInsets;
private int mSafeInsetLeft;
@@ -202,19 +237,26 @@ public class CutoutSpecification {
private boolean mIsTouchShortEdgeEnd;
private boolean mIsCloserToStartSide;
@VisibleForTesting(visibility = PACKAGE)
public Parser(float stableDensity, int stableDisplayWidth, int stableDisplayHeight) {
this(stableDensity, stableDisplayWidth, stableDisplayHeight, 1f);
}
/**
* The constructor of the CutoutSpecification parser to parse the specification of cutout.
* @param density the display density.
* @param displayWidth the display width.
* @param displayHeight the display height.
* @param stableDensity the display density.
* @param stableDisplayWidth the display width.
* @param stableDisplayHeight the display height.
* @param physicalPixelDisplaySizeRatio the display size ratio based on stable display size.
*/
@VisibleForTesting(visibility = PACKAGE)
public Parser(float density, int displayWidth, int displayHeight) {
mDensity = density;
mDisplayWidth = displayWidth;
mDisplayHeight = displayHeight;
Parser(float stableDensity, int stableDisplayWidth, int stableDisplayHeight,
float physicalPixelDisplaySizeRatio) {
mStableDensity = stableDensity;
mStableDisplayWidth = stableDisplayWidth;
mStableDisplayHeight = stableDisplayHeight;
mPhysicalPixelDisplaySizeRatio = physicalPixelDisplaySizeRatio;
mMatrix = new Matrix();
mIsShortEdgeOnTop = mDisplayWidth < mDisplayHeight;
mIsShortEdgeOnTop = mStableDisplayWidth < mStableDisplayHeight;
}
private void computeBoundsRectAndAddToRegion(Path p, Region inoutRegion, Rect inoutRect) {
@@ -239,38 +281,38 @@ public class CutoutSpecification {
private void translateMatrix() {
final float offsetX;
if (mPositionFromRight) {
offsetX = mDisplayWidth;
offsetX = mStableDisplayWidth;
} else if (mPositionFromLeft) {
offsetX = 0;
} else {
offsetX = mDisplayWidth / 2f;
offsetX = mStableDisplayWidth / 2f;
}
final float offsetY;
if (mPositionFromBottom) {
offsetY = mDisplayHeight;
offsetY = mStableDisplayHeight;
} else if (mPositionFromCenterVertical) {
offsetY = mDisplayHeight / 2f;
offsetY = mStableDisplayHeight / 2f;
} else {
offsetY = 0;
}
mMatrix.reset();
if (mInDp) {
mMatrix.postScale(mDensity, mDensity);
mMatrix.postScale(mStableDensity, mStableDensity);
}
mMatrix.postTranslate(offsetX, offsetY);
}
private int computeSafeInsets(int gravity, Rect rect) {
if (gravity == LEFT && rect.right > 0 && rect.right < mDisplayWidth) {
if (gravity == LEFT && rect.right > 0 && rect.right < mStableDisplayWidth) {
return rect.right;
} else if (gravity == TOP && rect.bottom > 0 && rect.bottom < mDisplayHeight) {
} else if (gravity == TOP && rect.bottom > 0 && rect.bottom < mStableDisplayHeight) {
return rect.bottom;
} else if (gravity == RIGHT && rect.left > 0 && rect.left < mDisplayWidth) {
return mDisplayWidth - rect.left;
} else if (gravity == BOTTOM && rect.top > 0 && rect.top < mDisplayHeight) {
return mDisplayHeight - rect.top;
} else if (gravity == RIGHT && rect.left > 0 && rect.left < mStableDisplayWidth) {
return mStableDisplayWidth - rect.left;
} else if (gravity == BOTTOM && rect.top > 0 && rect.top < mStableDisplayHeight) {
return mStableDisplayHeight - rect.top;
}
return 0;
}
@@ -373,12 +415,12 @@ public class CutoutSpecification {
if (mIsShortEdgeOnTop) {
mIsTouchShortEdgeStart = mTmpRect.top <= 0;
mIsTouchShortEdgeEnd = mTmpRect.bottom >= mDisplayHeight;
mIsCloserToStartSide = mTmpRect.centerY() < mDisplayHeight / 2;
mIsTouchShortEdgeEnd = mTmpRect.bottom >= mStableDisplayHeight;
mIsCloserToStartSide = mTmpRect.centerY() < mStableDisplayHeight / 2;
} else {
mIsTouchShortEdgeStart = mTmpRect.left <= 0;
mIsTouchShortEdgeEnd = mTmpRect.right >= mDisplayWidth;
mIsCloserToStartSide = mTmpRect.centerX() < mDisplayWidth / 2;
mIsTouchShortEdgeEnd = mTmpRect.right >= mStableDisplayWidth;
mIsCloserToStartSide = mTmpRect.centerX() < mStableDisplayWidth / 2;
}
setEdgeCutout(newPath);
@@ -476,7 +518,6 @@ public class CutoutSpecification {
}
parseSpecWithoutDp(spec);
mInsets = Insets.of(mSafeInsetLeft, mSafeInsetTop, mSafeInsetRight, mSafeInsetBottom);
return new CutoutSpecification(this);
}

View File

@@ -77,8 +77,10 @@ public final class DisplayCutout {
private static final Rect ZERO_RECT = new Rect();
private static final CutoutPathParserInfo EMPTY_PARSER_INFO = new CutoutPathParserInfo(
0 /* displayWidth */, 0 /* displayHeight */, 0f /* density */, "" /* cutoutSpec */,
0 /* rotation */, 0f /* scale */);
0 /* displayWidth */, 0 /* stableDisplayHeight */,
0 /* stableDisplayHeight */, 0 /* displayHeight */, 0f /* density */,
"" /* cutoutSpec */, 0 /* ROTATION_0 */, 0f /* scale */,
0f /* physicalPixelDisplaySizeRatio*/);
/**
* An instance where {@link #isEmpty()} returns {@code true}.
@@ -105,6 +107,8 @@ public final class DisplayCutout {
private static Pair<Path, DisplayCutout> sCachedCutout = NULL_PAIR;
@GuardedBy("CACHE_LOCK")
private static Insets sCachedWaterfallInsets;
@GuardedBy("CACHE_LOCK")
private static float sCachedPhysicalPixelDisplaySizeRatio;
@GuardedBy("CACHE_LOCK")
private static CutoutPathParserInfo sCachedCutoutPathParserInfo;
@@ -254,28 +258,38 @@ public final class DisplayCutout {
public static class CutoutPathParserInfo {
private final int mDisplayWidth;
private final int mDisplayHeight;
private final int mStableDisplayWidth;
private final int mStableDisplayHeight;
private final float mDensity;
private final String mCutoutSpec;
private final @Rotation int mRotation;
private final float mScale;
private final float mPhysicalPixelDisplaySizeRatio;
public CutoutPathParserInfo(int displayWidth, int displayHeight, float density,
@Nullable String cutoutSpec, @Rotation int rotation, float scale) {
public CutoutPathParserInfo(int displayWidth, int displayHeight, int stableDisplayWidth,
int stableDisplayHeight, float density, @Nullable String cutoutSpec,
@Rotation int rotation, float scale, float physicalPixelDisplaySizeRatio) {
mDisplayWidth = displayWidth;
mDisplayHeight = displayHeight;
mStableDisplayWidth = stableDisplayWidth;
mStableDisplayHeight = stableDisplayHeight;
mDensity = density;
mCutoutSpec = cutoutSpec == null ? "" : cutoutSpec;
mRotation = rotation;
mScale = scale;
mPhysicalPixelDisplaySizeRatio = physicalPixelDisplaySizeRatio;
}
public CutoutPathParserInfo(@NonNull CutoutPathParserInfo cutoutPathParserInfo) {
mDisplayWidth = cutoutPathParserInfo.mDisplayWidth;
mDisplayHeight = cutoutPathParserInfo.mDisplayHeight;
mStableDisplayWidth = cutoutPathParserInfo.mStableDisplayWidth;
mStableDisplayHeight = cutoutPathParserInfo.mStableDisplayHeight;
mDensity = cutoutPathParserInfo.mDensity;
mCutoutSpec = cutoutPathParserInfo.mCutoutSpec;
mRotation = cutoutPathParserInfo.mRotation;
mScale = cutoutPathParserInfo.mScale;
mPhysicalPixelDisplaySizeRatio = cutoutPathParserInfo.mPhysicalPixelDisplaySizeRatio;
}
public int getDisplayWidth() {
@@ -286,6 +300,14 @@ public final class DisplayCutout {
return mDisplayHeight;
}
public int getStableDisplayWidth() {
return mStableDisplayWidth;
}
public int getStableDisplayHeight() {
return mStableDisplayHeight;
}
public float getDensity() {
return mDensity;
}
@@ -302,6 +324,10 @@ public final class DisplayCutout {
return mScale;
}
public float getPhysicalPixelDisplaySizeRatio() {
return mPhysicalPixelDisplaySizeRatio;
}
private boolean hasCutout() {
return !mCutoutSpec.isEmpty();
}
@@ -315,6 +341,9 @@ public final class DisplayCutout {
result = result * 48271 + mCutoutSpec.hashCode();
result = result * 48271 + Integer.hashCode(mRotation);
result = result * 48271 + Float.hashCode(mScale);
result = result * 48271 + Float.hashCode(mPhysicalPixelDisplaySizeRatio);
result = result * 48271 + Integer.hashCode(mStableDisplayWidth);
result = result * 48271 + Integer.hashCode(mStableDisplayHeight);
return result;
}
@@ -326,8 +355,11 @@ public final class DisplayCutout {
if (o instanceof CutoutPathParserInfo) {
CutoutPathParserInfo c = (CutoutPathParserInfo) o;
return mDisplayWidth == c.mDisplayWidth && mDisplayHeight == c.mDisplayHeight
&& mStableDisplayWidth == c.mStableDisplayWidth
&& mStableDisplayHeight == c.mStableDisplayHeight
&& mDensity == c.mDensity && mCutoutSpec.equals(c.mCutoutSpec)
&& mRotation == c.mRotation && mScale == c.mScale;
&& mRotation == c.mRotation && mScale == c.mScale
&& mPhysicalPixelDisplaySizeRatio == c.mPhysicalPixelDisplaySizeRatio;
}
return false;
}
@@ -336,10 +368,13 @@ public final class DisplayCutout {
public String toString() {
return "CutoutPathParserInfo{displayWidth=" + mDisplayWidth
+ " displayHeight=" + mDisplayHeight
+ " stableDisplayHeight=" + mStableDisplayWidth
+ " stableDisplayHeight=" + mStableDisplayHeight
+ " density={" + mDensity + "}"
+ " cutoutSpec={" + mCutoutSpec + "}"
+ " rotation={" + mRotation + "}"
+ " scale={" + mScale + "}"
+ " physicalPixelDisplaySizeRatio={" + mPhysicalPixelDisplaySizeRatio + "}"
+ "}";
}
}
@@ -715,8 +750,9 @@ public final class DisplayCutout {
}
}
final CutoutSpecification cutoutSpec = new CutoutSpecification.Parser(
mCutoutPathParserInfo.getDensity(), mCutoutPathParserInfo.getDisplayWidth(),
mCutoutPathParserInfo.getDisplayHeight())
mCutoutPathParserInfo.getDensity(), mCutoutPathParserInfo.getStableDisplayWidth(),
mCutoutPathParserInfo.getStableDisplayHeight(),
mCutoutPathParserInfo.getPhysicalPixelDisplaySizeRatio())
.parse(mCutoutPathParserInfo.getCutoutSpec());
final Path cutoutPath = cutoutSpec.getPath();
@@ -1014,29 +1050,18 @@ public final class DisplayCutout {
* Creates the display cutout according to
* @android:string/config_mainBuiltInDisplayCutoutRectApproximation, which is the closest
* rectangle-base approximation of the cutout.
*
* @hide
*/
public static DisplayCutout fromResourcesRectApproximation(Resources res,
String displayUniqueId, int displayWidth, int displayHeight) {
String displayUniqueId, int stableDisplayWidth, int stableDisplayHeight,
int displayWidth, int displayHeight) {
return pathAndDisplayCutoutFromSpec(getDisplayCutoutPath(res, displayUniqueId),
getDisplayCutoutApproximationRect(res, displayUniqueId),
displayWidth, displayHeight, DENSITY_DEVICE_STABLE / (float) DENSITY_DEFAULT,
getDisplayCutoutApproximationRect(res, displayUniqueId), stableDisplayWidth,
stableDisplayHeight, displayWidth, displayHeight,
DENSITY_DEVICE_STABLE / (float) DENSITY_DEFAULT,
getWaterfallInsets(res, displayUniqueId)).second;
}
/**
* Creates an instance according to @android:string/config_mainBuiltInDisplayCutout.
*
* @hide
*/
public static Path pathFromResources(Resources res, String displayUniqueId, int displayWidth,
int displayHeight) {
return pathAndDisplayCutoutFromSpec(getDisplayCutoutPath(res, displayUniqueId), null,
displayWidth, displayHeight, DENSITY_DEVICE_STABLE / (float) DENSITY_DEFAULT,
getWaterfallInsets(res, displayUniqueId)).first;
}
/**
* Creates an instance according to the supplied {@link android.util.PathParser.PathData} spec.
*
@@ -1046,8 +1071,8 @@ public final class DisplayCutout {
public static DisplayCutout fromSpec(String pathSpec, int displayWidth,
int displayHeight, float density, Insets waterfallInsets) {
return pathAndDisplayCutoutFromSpec(
pathSpec, null, displayWidth, displayHeight, density, waterfallInsets)
.second;
pathSpec, null, displayWidth, displayHeight, displayWidth, displayHeight, density,
waterfallInsets).second;
}
/**
@@ -1055,6 +1080,8 @@ public final class DisplayCutout {
*
* @param pathSpec the spec string read from config_mainBuiltInDisplayCutout.
* @param rectSpec the spec string read from config_mainBuiltInDisplayCutoutRectApproximation.
* @param stableDisplayWidth the stable display width.
* @param stableDisplayHeight the stable display height.
* @param displayWidth the display width.
* @param displayHeight the display height.
* @param density the display density.
@@ -1062,8 +1089,8 @@ public final class DisplayCutout {
* @return a Pair contains the cutout path and the corresponding DisplayCutout instance.
*/
private static Pair<Path, DisplayCutout> pathAndDisplayCutoutFromSpec(
String pathSpec, String rectSpec, int displayWidth, int displayHeight, float density,
Insets waterfallInsets) {
String pathSpec, String rectSpec, int stableDisplayWidth, int stableDisplayHeight,
int displayWidth, int displayHeight, float density, Insets waterfallInsets) {
// Always use the rect approximation spec to create the cutout if it's not null because
// transforming and sending a Region constructed from a path is very costly.
String spec = rectSpec != null ? rectSpec : pathSpec;
@@ -1071,11 +1098,15 @@ public final class DisplayCutout {
return NULL_PAIR;
}
final float physicalPixelDisplaySizeRatio = DisplayUtils.getPhysicalPixelDisplaySizeRatio(
stableDisplayWidth, stableDisplayHeight, displayWidth, displayHeight);
synchronized (CACHE_LOCK) {
if (spec.equals(sCachedSpec) && sCachedDisplayWidth == displayWidth
&& sCachedDisplayHeight == displayHeight
&& sCachedDensity == density
&& waterfallInsets.equals(sCachedWaterfallInsets)) {
&& waterfallInsets.equals(sCachedWaterfallInsets)
&& sCachedPhysicalPixelDisplaySizeRatio == physicalPixelDisplaySizeRatio) {
return sCachedCutout;
}
}
@@ -1083,7 +1114,7 @@ public final class DisplayCutout {
spec = spec.trim();
CutoutSpecification cutoutSpec = new CutoutSpecification.Parser(density,
displayWidth, displayHeight).parse(spec);
stableDisplayWidth, stableDisplayHeight, physicalPixelDisplaySizeRatio).parse(spec);
Rect safeInset = cutoutSpec.getSafeInset();
final Rect boundLeft = cutoutSpec.getLeftBound();
final Rect boundTop = cutoutSpec.getTopBound();
@@ -1099,8 +1130,9 @@ public final class DisplayCutout {
Math.max(waterfallInsets.bottom, safeInset.bottom));
}
final CutoutPathParserInfo cutoutPathParserInfo = new CutoutPathParserInfo(displayWidth,
displayHeight, density, pathSpec.trim(), ROTATION_0, 1f /* scale */);
final CutoutPathParserInfo cutoutPathParserInfo = new CutoutPathParserInfo(
displayWidth, displayHeight, stableDisplayWidth, stableDisplayHeight, density,
pathSpec.trim(), ROTATION_0, 1f /* scale */, physicalPixelDisplaySizeRatio);
final DisplayCutout cutout = new DisplayCutout(
safeInset, waterfallInsets, boundLeft, boundTop, boundRight, boundBottom,
@@ -1113,6 +1145,7 @@ public final class DisplayCutout {
sCachedDensity = density;
sCachedCutout = result;
sCachedWaterfallInsets = waterfallInsets;
sCachedPhysicalPixelDisplaySizeRatio = physicalPixelDisplaySizeRatio;
}
return result;
}
@@ -1149,8 +1182,9 @@ public final class DisplayCutout {
Collections.rotate(Arrays.asList(newBounds), -rotation);
final CutoutPathParserInfo info = getCutoutPathParserInfo();
final CutoutPathParserInfo newInfo = new CutoutPathParserInfo(
info.getDisplayWidth(), info.getDisplayHeight(), info.getDensity(),
info.getCutoutSpec(), toRotation, info.getScale());
info.getDisplayWidth(), info.getDisplayHeight(), info.getStableDisplayWidth(),
info.getStableDisplayHeight(), info.getDensity(), info.getCutoutSpec(), toRotation,
info.getScale(), info.getPhysicalPixelDisplaySizeRatio());
final boolean swapAspect = (rotation % 2) != 0;
final int endWidth = swapAspect ? startHeight : startWidth;
final int endHeight = swapAspect ? startWidth : startHeight;
@@ -1250,10 +1284,13 @@ public final class DisplayCutout {
out.writeTypedObject(cutout.mWaterfallInsets, flags);
out.writeInt(cutout.mCutoutPathParserInfo.getDisplayWidth());
out.writeInt(cutout.mCutoutPathParserInfo.getDisplayHeight());
out.writeInt(cutout.mCutoutPathParserInfo.getStableDisplayWidth());
out.writeInt(cutout.mCutoutPathParserInfo.getStableDisplayHeight());
out.writeFloat(cutout.mCutoutPathParserInfo.getDensity());
out.writeString(cutout.mCutoutPathParserInfo.getCutoutSpec());
out.writeInt(cutout.mCutoutPathParserInfo.getRotation());
out.writeFloat(cutout.mCutoutPathParserInfo.getScale());
out.writeFloat(cutout.mCutoutPathParserInfo.getPhysicalPixelDisplaySizeRatio());
}
}
@@ -1299,12 +1336,16 @@ public final class DisplayCutout {
Insets waterfallInsets = in.readTypedObject(Insets.CREATOR);
int displayWidth = in.readInt();
int displayHeight = in.readInt();
int stableDisplayWidth = in.readInt();
int stableDisplayHeight = in.readInt();
float density = in.readFloat();
String cutoutSpec = in.readString();
int rotation = in.readInt();
float scale = in.readFloat();
float physicalPixelDisplaySizeRatio = in.readFloat();
final CutoutPathParserInfo info = new CutoutPathParserInfo(
displayWidth, displayHeight, density, cutoutSpec, rotation, scale);
displayWidth, displayHeight, stableDisplayWidth, stableDisplayHeight, density,
cutoutSpec, rotation, scale, physicalPixelDisplaySizeRatio);
return new DisplayCutout(
safeInsets, waterfallInsets, bounds, info, false /* copyArguments */);
@@ -1332,10 +1373,13 @@ public final class DisplayCutout {
final CutoutPathParserInfo info = new CutoutPathParserInfo(
mInner.mCutoutPathParserInfo.getDisplayWidth(),
mInner.mCutoutPathParserInfo.getDisplayHeight(),
mInner.mCutoutPathParserInfo.getStableDisplayWidth(),
mInner.mCutoutPathParserInfo.getStableDisplayHeight(),
mInner.mCutoutPathParserInfo.getDensity(),
mInner.mCutoutPathParserInfo.getCutoutSpec(),
mInner.mCutoutPathParserInfo.getRotation(),
scale);
scale,
mInner.mCutoutPathParserInfo.getPhysicalPixelDisplaySizeRatio());
mInner = new DisplayCutout(safeInsets, Insets.of(waterfallInsets), bounds, info);
}
@@ -1387,7 +1431,7 @@ public final class DisplayCutout {
if (mCutoutPath != null) {
// Create a fake CutoutPathParserInfo and set it to sCachedCutoutPathParserInfo so
// that when getCutoutPath() is called, it will return the cached Path.
info = new CutoutPathParserInfo(0, 0, 0, "test", 0, 1f);
info = new CutoutPathParserInfo(0, 0, 0, 0, 0, "test", ROTATION_0, 1f, 1f);
synchronized (CACHE_LOCK) {
DisplayCutout.sCachedCutoutPathParserInfo = info;
DisplayCutout.sCachedCutoutPath = mCutoutPath;

View File

@@ -67,6 +67,8 @@ public class RoundedCorners implements Parcelable {
private static Pair<Integer, Integer> sCachedRadii;
@GuardedBy("CACHE_LOCK")
private static RoundedCorners sCachedRoundedCorners;
@GuardedBy("CACHE_LOCK")
private static float sCachedPhysicalPixelDisplaySizeRatio;
@VisibleForTesting
public final RoundedCorner[] mRoundedCorners;
@@ -96,8 +98,10 @@ public class RoundedCorners implements Parcelable {
* @android:dimen/rounded_corner_radius_top and @android:dimen/rounded_corner_radius_bottom
*/
public static RoundedCorners fromResources(
Resources res, String displayUniqueId, int displayWidth, int displayHeight) {
return fromRadii(loadRoundedCornerRadii(res, displayUniqueId), displayWidth, displayHeight);
Resources res, String displayUniqueId, int stableDisplayWidth, int stableDisplayHeight,
int displayWidth, int displayHeight) {
return fromRadii(loadRoundedCornerRadii(res, displayUniqueId), stableDisplayWidth,
stableDisplayHeight, displayWidth, displayHeight);
}
/**
@@ -106,20 +110,33 @@ public class RoundedCorners implements Parcelable {
@VisibleForTesting
public static RoundedCorners fromRadii(Pair<Integer, Integer> radii, int displayWidth,
int displayHeight) {
return fromRadii(radii, displayWidth, displayHeight, displayWidth, displayHeight);
}
private static RoundedCorners fromRadii(Pair<Integer, Integer> radii, int stableDisplayWidth,
int stableDisplayHeight, int displayWidth, int displayHeight) {
if (radii == null) {
return null;
}
final float physicalPixelDisplaySizeRatio = DisplayUtils.getPhysicalPixelDisplaySizeRatio(
stableDisplayWidth, stableDisplayHeight, displayWidth, displayHeight);
synchronized (CACHE_LOCK) {
if (radii.equals(sCachedRadii) && sCachedDisplayWidth == displayWidth
&& sCachedDisplayHeight == displayHeight) {
&& sCachedDisplayHeight == displayHeight
&& sCachedPhysicalPixelDisplaySizeRatio == physicalPixelDisplaySizeRatio) {
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;
int topRadius = radii.first > 0 ? radii.first : 0;
int bottomRadius = radii.second > 0 ? radii.second : 0;
if (physicalPixelDisplaySizeRatio != 1f) {
topRadius = (int) (topRadius * physicalPixelDisplaySizeRatio + 0.5);
bottomRadius = (int) (bottomRadius * physicalPixelDisplaySizeRatio + 0.5);
}
for (int i = 0; i < ROUNDED_CORNER_POSITION_LENGTH; i++) {
roundedCorners[i] = createRoundedCorner(
i,
@@ -134,6 +151,7 @@ public class RoundedCorners implements Parcelable {
sCachedDisplayHeight = displayHeight;
sCachedRadii = radii;
sCachedRoundedCorners = result;
sCachedPhysicalPixelDisplaySizeRatio = physicalPixelDisplaySizeRatio;
}
return result;
}

View File

@@ -609,7 +609,8 @@ public class DisplayCutoutTest {
private static DisplayCutout.CutoutPathParserInfo createParserInfo(
@Surface.Rotation int rotation) {
return new DisplayCutout.CutoutPathParserInfo(
0 /* displayWidth */, 0 /* displayHeight */, 0f /* density */, "" /* cutoutSpec */,
rotation, 0f /* scale */);
0 /* displayWidth */, 0 /* displayHeight */, 0 /* displayWidth */,
0 /* displayHeight */, 0f /* density */, "" /* cutoutSpec */,
rotation, 0f /* scale */, 0f /* displaySizeRatio */);
}
}

View File

@@ -423,8 +423,9 @@ public class DisplayLayout {
}
final DisplayCutout.CutoutPathParserInfo info = cutout.getCutoutPathParserInfo();
final DisplayCutout.CutoutPathParserInfo newInfo = new DisplayCutout.CutoutPathParserInfo(
info.getDisplayWidth(), info.getDisplayHeight(), info.getDensity(),
info.getCutoutSpec(), rotation, info.getScale());
info.getDisplayWidth(), info.getDisplayHeight(), info.getStableDisplayWidth(),
info.getStableDisplayHeight(), info.getDensity(), info.getCutoutSpec(), rotation,
info.getScale(), info.getPhysicalPixelDisplaySizeRatio());
return computeSafeInsets(
DisplayCutout.constructDisplayCutout(newBounds, waterfallInsets, newInfo),
rotated ? displayHeight : displayWidth,

View File

@@ -19,8 +19,6 @@ import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
import static android.view.DisplayCutout.BOUNDS_POSITION_LENGTH;
import static android.view.DisplayCutout.BOUNDS_POSITION_RIGHT;
import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
@@ -38,10 +36,10 @@ import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.hardware.display.DisplayManager;
@@ -51,10 +49,12 @@ import android.os.Handler;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings.Secure;
import android.util.DisplayUtils;
import android.util.Log;
import android.util.Size;
import android.view.DisplayCutout;
import android.view.DisplayCutout.BoundsPosition;
import android.view.DisplayInfo;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
@@ -320,13 +320,15 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
}
private void startOnScreenDecorationsThread() {
mWindowManager = mContext.getSystemService(WindowManager.class);
mDisplayManager = mContext.getSystemService(DisplayManager.class);
mRotation = mContext.getDisplay().getRotation();
mDisplayUniqueId = mContext.getDisplay().getUniqueId();
mRoundedCornerResDelegate = new RoundedCornerResDelegate(mContext.getResources(),
mDisplayUniqueId);
mRoundedCornerResDelegate.setPhysicalPixelDisplaySizeRatio(
getPhysicalPixelDisplaySizeRatio());
mRoundedCornerFactory = new RoundedCornerDecorProviderFactory(mRoundedCornerResDelegate);
mWindowManager = mContext.getSystemService(WindowManager.class);
mDisplayManager = mContext.getSystemService(DisplayManager.class);
mHwcScreenDecorationSupport = mContext.getDisplay().getDisplayDecorationSupport();
updateHwLayerRoundedCornerDrawable();
setupDecorations();
@@ -402,6 +404,16 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
updateOverlayProviderViews();
}
final float newRatio = getPhysicalPixelDisplaySizeRatio();
if (mRoundedCornerResDelegate.getPhysicalPixelDisplaySizeRatio() != newRatio) {
mRoundedCornerResDelegate.setPhysicalPixelDisplaySizeRatio(newRatio);
if (mScreenDecorHwcLayer != null) {
updateHwLayerRoundedCornerExistAndSize();
}
updateOverlayProviderViews();
}
if (mCutoutViews != null) {
final int size = mCutoutViews.length;
for (int i = 0; i < size; i++) {
@@ -878,6 +890,16 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
}
}
@VisibleForTesting
float getPhysicalPixelDisplaySizeRatio() {
final Point stableDisplaySize = mDisplayManager.getStableDisplaySize();
final DisplayInfo displayInfo = new DisplayInfo();
mContext.getDisplay().getDisplayInfo(displayInfo);
return DisplayUtils.getPhysicalPixelDisplaySizeRatio(
stableDisplaySize.x, stableDisplaySize.y, displayInfo.logicalWidth,
displayInfo.logicalHeight);
}
@Override
protected void onConfigurationChanged(Configuration newConfig) {
if (DEBUG_DISABLE_SCREEN_DECORATIONS) {
@@ -1182,25 +1204,12 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
}
private void updateBoundingPath() {
int lw = displayInfo.logicalWidth;
int lh = displayInfo.logicalHeight;
boolean flipped = displayInfo.rotation == ROTATION_90
|| displayInfo.rotation == ROTATION_270;
int dw = flipped ? lh : lw;
int dh = flipped ? lw : lh;
Path path = DisplayCutout.pathFromResources(
getResources(), getDisplay().getUniqueId(), dw, dh);
final Path path = displayInfo.displayCutout.getCutoutPath();
if (path != null) {
cutoutPath.set(path);
} else {
cutoutPath.reset();
}
Matrix m = new Matrix();
transformPhysicalToLogicalCoordinates(displayInfo.rotation, dw, dh, m);
cutoutPath.transform(m);
}
private void updateGravity() {

View File

@@ -68,6 +68,15 @@ class RoundedCornerResDelegate(
reloadMeasures()
}
var physicalPixelDisplaySizeRatio: Float = 1f
set(value) {
if (field == value) {
return
}
field = value
reloadMeasures()
}
init {
reloadRes()
reloadMeasures()
@@ -134,6 +143,19 @@ class RoundedCornerResDelegate(
bottomRoundedSize = Size(length, length)
}
}
if (physicalPixelDisplaySizeRatio != 1f) {
if (topRoundedSize.width != 0) {
topRoundedSize = Size(
(physicalPixelDisplaySizeRatio * topRoundedSize.width + 0.5f).toInt(),
(physicalPixelDisplaySizeRatio * topRoundedSize.height + 0.5f).toInt())
}
if (bottomRoundedSize.width != 0) {
bottomRoundedSize = Size(
(physicalPixelDisplaySizeRatio * bottomRoundedSize.width + 0.5f).toInt(),
(physicalPixelDisplaySizeRatio * bottomRoundedSize.height + 0.5f).toInt())
}
}
}
private fun getDrawable(
@@ -160,5 +182,6 @@ class RoundedCornerResDelegate(
pw.println(" topRoundedSize(w,h)=(${topRoundedSize.width},${topRoundedSize.height})")
pw.println(" bottomRoundedSize(w,h)=(${bottomRoundedSize.width}," +
"${bottomRoundedSize.height})")
pw.println(" physicalPixelDisplaySizeRatio=$physicalPixelDisplaySizeRatio")
}
}

View File

@@ -213,6 +213,7 @@ public class ScreenDecorationsTest extends SysuiTestCase {
mExecutor.runAllReady();
}
});
doReturn(1f).when(mScreenDecorations).getPhysicalPixelDisplaySizeRatio();
reset(mTunerService);
try {

View File

@@ -133,6 +133,22 @@ class RoundedCornerResDelegateTest : SysuiTestCase() {
assertEquals(Size(4, 4), roundedCornerResDelegate.bottomRoundedSize)
}
@Test
fun testPhysicalPixelDisplaySizeChanged() {
setupResources(
roundedTopDrawable = getTestsDrawable(R.drawable.rounded4px),
roundedBottomDrawable = getTestsDrawable(R.drawable.rounded4px))
roundedCornerResDelegate = RoundedCornerResDelegate(mContext.resources, null)
assertEquals(Size(4, 4), roundedCornerResDelegate.topRoundedSize)
assertEquals(Size(4, 4), roundedCornerResDelegate.bottomRoundedSize)
roundedCornerResDelegate.physicalPixelDisplaySizeRatio = 0.5f
assertEquals(Size(2, 2), roundedCornerResDelegate.topRoundedSize)
assertEquals(Size(2, 2), roundedCornerResDelegate.bottomRoundedSize)
}
private fun getTestsDrawable(@DrawableRes drawableId: Int): Drawable? {
return mContext.createPackageContext("com.android.systemui.tests", 0)
.getDrawable(drawableId)

View File

@@ -22,6 +22,8 @@ import static android.view.Display.Mode.INVALID_MODE_ID;
import android.app.ActivityThread;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Point;
import android.hardware.display.DisplayManager;
import android.hardware.sidekick.SidekickInternal;
import android.os.Build;
import android.os.Handler;
@@ -677,11 +679,16 @@ final class LocalDisplayAdapter extends DisplayAdapter {
if (DisplayCutout.getMaskBuiltInDisplayCutout(res, mInfo.uniqueId)) {
mInfo.flags |= DisplayDeviceInfo.FLAG_MASK_DISPLAY_CUTOUT;
}
final Point stableDisplaySize = getOverlayContext()
.getSystemService(DisplayManager.class).getStableDisplaySize();
mInfo.displayCutout = DisplayCutout.fromResourcesRectApproximation(res,
mInfo.uniqueId, mInfo.width, mInfo.height);
mInfo.uniqueId, stableDisplaySize.x, stableDisplaySize.y, mInfo.width,
mInfo.height);
mInfo.roundedCorners = RoundedCorners.fromResources(
res, mInfo.uniqueId, mInfo.width, mInfo.height);
res, mInfo.uniqueId, stableDisplaySize.x, stableDisplaySize.y, mInfo.width,
mInfo.height);
mInfo.installOrientation = mStaticDisplayInfo.installOrientation;
if (mStaticDisplayInfo.isInternal) {

View File

@@ -358,6 +358,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
float mInitialPhysicalXDpi = 0.0f;
float mInitialPhysicalYDpi = 0.0f;
private Point mStableDisplaySize;
DisplayCutout mInitialDisplayCutout;
private final RotationCache<DisplayCutout, WmDisplayCutout> mDisplayCutoutCache
= new RotationCache<>(this::calculateDisplayCutoutForRotationUncached);
@@ -379,6 +381,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
*/
int mBaseDisplayWidth = 0;
int mBaseDisplayHeight = 0;
DisplayCutout mBaseDisplayCutout;
RoundedCorners mBaseRoundedCorners;
boolean mIsSizeForced = false;
/**
@@ -2078,7 +2082,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
WmDisplayCutout calculateDisplayCutoutForRotation(int rotation) {
return mDisplayCutoutCache.getOrCompute(mInitialDisplayCutout, rotation);
return mDisplayCutoutCache.getOrCompute(
mIsSizeForced ? mBaseDisplayCutout : mInitialDisplayCutout, rotation);
}
static WmDisplayCutout calculateDisplayCutoutForRotationAndDisplaySizeUncached(
@@ -2101,11 +2106,13 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
private WmDisplayCutout calculateDisplayCutoutForRotationUncached(
DisplayCutout cutout, int rotation) {
return calculateDisplayCutoutForRotationAndDisplaySizeUncached(cutout, rotation,
mInitialDisplayWidth, mInitialDisplayHeight);
mIsSizeForced ? mBaseDisplayWidth : mInitialDisplayWidth,
mIsSizeForced ? mBaseDisplayHeight : mInitialDisplayHeight);
}
RoundedCorners calculateRoundedCornersForRotation(int rotation) {
return mRoundedCornerCache.getOrCompute(mInitialRoundedCorners, rotation);
return mRoundedCornerCache.getOrCompute(
mIsSizeForced ? mBaseRoundedCorners : mInitialRoundedCorners, rotation);
}
private RoundedCorners calculateRoundedCornersForRotationUncached(
@@ -2118,7 +2125,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
return roundedCorners;
}
return roundedCorners.rotate(rotation, mInitialDisplayWidth, mInitialDisplayHeight);
return roundedCorners.rotate(
rotation,
mIsSizeForced ? mBaseDisplayWidth : mInitialDisplayWidth,
mIsSizeForced ? mBaseDisplayHeight : mInitialDisplayHeight);
}
PrivacyIndicatorBounds calculatePrivacyIndicatorBoundsForRotation(int rotation) {
@@ -2719,6 +2729,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mInitialRoundedCorners = mDisplayInfo.roundedCorners;
mCurrentPrivacyIndicatorBounds = new PrivacyIndicatorBounds(new Rect[4],
mDisplayInfo.rotation);
mStableDisplaySize = mWmService.mDisplayManager.getStableDisplaySize();
}
/**
@@ -2814,6 +2825,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mBaseDisplayDensity = baseDensity;
mBaseDisplayPhysicalXDpi = baseXDpi;
mBaseDisplayPhysicalYDpi = baseYDpi;
if (mIsSizeForced) {
mBaseDisplayCutout = loadDisplayCutout(baseWidth, baseHeight);
mBaseRoundedCorners = loadRoundedCorners(baseWidth, baseHeight);
}
if (mMaxUiWidth > 0 && mBaseDisplayWidth > mMaxUiWidth) {
final float ratio = mMaxUiWidth / (float) mBaseDisplayWidth;
@@ -2904,6 +2919,24 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mWmService.mDisplayWindowSettings.setForcedSize(this, width, height);
}
DisplayCutout loadDisplayCutout(int displayWidth, int displayHeight) {
if (mDisplayPolicy == null) {
return null;
}
return DisplayCutout.fromResourcesRectApproximation(
mDisplayPolicy.getSystemUiContext().getResources(), mDisplayInfo.uniqueId,
mStableDisplaySize.x, mStableDisplaySize.y, displayWidth, displayHeight);
}
RoundedCorners loadRoundedCorners(int displayWidth, int displayHeight) {
if (mDisplayPolicy == null) {
return null;
}
return RoundedCorners.fromResources(
mDisplayPolicy.getSystemUiContext().getResources(), mDisplayInfo.uniqueId,
mStableDisplaySize.x, mStableDisplaySize.y, displayWidth, displayHeight);
}
@Override
void getStableRect(Rect out) {
final InsetsState state = mDisplayContent.getInsetsStateController().getRawInsetsState();

View File

@@ -35,6 +35,8 @@ import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Point;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerInternal.RefreshRateRange;
import android.os.Binder;
import android.os.Handler;
@@ -93,6 +95,11 @@ public class LocalDisplayAdapterTest {
@Mock
private LogicalLight mMockedBacklight;
@Mock
private DisplayManager mMockDisplayManager;
@Mock
private Point mMockDisplayStableSize;
private Handler mHandler;
private TestListener mListener = new TestListener();
@@ -123,6 +130,8 @@ public class LocalDisplayAdapterTest {
mListener, mInjector);
spyOn(mAdapter);
doReturn(mMockedContext).when(mAdapter).getOverlayContext();
doReturn(mMockDisplayManager).when(mMockedContext).getSystemService(DisplayManager.class);
doReturn(mMockDisplayStableSize).when(mMockDisplayManager).getStableDisplaySize();
TypedArray mockNitsRange = createFloatTypedArray(DISPLAY_RANGE_NITS);
when(mMockedResources.obtainTypedArray(R.array.config_screenBrightnessNits))

View File

@@ -854,7 +854,8 @@ public class DisplayContentTests extends WindowTestsBase {
final Rect[] bounds = new Rect[]{zeroRect, new Rect(left, top, right, bottom), zeroRect,
zeroRect};
final DisplayCutout.CutoutPathParserInfo info = new DisplayCutout.CutoutPathParserInfo(
displayWidth, displayHeight, density, "", Surface.ROTATION_0, 1f);
displayWidth, displayHeight, displayWidth, displayHeight, density, "",
Surface.ROTATION_0, 1f, 1f);
final DisplayCutout cutout = new WmDisplayCutout(
DisplayCutout.constructDisplayCutout(bounds, Insets.NONE, info), null)
.computeSafeInsets(displayWidth, displayHeight).getDisplayCutout();
@@ -874,7 +875,8 @@ public class DisplayContentTests extends WindowTestsBase {
final Rect[] bounds90 = new Rect[]{new Rect(top, left, bottom, right), zeroRect, zeroRect,
zeroRect};
final DisplayCutout.CutoutPathParserInfo info90 = new DisplayCutout.CutoutPathParserInfo(
displayWidth, displayHeight, density, "", Surface.ROTATION_90, 1f);
displayWidth, displayHeight, displayWidth, displayHeight, density, "",
Surface.ROTATION_90, 1f, 1f);
assertEquals(new WmDisplayCutout(
DisplayCutout.constructDisplayCutout(bounds90, Insets.NONE, info90), null)
.computeSafeInsets(displayHeight, displayWidth).getDisplayCutout(),