Loading core/java/android/util/DisplayUtils.java +14 −1 Original line number Diff line number Diff line Loading @@ -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 */ Loading Loading @@ -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); } } core/java/android/view/CutoutSpecification.java +70 −29 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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", Loading @@ -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() { Loading Loading @@ -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; Loading Loading @@ -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) { Loading @@ -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; } Loading Loading @@ -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); Loading Loading @@ -476,7 +518,6 @@ public class CutoutSpecification { } parseSpecWithoutDp(spec); mInsets = Insets.of(mSafeInsetLeft, mSafeInsetTop, mSafeInsetRight, mSafeInsetBottom); return new CutoutSpecification(this); } Loading core/java/android/view/DisplayCutout.java +80 −36 Original line number Diff line number Diff line Loading @@ -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}. Loading @@ -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; Loading Loading @@ -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() { Loading @@ -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; } Loading @@ -302,6 +324,10 @@ public final class DisplayCutout { return mScale; } public float getPhysicalPixelDisplaySizeRatio() { return mPhysicalPixelDisplaySizeRatio; } private boolean hasCutout() { return !mCutoutSpec.isEmpty(); } Loading @@ -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; } Loading @@ -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; } Loading @@ -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 + "}" + "}"; } } Loading Loading @@ -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(); Loading Loading @@ -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. * Loading @@ -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; } /** Loading @@ -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. Loading @@ -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; Loading @@ -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; } } Loading @@ -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(); Loading @@ -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, Loading @@ -1113,6 +1145,7 @@ public final class DisplayCutout { sCachedDensity = density; sCachedCutout = result; sCachedWaterfallInsets = waterfallInsets; sCachedPhysicalPixelDisplaySizeRatio = physicalPixelDisplaySizeRatio; } return result; } Loading Loading @@ -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; Loading Loading @@ -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()); } } Loading Loading @@ -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 */); Loading Loading @@ -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); } Loading Loading @@ -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; Loading core/java/android/view/RoundedCorners.java +23 −5 File changed.Preview size limit exceeded, changes collapsed. Show changes core/tests/coretests/src/android/view/DisplayCutoutTest.java +3 −2 Original line number Diff line number Diff line Loading @@ -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 */); } } Loading
core/java/android/util/DisplayUtils.java +14 −1 Original line number Diff line number Diff line Loading @@ -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 */ Loading Loading @@ -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); } }
core/java/android/view/CutoutSpecification.java +70 −29 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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", Loading @@ -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() { Loading Loading @@ -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; Loading Loading @@ -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) { Loading @@ -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; } Loading Loading @@ -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); Loading Loading @@ -476,7 +518,6 @@ public class CutoutSpecification { } parseSpecWithoutDp(spec); mInsets = Insets.of(mSafeInsetLeft, mSafeInsetTop, mSafeInsetRight, mSafeInsetBottom); return new CutoutSpecification(this); } Loading
core/java/android/view/DisplayCutout.java +80 −36 Original line number Diff line number Diff line Loading @@ -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}. Loading @@ -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; Loading Loading @@ -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() { Loading @@ -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; } Loading @@ -302,6 +324,10 @@ public final class DisplayCutout { return mScale; } public float getPhysicalPixelDisplaySizeRatio() { return mPhysicalPixelDisplaySizeRatio; } private boolean hasCutout() { return !mCutoutSpec.isEmpty(); } Loading @@ -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; } Loading @@ -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; } Loading @@ -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 + "}" + "}"; } } Loading Loading @@ -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(); Loading Loading @@ -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. * Loading @@ -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; } /** Loading @@ -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. Loading @@ -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; Loading @@ -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; } } Loading @@ -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(); Loading @@ -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, Loading @@ -1113,6 +1145,7 @@ public final class DisplayCutout { sCachedDensity = density; sCachedCutout = result; sCachedWaterfallInsets = waterfallInsets; sCachedPhysicalPixelDisplaySizeRatio = physicalPixelDisplaySizeRatio; } return result; } Loading Loading @@ -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; Loading Loading @@ -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()); } } Loading Loading @@ -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 */); Loading Loading @@ -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); } Loading Loading @@ -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; Loading
core/java/android/view/RoundedCorners.java +23 −5 File changed.Preview size limit exceeded, changes collapsed. Show changes
core/tests/coretests/src/android/view/DisplayCutoutTest.java +3 −2 Original line number Diff line number Diff line Loading @@ -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 */); } }