summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/res/res/values/config.xml4
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java37
-rw-r--r--services/core/java/com/android/server/wm/BarController.java2
-rw-r--r--services/core/java/com/android/server/wm/Letterbox.java19
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java59
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerShellCommand.java44
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java49
9 files changed, 211 insertions, 12 deletions
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 71b59e4d643f..5e0cda69911b 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -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>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 78a6e6232074..dfccdf4bd9a5 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -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" />
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index a5cd3ffe5359..3bb4c74b7dc0 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -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();
}
/**
diff --git a/services/core/java/com/android/server/wm/BarController.java b/services/core/java/com/android/server/wm/BarController.java
index 4a90bbcc6623..eee27c72e583 100644
--- a/services/core/java/com/android/server/wm/BarController.java
+++ b/services/core/java/com/android/server/wm/BarController.java
@@ -56,6 +56,6 @@ public class BarController {
if (win == null) {
return true;
}
- return win.letterboxNotIntersectsOrFullyContains(getContentFrame(win));
+ return win.isTransparentBarAllowed(getContentFrame(win));
}
}
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index 44ce4de529b0..02a43b74aa33 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -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. */
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index b6fabee33d60..8e6a778c351d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -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(
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index a3a9c1ce9219..badd29aba968 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -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) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index f983f27af465..9f3188be7623 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -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) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
index 2f3004bf6832..a045100c4cd8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
@@ -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);
@@ -165,6 +171,41 @@ public class LetterboxTest {
}
@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));
mLetterbox.applySurfaceChanges(mTransaction);
@@ -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();