Merge "Letterbox: configurable rounded corners." into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
544ee86b0c
@@ -4632,6 +4632,10 @@
|
||||
maximum screen area that can be occupied by the app in the letterbox mode. -->
|
||||
<item name="config_taskLetterboxAspectRatio" format="float" type="dimen">0.0</item>
|
||||
|
||||
<!-- Corners radius for activity presented the letterbox mode. Values < 0 will be ignored and
|
||||
corners of the activity won't be rounded. -->
|
||||
<integer name="config_letterboxActivityCornersRadius">0</integer>
|
||||
|
||||
<!-- If true, hide the display cutout with display area -->
|
||||
<bool name="config_hideDisplayCutoutWithDisplayArea">false</bool>
|
||||
|
||||
|
||||
@@ -4132,6 +4132,7 @@
|
||||
<java-symbol type="dimen" name="controls_thumbnail_image_max_width" />
|
||||
|
||||
<java-symbol type="dimen" name="config_taskLetterboxAspectRatio" />
|
||||
<java-symbol type="integer" name="config_letterboxActivityCornersRadius" />
|
||||
|
||||
<java-symbol type="bool" name="config_hideDisplayCutoutWithDisplayArea" />
|
||||
|
||||
|
||||
@@ -1355,11 +1355,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
final boolean surfaceReady = w.isDrawn() // Regular case
|
||||
|| w.mWinAnimator.mSurfaceDestroyDeferred // The preserved surface is still ready.
|
||||
|| w.isDragResizeChanged(); // Waiting for relayoutWindow to call preserveSurface.
|
||||
final boolean needsLetterbox = surfaceReady && w.isLetterboxedAppWindow() && fillsParent();
|
||||
final boolean needsLetterbox = surfaceReady && isLetterboxed(w);
|
||||
updateRoundedCorners(w);
|
||||
if (needsLetterbox) {
|
||||
if (mLetterbox == null) {
|
||||
mLetterbox = new Letterbox(() -> makeChildSurface(null),
|
||||
mWmService.mTransactionFactory);
|
||||
mWmService.mTransactionFactory,
|
||||
mWmService::isLetterboxActivityCornersRounded);
|
||||
mLetterbox.attachInput(w);
|
||||
}
|
||||
getPosition(mTmpPoint);
|
||||
@@ -1379,6 +1381,27 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
}
|
||||
}
|
||||
|
||||
/** @return {@code true} when main window is letterboxed and activity isn't transparent. */
|
||||
private boolean isLetterboxed(WindowState mainWindow) {
|
||||
return mainWindow.isLetterboxedAppWindow() && fillsParent();
|
||||
}
|
||||
|
||||
private void updateRoundedCorners(WindowState mainWindow) {
|
||||
int cornersRadius =
|
||||
// Don't round corners if letterboxed only for display cutout.
|
||||
isLetterboxed(mainWindow) && !mainWindow.isLetterboxedForDisplayCutout()
|
||||
? Math.max(0, mWmService.getLetterboxActivityCornersRadius()) : 0;
|
||||
setCornersRadius(mainWindow, cornersRadius);
|
||||
}
|
||||
|
||||
private void setCornersRadius(WindowState mainWindow, int cornersRadius) {
|
||||
final SurfaceControl windowSurface = mainWindow.getClientViewRootSurface();
|
||||
if (windowSurface != null && windowSurface.isValid()) {
|
||||
Transaction transaction = getPendingTransaction();
|
||||
transaction.setCornerRadius(windowSurface, cornersRadius);
|
||||
}
|
||||
}
|
||||
|
||||
void updateLetterboxSurface(WindowState winHint) {
|
||||
final WindowState w = findMainWindow();
|
||||
if (w != winHint && winHint != null && w != null) {
|
||||
@@ -1408,10 +1431,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Letterbox#notIntersectsOrFullyContains(Rect)
|
||||
* @return {@code true} if bar shown within a given rectangle is allowed to be transparent
|
||||
* when the current activity is displayed.
|
||||
*/
|
||||
boolean letterboxNotIntersectsOrFullyContains(Rect rect) {
|
||||
return mLetterbox == null || mLetterbox.notIntersectsOrFullyContains(rect);
|
||||
boolean isTransparentBarAllowed(Rect rect) {
|
||||
// TODO(b/175482966): Allow status and navigation bars to be semi-transparent black
|
||||
// in letterbox mode.
|
||||
return mLetterbox == null || mLetterbox.notIntersectsOrFullyContains(rect)
|
||||
|| mWmService.isLetterboxActivityCornersRounded();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -56,6 +56,6 @@ public class BarController {
|
||||
if (win == null) {
|
||||
return true;
|
||||
}
|
||||
return win.letterboxNotIntersectsOrFullyContains(getContentFrame(win));
|
||||
return win.isTransparentBarAllowed(getContentFrame(win));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,12 +44,17 @@ public class Letterbox {
|
||||
|
||||
private final Supplier<SurfaceControl.Builder> mSurfaceControlFactory;
|
||||
private final Supplier<SurfaceControl.Transaction> mTransactionFactory;
|
||||
private final Supplier<Boolean> mAreCornersRounded;
|
||||
private final Rect mOuter = new Rect();
|
||||
private final Rect mInner = new Rect();
|
||||
private final LetterboxSurface mTop = new LetterboxSurface("top");
|
||||
private final LetterboxSurface mLeft = new LetterboxSurface("left");
|
||||
private final LetterboxSurface mBottom = new LetterboxSurface("bottom");
|
||||
private final LetterboxSurface mRight = new LetterboxSurface("right");
|
||||
// Prevents wallpaper from peeking through near rounded corners. It's not included in
|
||||
// mSurfaces array since it isn't needed in methods like notIntersectsOrFullyContains
|
||||
// or attachInput.
|
||||
private final LetterboxSurface mBehind = new LetterboxSurface("behind");
|
||||
private final LetterboxSurface[] mSurfaces = { mLeft, mTop, mRight, mBottom };
|
||||
|
||||
/**
|
||||
@@ -58,9 +63,11 @@ public class Letterbox {
|
||||
* @param surfaceControlFactory a factory for creating the managed {@link SurfaceControl}s
|
||||
*/
|
||||
public Letterbox(Supplier<SurfaceControl.Builder> surfaceControlFactory,
|
||||
Supplier<SurfaceControl.Transaction> transactionFactory) {
|
||||
Supplier<SurfaceControl.Transaction> transactionFactory,
|
||||
Supplier<Boolean> areCornersRounded) {
|
||||
mSurfaceControlFactory = surfaceControlFactory;
|
||||
mTransactionFactory = transactionFactory;
|
||||
mAreCornersRounded = areCornersRounded;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -82,6 +89,7 @@ public class Letterbox {
|
||||
mLeft.layout(outer.left, outer.top, inner.left, outer.bottom, surfaceOrigin);
|
||||
mBottom.layout(outer.left, inner.bottom, outer.right, outer.bottom, surfaceOrigin);
|
||||
mRight.layout(inner.right, outer.top, outer.right, outer.bottom, surfaceOrigin);
|
||||
mBehind.layout(inner.left, inner.top, inner.right, inner.bottom, surfaceOrigin);
|
||||
}
|
||||
|
||||
|
||||
@@ -157,6 +165,7 @@ public class Letterbox {
|
||||
for (LetterboxSurface surface : mSurfaces) {
|
||||
surface.remove();
|
||||
}
|
||||
mBehind.remove();
|
||||
}
|
||||
|
||||
/** Returns whether a call to {@link #applySurfaceChanges} would change the surface. */
|
||||
@@ -166,6 +175,9 @@ public class Letterbox {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (mBehind.needsApplySurfaceChanges()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -173,6 +185,11 @@ public class Letterbox {
|
||||
for (LetterboxSurface surface : mSurfaces) {
|
||||
surface.applySurfaceChanges(t);
|
||||
}
|
||||
if (mAreCornersRounded.get()) {
|
||||
mBehind.applySurfaceChanges(t);
|
||||
} else {
|
||||
mBehind.remove();
|
||||
}
|
||||
}
|
||||
|
||||
/** Enables touches to slide into other neighboring surfaces. */
|
||||
|
||||
@@ -1012,6 +1012,9 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
// ignored.
|
||||
private float mTaskLetterboxAspectRatio;
|
||||
|
||||
// Corners radius for activities presented in the letterbox mode, values < 0 will be ignored.
|
||||
private int mLetterboxActivityCornersRadius;
|
||||
|
||||
final InputManagerService mInputManager;
|
||||
final DisplayManagerInternal mDisplayManagerInternal;
|
||||
final DisplayManager mDisplayManager;
|
||||
@@ -1239,6 +1242,8 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
com.android.internal.R.bool.config_assistantOnTopOfDream);
|
||||
mTaskLetterboxAspectRatio = context.getResources().getFloat(
|
||||
com.android.internal.R.dimen.config_taskLetterboxAspectRatio);
|
||||
mLetterboxActivityCornersRadius = context.getResources().getInteger(
|
||||
com.android.internal.R.integer.config_letterboxActivityCornersRadius);
|
||||
mInputManager = inputManager; // Must be before createDisplayContentLocked.
|
||||
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
|
||||
|
||||
@@ -3936,6 +3941,60 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides corners raidus for activities presented in the letterbox mode. If given value < 0,
|
||||
* both it and a value of {@link
|
||||
* com.android.internal.R.integer.config_letterboxActivityCornersRadius} will be ignored and
|
||||
* and corners of the activity won't be rounded.
|
||||
*/
|
||||
void setLetterboxActivityCornersRadius(int cornersRadius) {
|
||||
final long origId = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mGlobalLock) {
|
||||
mLetterboxActivityCornersRadius = cornersRadius;
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(origId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets corners raidus for activities presented in the letterbox mode to {@link
|
||||
* com.android.internal.R.integer.config_letterboxActivityCornersRadius}.
|
||||
*/
|
||||
void resetLetterboxActivityCornersRadius() {
|
||||
final long origId = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mGlobalLock) {
|
||||
mLetterboxActivityCornersRadius = mContext.getResources().getInteger(
|
||||
com.android.internal.R.integer.config_letterboxActivityCornersRadius);
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(origId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether corners of letterboxed activities are rounded.
|
||||
*/
|
||||
boolean isLetterboxActivityCornersRounded() {
|
||||
return getLetterboxActivityCornersRadius() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets corners raidus for activities presented in the letterbox mode.
|
||||
*/
|
||||
int getLetterboxActivityCornersRadius() {
|
||||
final long origId = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mGlobalLock) {
|
||||
return mLetterboxActivityCornersRadius;
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(origId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIgnoreOrientationRequest(int displayId, boolean ignoreOrientationRequest) {
|
||||
mAtmInternal.enforceCallerIsRecentsOrHasPermission(
|
||||
|
||||
@@ -115,6 +115,10 @@ public class WindowManagerShellCommand extends ShellCommand {
|
||||
return runSetTaskLetterboxAspectRatio(pw);
|
||||
case "get-task-letterbox-aspect-ratio":
|
||||
return runGetTaskLetterboxAspectRatio(pw);
|
||||
case "set-letterbox-activity-corners-radius":
|
||||
return runSetLetterboxActivityCornersRadius(pw);
|
||||
case "get-letterbox-activity-corners-radius":
|
||||
return runGetLetterboxActivityCornersRadius(pw);
|
||||
case "reset":
|
||||
return runReset(pw);
|
||||
default:
|
||||
@@ -545,6 +549,38 @@ public class WindowManagerShellCommand extends ShellCommand {
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int runSetLetterboxActivityCornersRadius(PrintWriter pw) throws RemoteException {
|
||||
final int cornersRadius;
|
||||
try {
|
||||
String arg = getNextArgRequired();
|
||||
if ("reset".equals(arg)) {
|
||||
mInternal.resetLetterboxActivityCornersRadius();
|
||||
return 0;
|
||||
}
|
||||
cornersRadius = Integer.parseInt(arg);
|
||||
} catch (NumberFormatException e) {
|
||||
getErrPrintWriter().println("Error: bad corners radius format " + e);
|
||||
return -1;
|
||||
} catch (IllegalArgumentException e) {
|
||||
getErrPrintWriter().println(
|
||||
"Error: 'reset' or corners radius should be provided as an argument " + e);
|
||||
return -1;
|
||||
}
|
||||
|
||||
mInternal.setLetterboxActivityCornersRadius(cornersRadius);
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int runGetLetterboxActivityCornersRadius(PrintWriter pw) throws RemoteException {
|
||||
final int cornersRadius = mInternal.getLetterboxActivityCornersRadius();
|
||||
if (cornersRadius < 0) {
|
||||
pw.println("Letterbox corners radius is not set");
|
||||
} else {
|
||||
pw.println("Letterbox corners radius is " + cornersRadius);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int runReset(PrintWriter pw) throws RemoteException {
|
||||
int displayId = getDisplayId(getNextArg());
|
||||
|
||||
@@ -572,6 +608,9 @@ public class WindowManagerShellCommand extends ShellCommand {
|
||||
// set-task-letterbox-aspect-ratio
|
||||
mInternal.resetTaskLetterboxAspectRatio();
|
||||
|
||||
// set-letterbox-activity-corners-radius
|
||||
mInternal.resetLetterboxActivityCornersRadius();
|
||||
|
||||
pw.println("Reset all settings for displayId=" + displayId);
|
||||
return 0;
|
||||
}
|
||||
@@ -608,6 +647,11 @@ public class WindowManagerShellCommand extends ShellCommand {
|
||||
+ WindowManagerService.MIN_TASK_LETTERBOX_ASPECT_RATIO);
|
||||
pw.println(" both it and R.dimen.config_taskLetterboxAspectRatio will be ignored");
|
||||
pw.println(" and framework implementation will be used to determine aspect ratio.");
|
||||
pw.println(" set-letterbox-activity-corners-radius [reset|cornersRadius]");
|
||||
pw.println(" get-letterbox-activity-corners-radius");
|
||||
pw.println(" Corners radius for activities in the letterbox mode. If radius < 0,");
|
||||
pw.println(" both it and R.integer.config_letterboxActivityCornersRadius will be");
|
||||
pw.println(" ignored and corners of the activity won't be rounded.");
|
||||
pw.println(" reset [-d DISPLAY_ID]");
|
||||
pw.println(" Reset all override settings.");
|
||||
if (!IS_USER) {
|
||||
|
||||
@@ -3813,11 +3813,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Letterbox#notIntersectsOrFullyContains(Rect)
|
||||
* @return {@code true} if bar shown within a given frame is allowed to be transparent
|
||||
* when the current window is displayed.
|
||||
*/
|
||||
boolean letterboxNotIntersectsOrFullyContains(Rect rect) {
|
||||
return mActivityRecord == null
|
||||
|| mActivityRecord.letterboxNotIntersectsOrFullyContains(rect);
|
||||
boolean isTransparentBarAllowed(Rect frame) {
|
||||
return mActivityRecord == null || mActivityRecord.isTransparentBarAllowed(frame);
|
||||
}
|
||||
|
||||
public boolean isLetterboxedOverlappingWith(Rect rect) {
|
||||
|
||||
@@ -17,6 +17,9 @@
|
||||
package com.android.server.wm;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.clearInvocations;
|
||||
@@ -47,10 +50,12 @@ public class LetterboxTest {
|
||||
SurfaceControlMocker mSurfaces;
|
||||
SurfaceControl.Transaction mTransaction;
|
||||
|
||||
private boolean mAreCornersRounded = false;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
mSurfaces = new SurfaceControlMocker();
|
||||
mLetterbox = new Letterbox(mSurfaces, StubTransaction::new);
|
||||
mLetterbox = new Letterbox(mSurfaces, StubTransaction::new, () -> mAreCornersRounded);
|
||||
mTransaction = spy(StubTransaction.class);
|
||||
}
|
||||
|
||||
@@ -64,6 +69,7 @@ public class LetterboxTest {
|
||||
private static final int BOTTOM_BAR = 0x2;
|
||||
private static final int LEFT_BAR = 0x4;
|
||||
private static final int RIGHT_BAR = 0x8;
|
||||
|
||||
@Test
|
||||
public void testNotIntersectsOrFullyContains_usesGlobalCoordinates() {
|
||||
final Rect outer = new Rect(0, 0, 10, 50);
|
||||
@@ -164,6 +170,41 @@ public class LetterboxTest {
|
||||
verify(mTransaction).setPosition(mSurfaces.top, -1000, -2000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testApplySurfaceChanges_cornersNotRounded_surfaceBehindNotCreated() {
|
||||
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
|
||||
mLetterbox.applySurfaceChanges(mTransaction);
|
||||
|
||||
assertNull(mSurfaces.behind);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testApplySurfaceChanges_cornersRounded_surfaceBehindCreated() {
|
||||
mAreCornersRounded = true;
|
||||
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
|
||||
mLetterbox.applySurfaceChanges(mTransaction);
|
||||
|
||||
assertNotNull(mSurfaces.behind);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsOverlappingWith_cornersRounded_doesNotCheckSurfaceBehind() {
|
||||
mAreCornersRounded = true;
|
||||
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(0, 0));
|
||||
mLetterbox.applySurfaceChanges(mTransaction);
|
||||
|
||||
assertFalse(mLetterbox.isOverlappingWith(new Rect(1, 2, 9, 9)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotIntersectsOrFullyContains_cornersRounded_doesNotCheckSurfaceBehind() {
|
||||
mAreCornersRounded = true;
|
||||
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(0, 0));
|
||||
mLetterbox.applySurfaceChanges(mTransaction);
|
||||
|
||||
assertTrue(mLetterbox.notIntersectsOrFullyContains(new Rect(1, 2, 9, 9)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSurfaceOrigin_changeCausesReapply() {
|
||||
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
|
||||
@@ -184,6 +225,8 @@ public class LetterboxTest {
|
||||
public SurfaceControl right;
|
||||
private SurfaceControl.Builder mBottomBuilder;
|
||||
public SurfaceControl bottom;
|
||||
private SurfaceControl.Builder mBehindBuilder;
|
||||
public SurfaceControl behind;
|
||||
|
||||
@Override
|
||||
public SurfaceControl.Builder get() {
|
||||
@@ -198,6 +241,8 @@ public class LetterboxTest {
|
||||
mRightBuilder = (SurfaceControl.Builder) i.getMock();
|
||||
} else if (((String) i.getArgument(0)).contains("bottom")) {
|
||||
mBottomBuilder = (SurfaceControl.Builder) i.getMock();
|
||||
} else if (((String) i.getArgument(0)).contains("behind")) {
|
||||
mBehindBuilder = (SurfaceControl.Builder) i.getMock();
|
||||
}
|
||||
return i.getMock();
|
||||
});
|
||||
@@ -212,6 +257,8 @@ public class LetterboxTest {
|
||||
right = control;
|
||||
} else if (i.getMock() == mBottomBuilder) {
|
||||
bottom = control;
|
||||
} else if (i.getMock() == mBehindBuilder) {
|
||||
behind = control;
|
||||
}
|
||||
return control;
|
||||
}).when(builder).build();
|
||||
|
||||
Reference in New Issue
Block a user