summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Massimo Carli <mcarli@google.com> 2022-12-14 14:29:56 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2022-12-14 14:29:56 +0000
commit86c6d900c60c0dba5c4c549ef5f62ec6643d37fe (patch)
tree31011a8a2348facf9915891ded9e5f54c8b97d28
parentbb848b653a1a401cd9dd80d4bbf42bf86d26766a (diff)
parentde531310145c462c7356dcaac378bda6aacaefbb (diff)
Merge "Fix Letterboxing for Transparent Activities" into tm-qpr-dev
-rw-r--r--core/res/res/values/config.xml4
-rw-r--r--core/res/res/values/symbols.xml3
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java62
-rw-r--r--services/core/java/com/android/server/wm/LetterboxConfiguration.java47
-rw-r--r--services/core/java/com/android/server/wm/LetterboxUiController.java193
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java37
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerShellCommand.java45
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java108
8 files changed, 474 insertions, 25 deletions
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 18c29d196ada..bbbb79c79527 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -5300,6 +5300,10 @@
<!-- Whether using split screen aspect ratio as a default aspect ratio for unresizable apps. -->
<bool name="config_letterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled">false</bool>
+ <!-- Whether the specific behaviour for translucent activities letterboxing is enabled.
+ TODO(b/255532890) Enable when ignoreOrientationRequest is set -->
+ <bool name="config_letterboxIsEnabledForTranslucentActivities">false</bool>
+
<!-- Whether a camera compat controller is enabled to allow the user to apply or revert
treatment for stretched issues in camera viewfinder. -->
<bool name="config_isCameraCompatControlForStretchedIssuesEnabled">false</bool>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index b9259218c2d3..b0f6ae6085e3 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4401,6 +4401,9 @@
<!-- Set to true to make assistant show in front of the dream/screensaver. -->
<java-symbol type="bool" name="config_assistantOnTopOfDream"/>
+ <!-- Set to true to enable letterboxing on translucent activities. -->
+ <java-symbol type="bool" name="config_letterboxIsEnabledForTranslucentActivities" />
+
<java-symbol type="string" name="config_overrideComponentUiPackage" />
<java-symbol type="string" name="notification_channel_network_status" />
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 2c274827ba8e..e639866a6bab 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1239,8 +1239,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
pw.println(prefix + "supportsEnterPipOnTaskSwitch: "
+ supportsEnterPipOnTaskSwitch);
}
- if (info.getMaxAspectRatio() != 0) {
- pw.println(prefix + "maxAspectRatio=" + info.getMaxAspectRatio());
+ if (getMaxAspectRatio() != 0) {
+ pw.println(prefix + "maxAspectRatio=" + getMaxAspectRatio());
}
final float minAspectRatio = getMinAspectRatio();
if (minAspectRatio != 0) {
@@ -1590,6 +1590,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
newParent.setResumedActivity(this, "onParentChanged");
mImeInsetsFrozenUntilStartInput = false;
}
+ mLetterboxUiController.onActivityParentChanged(newParent);
}
if (rootTask != null && rootTask.topRunningActivity() == this) {
@@ -7682,6 +7683,15 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
@Configuration.Orientation
@Override
int getRequestedConfigurationOrientation(boolean forDisplay) {
+ if (mLetterboxUiController.hasInheritedOrientation()) {
+ final RootDisplayArea root = getRootDisplayArea();
+ if (forDisplay && root != null && root.isOrientationDifferentFromDisplay()) {
+ return ActivityInfo.reverseOrientation(
+ mLetterboxUiController.getInheritedOrientation());
+ } else {
+ return mLetterboxUiController.getInheritedOrientation();
+ }
+ }
if (mOrientation == SCREEN_ORIENTATION_BEHIND && task != null) {
// We use Task here because we want to be consistent with what happens in
// multi-window mode where other tasks orientations are ignored.
@@ -7809,6 +7819,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
@Nullable
CompatDisplayInsets getCompatDisplayInsets() {
+ if (mLetterboxUiController.hasInheritedLetterboxBehavior()) {
+ return mLetterboxUiController.getInheritedCompatDisplayInsets();
+ }
return mCompatDisplayInsets;
}
@@ -7891,6 +7904,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer.
private void updateCompatDisplayInsets() {
+ if (mLetterboxUiController.hasInheritedLetterboxBehavior()) {
+ mCompatDisplayInsets = mLetterboxUiController.getInheritedCompatDisplayInsets();
+ return;
+ }
if (mCompatDisplayInsets != null || !shouldCreateCompatDisplayInsets()) {
// The override configuration is set only once in size compatibility mode.
return;
@@ -7952,6 +7969,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
@Override
float getCompatScale() {
+ if (mLetterboxUiController.hasInheritedLetterboxBehavior()) {
+ return mLetterboxUiController.getInheritedSizeCompatScale();
+ }
return hasSizeCompatBounds() ? mSizeCompatScale : super.getCompatScale();
}
@@ -8061,6 +8081,16 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
/**
+ * @return The orientation to use to understand if reachability is enabled.
+ */
+ @ActivityInfo.ScreenOrientation
+ int getOrientationForReachability() {
+ return mLetterboxUiController.hasInheritedLetterboxBehavior()
+ ? mLetterboxUiController.getInheritedOrientation()
+ : getRequestedConfigurationOrientation();
+ }
+
+ /**
* Returns whether activity bounds are letterboxed.
*
* <p>Note that letterbox UI may not be shown even when this returns {@code true}. See {@link
@@ -8100,6 +8130,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (!ignoreVisibility && !mVisibleRequested) {
return APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE;
}
+ // TODO(b/256564921): Investigate if we need new metrics for translucent activities
+ if (mLetterboxUiController.hasInheritedLetterboxBehavior()) {
+ return mLetterboxUiController.getInheritedAppCompatState();
+ }
if (mInSizeCompatModeForBounds) {
return APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_SIZE_COMPAT_MODE;
}
@@ -8570,6 +8604,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
private boolean isInSizeCompatModeForBounds(final Rect appBounds, final Rect containerBounds) {
+ if (mLetterboxUiController.hasInheritedLetterboxBehavior()) {
+ // To avoid wrong app behaviour, we decided to disable SCM when a translucent activity
+ // is letterboxed.
+ return false;
+ }
final int appWidth = appBounds.width();
final int appHeight = appBounds.height();
final int containerAppWidth = containerBounds.width();
@@ -8590,10 +8629,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// The rest of the condition is that only one side is smaller than the container, but it
// still needs to exclude the cases where the size is limited by the fixed aspect ratio.
- if (info.getMaxAspectRatio() > 0) {
+ final float maxAspectRatio = getMaxAspectRatio();
+ if (maxAspectRatio > 0) {
final float aspectRatio = (0.5f + Math.max(appWidth, appHeight))
/ Math.min(appWidth, appHeight);
- if (aspectRatio >= info.getMaxAspectRatio()) {
+ if (aspectRatio >= maxAspectRatio) {
// The current size has reached the max aspect ratio.
return false;
}
@@ -8815,7 +8855,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer.
private boolean applyAspectRatio(Rect outBounds, Rect containingAppBounds,
Rect containingBounds, float desiredAspectRatio) {
- final float maxAspectRatio = info.getMaxAspectRatio();
+ final float maxAspectRatio = getMaxAspectRatio();
final Task rootTask = getRootTask();
final float minAspectRatio = getMinAspectRatio();
final TaskFragment organizedTf = getOrganizedTaskFragment();
@@ -8922,6 +8962,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
* Returns the min aspect ratio of this activity.
*/
float getMinAspectRatio() {
+ if (mLetterboxUiController.hasInheritedLetterboxBehavior()) {
+ return mLetterboxUiController.getInheritedMinAspectRatio();
+ }
if (info.applicationInfo == null) {
return info.getMinAspectRatio();
}
@@ -8966,11 +9009,18 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
&& parent.getWindowConfiguration().getWindowingMode() == WINDOWING_MODE_FULLSCREEN;
}
+ float getMaxAspectRatio() {
+ if (mLetterboxUiController.hasInheritedLetterboxBehavior()) {
+ return mLetterboxUiController.getInheritedMaxAspectRatio();
+ }
+ return info.getMaxAspectRatio();
+ }
+
/**
* Returns true if the activity has maximum or minimum aspect ratio.
*/
private boolean hasFixedAspectRatio() {
- return info.getMaxAspectRatio() != 0 || getMinAspectRatio() != 0;
+ return getMaxAspectRatio() != 0 || getMinAspectRatio() != 0;
}
/**
diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
index c19353cb2676..127a7bf1c9a5 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
@@ -21,6 +21,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Color;
+import android.provider.DeviceConfig;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
@@ -103,6 +104,10 @@ final class LetterboxConfiguration {
final Context mContext;
+ // Responsible for the persistence of letterbox[Horizontal|Vertical]PositionMultiplier
+ @NonNull
+ private final LetterboxConfigurationPersister mLetterboxConfigurationPersister;
+
// Aspect ratio of letterbox for fixed orientation, values <=
// MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO will be ignored.
private float mFixedOrientationLetterboxAspectRatio;
@@ -165,9 +170,12 @@ final class LetterboxConfiguration {
// Whether using split screen aspect ratio as a default aspect ratio for unresizable apps.
private boolean mIsSplitScreenAspectRatioForUnresizableAppsEnabled;
- // Responsible for the persistence of letterbox[Horizontal|Vertical]PositionMultiplier
- @NonNull
- private final LetterboxConfigurationPersister mLetterboxConfigurationPersister;
+ // Whether letterboxing strategy is enabled for translucent activities. If {@value false}
+ // all the feature is disabled
+ private boolean mTranslucentLetterboxingEnabled;
+
+ // Allows to enable letterboxing strategy for translucent activities ignoring flags.
+ private boolean mTranslucentLetterboxingOverrideEnabled;
LetterboxConfiguration(Context systemUiContext) {
this(systemUiContext, new LetterboxConfigurationPersister(systemUiContext,
@@ -206,6 +214,8 @@ final class LetterboxConfiguration {
R.dimen.config_letterboxDefaultMinAspectRatioForUnresizableApps));
mIsSplitScreenAspectRatioForUnresizableAppsEnabled = mContext.getResources().getBoolean(
R.bool.config_letterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled);
+ mTranslucentLetterboxingEnabled = mContext.getResources().getBoolean(
+ R.bool.config_letterboxIsEnabledForTranslucentActivities);
mLetterboxConfigurationPersister = letterboxConfigurationPersister;
mLetterboxConfigurationPersister.start();
}
@@ -817,6 +827,32 @@ final class LetterboxConfiguration {
R.bool.config_letterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled);
}
+ boolean isTranslucentLetterboxingEnabled() {
+ return mTranslucentLetterboxingOverrideEnabled || (mTranslucentLetterboxingEnabled
+ && isTranslucentLetterboxingAllowed());
+ }
+
+ void setTranslucentLetterboxingEnabled(boolean translucentLetterboxingEnabled) {
+ mTranslucentLetterboxingEnabled = translucentLetterboxingEnabled;
+ }
+
+ void setTranslucentLetterboxingOverrideEnabled(
+ boolean translucentLetterboxingOverrideEnabled) {
+ mTranslucentLetterboxingOverrideEnabled = translucentLetterboxingOverrideEnabled;
+ setTranslucentLetterboxingEnabled(translucentLetterboxingOverrideEnabled);
+ }
+
+ /**
+ * Resets whether we use the constraints override strategy for letterboxing when dealing
+ * with translucent activities {@link R.bool.config_letterboxIsEnabledForTranslucentActivities}.
+ */
+ void resetTranslucentLetterboxingEnabled() {
+ final boolean newValue = mContext.getResources().getBoolean(
+ R.bool.config_letterboxIsEnabledForTranslucentActivities);
+ setTranslucentLetterboxingEnabled(newValue);
+ setTranslucentLetterboxingOverrideEnabled(false);
+ }
+
/** Calculates a new letterboxPositionForHorizontalReachability value and updates the store */
private void updatePositionForHorizontalReachability(
Function<Integer, Integer> newHorizonalPositionFun) {
@@ -839,4 +875,9 @@ final class LetterboxConfiguration {
nextVerticalPosition);
}
+ // TODO(b/262378106): Cache runtime flag and implement DeviceConfig.OnPropertiesChangedListener
+ static boolean isTranslucentLetterboxingAllowed() {
+ return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+ "enable_translucent_activity_letterbox", false);
+ }
}
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index bcea6f4db1dc..a53a5fc00b0c 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
@@ -27,6 +28,7 @@ import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANG
import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__RIGHT;
import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__TOP;
import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__UNKNOWN_POSITION;
+import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__UNKNOWN;
import static com.android.internal.util.FrameworkStatsLog.LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__BOTTOM_TO_CENTER;
import static com.android.internal.util.FrameworkStatsLog.LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__CENTER_TO_BOTTOM;
import static com.android.internal.util.FrameworkStatsLog.LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__CENTER_TO_LEFT;
@@ -82,13 +84,44 @@ final class LetterboxUiController {
private static final String TAG = TAG_WITH_CLASS_NAME ? "LetterboxUiController" : TAG_ATM;
+ private static final float UNDEFINED_ASPECT_RATIO = 0f;
+
private final Point mTmpPoint = new Point();
private final LetterboxConfiguration mLetterboxConfiguration;
+
private final ActivityRecord mActivityRecord;
+ /*
+ * WindowContainerListener responsible to make translucent activities inherit
+ * constraints from the first opaque activity beneath them. It's null for not
+ * translucent activities.
+ */
+ @Nullable
+ private WindowContainerListener mLetterboxConfigListener;
+
private boolean mShowWallpaperForLetterboxBackground;
+ // In case of transparent activities we might need to access the aspectRatio of the
+ // first opaque activity beneath.
+ private float mInheritedMinAspectRatio = UNDEFINED_ASPECT_RATIO;
+ private float mInheritedMaxAspectRatio = UNDEFINED_ASPECT_RATIO;
+
+ @Configuration.Orientation
+ private int mInheritedOrientation = Configuration.ORIENTATION_UNDEFINED;
+
+ // The app compat state for the opaque activity if any
+ private int mInheritedAppCompatState = APP_COMPAT_STATE_CHANGED__STATE__UNKNOWN;
+
+ // If true it means that the opaque activity beneath a translucent one is in SizeCompatMode.
+ private boolean mIsInheritedInSizeCompatMode;
+
+ // This is the SizeCompatScale of the opaque activity beneath a translucent one
+ private float mInheritedSizeCompatScale;
+
+ // The CompatDisplayInsets of the opaque activity beneath the translucent one.
+ private ActivityRecord.CompatDisplayInsets mInheritedCompatDisplayInsets;
+
@Nullable
private Letterbox mLetterbox;
@@ -220,7 +253,9 @@ final class LetterboxUiController {
: mActivityRecord.inMultiWindowMode()
? mActivityRecord.getTask().getBounds()
: mActivityRecord.getRootTask().getParent().getBounds();
- mLetterbox.layout(spaceToFill, w.getFrame(), mTmpPoint);
+ final Rect innerFrame = hasInheritedLetterboxBehavior()
+ ? mActivityRecord.getWindowConfiguration().getBounds() : w.getFrame();
+ mLetterbox.layout(spaceToFill, innerFrame, mTmpPoint);
} else if (mLetterbox != null) {
mLetterbox.hide();
}
@@ -305,7 +340,9 @@ final class LetterboxUiController {
}
private void handleHorizontalDoubleTap(int x) {
- if (!isHorizontalReachabilityEnabled() || mActivityRecord.isInTransition()) {
+ // TODO(b/260857308): Investigate if enabling reachability for translucent activity
+ if (hasInheritedLetterboxBehavior() || !isHorizontalReachabilityEnabled()
+ || mActivityRecord.isInTransition()) {
return;
}
@@ -341,7 +378,9 @@ final class LetterboxUiController {
}
private void handleVerticalDoubleTap(int y) {
- if (!isVerticalReachabilityEnabled() || mActivityRecord.isInTransition()) {
+ // TODO(b/260857308): Investigate if enabling reachability for translucent activity
+ if (hasInheritedLetterboxBehavior() || !isVerticalReachabilityEnabled()
+ || mActivityRecord.isInTransition()) {
return;
}
@@ -390,7 +429,7 @@ final class LetterboxUiController {
&& parentConfiguration.windowConfiguration.getWindowingMode()
== WINDOWING_MODE_FULLSCREEN
&& (parentConfiguration.orientation == ORIENTATION_LANDSCAPE
- && mActivityRecord.getRequestedConfigurationOrientation() == ORIENTATION_PORTRAIT);
+ && mActivityRecord.getOrientationForReachability() == ORIENTATION_PORTRAIT);
}
private boolean isHorizontalReachabilityEnabled() {
@@ -412,7 +451,7 @@ final class LetterboxUiController {
&& parentConfiguration.windowConfiguration.getWindowingMode()
== WINDOWING_MODE_FULLSCREEN
&& (parentConfiguration.orientation == ORIENTATION_PORTRAIT
- && mActivityRecord.getRequestedConfigurationOrientation() == ORIENTATION_LANDSCAPE);
+ && mActivityRecord.getOrientationForReachability() == ORIENTATION_LANDSCAPE);
}
private boolean isVerticalReachabilityEnabled() {
@@ -576,9 +615,7 @@ final class LetterboxUiController {
// Rounded corners should be displayed above the taskbar.
bounds.bottom =
Math.min(bounds.bottom, getTaskbarInsetsSource(mainWindow).getFrame().top);
- if (mActivityRecord.inSizeCompatMode() && mActivityRecord.getCompatScale() < 1.0f) {
- bounds.scale(1.0f / mActivityRecord.getCompatScale());
- }
+ scaleIfNeeded(bounds);
}
private int getInsetsStateCornerRadius(
@@ -788,4 +825,144 @@ final class LetterboxUiController {
w.mAttrs.insetsFlags.appearance
);
}
+
+ /**
+ * Handles translucent activities letterboxing inheriting constraints from the
+ * first opaque activity beneath.
+ * @param parent The parent container.
+ */
+ void onActivityParentChanged(WindowContainer<?> parent) {
+ if (!mLetterboxConfiguration.isTranslucentLetterboxingEnabled()) {
+ return;
+ }
+ if (mLetterboxConfigListener != null) {
+ mLetterboxConfigListener.onRemoved();
+ clearInheritedConfig();
+ }
+ // In case mActivityRecord.getCompatDisplayInsets() is not null we don't apply the
+ // opaque activity constraints because we're expecting the activity is already letterboxed.
+ if (mActivityRecord.getTask() == null || mActivityRecord.getCompatDisplayInsets() != null
+ || mActivityRecord.fillsParent()) {
+ return;
+ }
+ final ActivityRecord firstOpaqueActivityBeneath = mActivityRecord.getTask().getActivity(
+ ActivityRecord::fillsParent, mActivityRecord, false /* includeBoundary */,
+ true /* traverseTopToBottom */);
+ if (firstOpaqueActivityBeneath == null
+ || mActivityRecord.launchedFromUid != firstOpaqueActivityBeneath.getUid()) {
+ // We skip letterboxing if the translucent activity doesn't have any opaque
+ // activities beneath of if it's launched from a different user (e.g. notification)
+ return;
+ }
+ inheritConfiguration(firstOpaqueActivityBeneath);
+ mLetterboxConfigListener = WindowContainer.overrideConfigurationPropagation(
+ mActivityRecord, firstOpaqueActivityBeneath,
+ (opaqueConfig, transparentConfig) -> {
+ final Configuration mutatedConfiguration = new Configuration();
+ final Rect parentBounds = parent.getWindowConfiguration().getBounds();
+ final Rect bounds = mutatedConfiguration.windowConfiguration.getBounds();
+ final Rect letterboxBounds = opaqueConfig.windowConfiguration.getBounds();
+ // We cannot use letterboxBounds directly here because the position relies on
+ // letterboxing. Using letterboxBounds directly, would produce a double offset.
+ bounds.set(parentBounds.left, parentBounds.top,
+ parentBounds.left + letterboxBounds.width(),
+ parentBounds.top + letterboxBounds.height());
+ // We need to initialize appBounds to avoid NPE. The actual value will
+ // be set ahead when resolving the Configuration for the activity.
+ mutatedConfiguration.windowConfiguration.setAppBounds(new Rect());
+ return mutatedConfiguration;
+ });
+ }
+
+ /**
+ * @return {@code true} if the current activity is translucent with an opaque activity
+ * beneath. In this case it will inherit bounds, orientation and aspect ratios from
+ * the first opaque activity beneath.
+ */
+ boolean hasInheritedLetterboxBehavior() {
+ return mLetterboxConfigListener != null && !mActivityRecord.matchParentBounds();
+ }
+
+ /**
+ * @return {@code true} if the current activity is translucent with an opaque activity
+ * beneath and needs to inherit its orientation.
+ */
+ boolean hasInheritedOrientation() {
+ // To force a different orientation, the transparent one needs to have an explicit one
+ // otherwise the existing one is fine and the actual orientation will depend on the
+ // bounds.
+ // To avoid wrong behaviour, we're not forcing orientation for activities with not
+ // fixed orientation (e.g. permission dialogs).
+ return hasInheritedLetterboxBehavior()
+ && mActivityRecord.mOrientation != SCREEN_ORIENTATION_UNSPECIFIED;
+ }
+
+ float getInheritedMinAspectRatio() {
+ return mInheritedMinAspectRatio;
+ }
+
+ float getInheritedMaxAspectRatio() {
+ return mInheritedMaxAspectRatio;
+ }
+
+ int getInheritedAppCompatState() {
+ return mInheritedAppCompatState;
+ }
+
+ float getInheritedSizeCompatScale() {
+ return mInheritedSizeCompatScale;
+ }
+
+ @Configuration.Orientation
+ int getInheritedOrientation() {
+ return mInheritedOrientation;
+ }
+
+ public ActivityRecord.CompatDisplayInsets getInheritedCompatDisplayInsets() {
+ return mInheritedCompatDisplayInsets;
+ }
+
+ private void inheritConfiguration(ActivityRecord firstOpaque) {
+ // To avoid wrong behaviour, we're not forcing a specific aspet ratio to activities
+ // which are not already providing one (e.g. permission dialogs) and presumably also
+ // not resizable.
+ if (mActivityRecord.getMinAspectRatio() != UNDEFINED_ASPECT_RATIO) {
+ mInheritedMinAspectRatio = firstOpaque.getMinAspectRatio();
+ }
+ if (mActivityRecord.getMaxAspectRatio() != UNDEFINED_ASPECT_RATIO) {
+ mInheritedMaxAspectRatio = firstOpaque.getMaxAspectRatio();
+ }
+ mInheritedOrientation = firstOpaque.getRequestedConfigurationOrientation();
+ mInheritedAppCompatState = firstOpaque.getAppCompatState();
+ mIsInheritedInSizeCompatMode = firstOpaque.inSizeCompatMode();
+ mInheritedSizeCompatScale = firstOpaque.getCompatScale();
+ mInheritedCompatDisplayInsets = firstOpaque.getCompatDisplayInsets();
+ }
+
+ private void clearInheritedConfig() {
+ mLetterboxConfigListener = null;
+ mInheritedMinAspectRatio = UNDEFINED_ASPECT_RATIO;
+ mInheritedMaxAspectRatio = UNDEFINED_ASPECT_RATIO;
+ mInheritedOrientation = Configuration.ORIENTATION_UNDEFINED;
+ mInheritedAppCompatState = APP_COMPAT_STATE_CHANGED__STATE__UNKNOWN;
+ mIsInheritedInSizeCompatMode = false;
+ mInheritedSizeCompatScale = 1f;
+ mInheritedCompatDisplayInsets = null;
+ }
+
+ private void scaleIfNeeded(Rect bounds) {
+ if (boundsNeedToScale()) {
+ bounds.scale(1.0f / mActivityRecord.getCompatScale());
+ }
+ }
+
+ private boolean boundsNeedToScale() {
+ if (hasInheritedLetterboxBehavior()) {
+ return mIsInheritedInSizeCompatMode
+ && mInheritedSizeCompatScale < 1.0f;
+ } else {
+ return mActivityRecord.inSizeCompatMode()
+ && mActivityRecord.getCompatScale() < 1.0f;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 80e7ddb432d3..5c893de6b920 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -3938,27 +3938,54 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
unregisterConfigurationChangeListener(listener);
}
+ static void overrideConfigurationPropagation(WindowContainer<?> receiver,
+ WindowContainer<?> supplier) {
+ overrideConfigurationPropagation(receiver, supplier, null /* configurationMerger */);
+ }
+
/**
* Forces the receiver container to always use the configuration of the supplier container as
* its requested override configuration. It allows to propagate configuration without changing
* the relationship between child and parent.
+ *
+ * @param receiver The {@link WindowContainer<?>} which will receive the {@link
+ * Configuration} result of the merging operation.
+ * @param supplier The {@link WindowContainer<?>} which provides the initial {@link
+ * Configuration}.
+ * @param configurationMerger A {@link ConfigurationMerger} which combines the {@link
+ * Configuration} of the receiver and the supplier.
*/
- static void overrideConfigurationPropagation(WindowContainer<?> receiver,
- WindowContainer<?> supplier) {
+ static WindowContainerListener overrideConfigurationPropagation(WindowContainer<?> receiver,
+ WindowContainer<?> supplier, @Nullable ConfigurationMerger configurationMerger) {
final ConfigurationContainerListener listener = new ConfigurationContainerListener() {
@Override
public void onMergedOverrideConfigurationChanged(Configuration mergedOverrideConfig) {
- receiver.onRequestedOverrideConfigurationChanged(supplier.getConfiguration());
+ final Configuration mergedConfiguration =
+ configurationMerger != null
+ ? configurationMerger.merge(mergedOverrideConfig,
+ receiver.getConfiguration())
+ : supplier.getConfiguration();
+ receiver.onRequestedOverrideConfigurationChanged(mergedConfiguration);
}
};
supplier.registerConfigurationChangeListener(listener);
- receiver.registerWindowContainerListener(new WindowContainerListener() {
+ final WindowContainerListener wcListener = new WindowContainerListener() {
@Override
public void onRemoved() {
receiver.unregisterWindowContainerListener(this);
supplier.unregisterConfigurationChangeListener(listener);
}
- });
+ };
+ receiver.registerWindowContainerListener(wcListener);
+ return wcListener;
+ }
+
+ /**
+ * Abstraction for functions merging two {@link Configuration} objects into one.
+ */
+ @FunctionalInterface
+ interface ConfigurationMerger {
+ Configuration merge(Configuration first, Configuration second);
}
/**
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index b8cf0ad2e774..4e692e2d212a 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -963,6 +963,29 @@ public class WindowManagerShellCommand extends ShellCommand {
return 0;
}
+ private int runSetTranslucentLetterboxingEnabled(PrintWriter pw) {
+ String arg = getNextArg();
+ final boolean enabled;
+ switch (arg) {
+ case "true":
+ case "1":
+ enabled = true;
+ break;
+ case "false":
+ case "0":
+ enabled = false;
+ break;
+ default:
+ getErrPrintWriter().println("Error: expected true, 1, false, 0, but got " + arg);
+ return -1;
+ }
+
+ synchronized (mInternal.mGlobalLock) {
+ mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(enabled);
+ }
+ return 0;
+ }
+
private int runSetLetterboxStyle(PrintWriter pw) throws RemoteException {
if (peekNextArg() == null) {
getErrPrintWriter().println("Error: No arguments provided.");
@@ -1018,6 +1041,9 @@ public class WindowManagerShellCommand extends ShellCommand {
case "--isSplitScreenAspectRatioForUnresizableAppsEnabled":
runSetLetterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled(pw);
break;
+ case "--isTranslucentLetterboxingEnabled":
+ runSetTranslucentLetterboxingEnabled(pw);
+ break;
default:
getErrPrintWriter().println(
"Error: Unrecognized letterbox style option: " + arg);
@@ -1081,6 +1107,9 @@ public class WindowManagerShellCommand extends ShellCommand {
mLetterboxConfiguration
.getIsSplitScreenAspectRatioForUnresizableAppsEnabled();
break;
+ case "isTranslucentLetterboxingEnabled":
+ mLetterboxConfiguration.resetTranslucentLetterboxingEnabled();
+ break;
default:
getErrPrintWriter().println(
"Error: Unrecognized letterbox style option: " + arg);
@@ -1181,6 +1210,7 @@ public class WindowManagerShellCommand extends ShellCommand {
mLetterboxConfiguration.resetDefaultPositionForVerticalReachability();
mLetterboxConfiguration.resetIsEducationEnabled();
mLetterboxConfiguration.resetIsSplitScreenAspectRatioForUnresizableAppsEnabled();
+ mLetterboxConfiguration.resetTranslucentLetterboxingEnabled();
}
}
@@ -1217,7 +1247,6 @@ public class WindowManagerShellCommand extends ShellCommand {
pw.println("Is using split screen aspect ratio as aspect ratio for unresizable apps: "
+ mLetterboxConfiguration
.getIsSplitScreenAspectRatioForUnresizableAppsEnabled());
-
pw.println("Background type: "
+ LetterboxConfiguration.letterboxBackgroundTypeToString(
mLetterboxConfiguration.getLetterboxBackgroundType()));
@@ -1227,6 +1256,12 @@ public class WindowManagerShellCommand extends ShellCommand {
+ mLetterboxConfiguration.getLetterboxBackgroundWallpaperBlurRadius());
pw.println(" Wallpaper dark scrim alpha: "
+ mLetterboxConfiguration.getLetterboxBackgroundWallpaperDarkScrimAlpha());
+
+ if (mLetterboxConfiguration.isTranslucentLetterboxingEnabled()) {
+ pw.println("Letterboxing for translucent activities: enabled");
+ } else {
+ pw.println("Letterboxing for translucent activities: disabled");
+ }
}
return 0;
}
@@ -1419,12 +1454,16 @@ public class WindowManagerShellCommand extends ShellCommand {
pw.println(" --isSplitScreenAspectRatioForUnresizableAppsEnabled [true|1|false|0]");
pw.println(" Whether using split screen aspect ratio as a default aspect ratio for");
pw.println(" unresizable apps.");
+ pw.println(" --isTranslucentLetterboxingEnabled [true|1|false|0]");
+ pw.println(" Whether letterboxing for translucent activities is enabled.");
+
pw.println(" reset-letterbox-style [aspectRatio|cornerRadius|backgroundType");
pw.println(" |backgroundColor|wallpaperBlurRadius|wallpaperDarkScrimAlpha");
pw.println(" |horizontalPositionMultiplier|verticalPositionMultiplier");
pw.println(" |isHorizontalReachabilityEnabled|isVerticalReachabilityEnabled");
- pw.println(" isEducationEnabled||defaultPositionMultiplierForHorizontalReachability");
- pw.println(" ||defaultPositionMultiplierForVerticalReachability]");
+ pw.println(" |isEducationEnabled||defaultPositionMultiplierForHorizontalReachability");
+ pw.println(" |isTranslucentLetterboxingEnabled");
+ pw.println(" |defaultPositionMultiplierForVerticalReachability]");
pw.println(" Resets overrides to default values for specified properties separated");
pw.println(" by space, e.g. 'reset-letterbox-style aspectRatio cornerRadius'.");
pw.println(" If no arguments provided, all values will be reset.");
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index babad4d4d744..94c33f27f651 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -164,6 +164,114 @@ public class SizeCompatTests extends WindowTestsBase {
}
@Test
+ public void testApplyStrategyToTranslucentActivities() {
+ mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
+ setUpDisplaySizeWithApp(2000, 1000);
+ prepareUnresizable(mActivity, 1.5f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
+ mActivity.info.setMinAspectRatio(1.2f);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ // Translucent Activity
+ final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
+ .setLaunchedFromUid(mActivity.getUid())
+ .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE)
+ .setMinAspectRatio(1.1f)
+ .setMaxAspectRatio(3f)
+ .build();
+ doReturn(false).when(translucentActivity).fillsParent();
+ mTask.addChild(translucentActivity);
+ // We check bounds
+ final Rect opaqueBounds = mActivity.getConfiguration().windowConfiguration.getBounds();
+ final Rect translucentRequestedBounds = translucentActivity.getRequestedOverrideBounds();
+ assertEquals(opaqueBounds, translucentRequestedBounds);
+ // We check orientation
+ final int translucentOrientation =
+ translucentActivity.getRequestedConfigurationOrientation();
+ assertEquals(ORIENTATION_PORTRAIT, translucentOrientation);
+ // We check aspect ratios
+ assertEquals(1.2f, translucentActivity.getMinAspectRatio(), 0.00001f);
+ assertEquals(1.5f, translucentActivity.getMaxAspectRatio(), 0.00001f);
+ }
+
+ @Test
+ public void testNotApplyStrategyToTranslucentActivitiesWithDifferentUid() {
+ mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
+ setUpDisplaySizeWithApp(2000, 1000);
+ prepareUnresizable(mActivity, 1.5f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
+ mActivity.info.setMinAspectRatio(1.2f);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ // Translucent Activity
+ final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
+ .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE)
+ .setMinAspectRatio(1.1f)
+ .setMaxAspectRatio(3f)
+ .build();
+ doReturn(false).when(translucentActivity).fillsParent();
+ mTask.addChild(translucentActivity);
+ // We check bounds
+ final Rect opaqueBounds = mActivity.getConfiguration().windowConfiguration.getBounds();
+ final Rect translucentRequestedBounds = translucentActivity.getRequestedOverrideBounds();
+ assertNotEquals(opaqueBounds, translucentRequestedBounds);
+ }
+
+ @Test
+ public void testApplyStrategyToMultipleTranslucentActivities() {
+ mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
+ setUpDisplaySizeWithApp(2000, 1000);
+ prepareUnresizable(mActivity, 1.5f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
+ mActivity.info.setMinAspectRatio(1.2f);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ // Translucent Activity
+ final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
+ .setLaunchedFromUid(mActivity.getUid())
+ .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE)
+ .setMinAspectRatio(1.1f)
+ .setMaxAspectRatio(3f)
+ .build();
+ doReturn(false).when(translucentActivity).fillsParent();
+ mTask.addChild(translucentActivity);
+ // We check bounds
+ final Rect opaqueBounds = mActivity.getConfiguration().windowConfiguration.getBounds();
+ final Rect translucentRequestedBounds = translucentActivity.getRequestedOverrideBounds();
+ assertEquals(opaqueBounds, translucentRequestedBounds);
+ // Launch another translucent activity
+ final ActivityRecord translucentActivity2 = new ActivityBuilder(mAtm)
+ .setLaunchedFromUid(mActivity.getUid())
+ .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE)
+ .build();
+ doReturn(false).when(translucentActivity2).fillsParent();
+ mTask.addChild(translucentActivity2);
+ // We check bounds
+ final Rect translucent2RequestedBounds = translucentActivity2.getRequestedOverrideBounds();
+ assertEquals(opaqueBounds, translucent2RequestedBounds);
+ }
+
+ @Test
+ public void testTranslucentActivitiesDontGoInSizeCompactMode() {
+ mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
+ setUpDisplaySizeWithApp(2800, 1400);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ prepareUnresizable(mActivity, -1f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
+ // Rotate to put activity in size compat mode.
+ rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
+ assertTrue(mActivity.inSizeCompatMode());
+ // Rotate back
+ rotateDisplay(mActivity.mDisplayContent, ROTATION_0);
+ assertFalse(mActivity.inSizeCompatMode());
+ // We launch a transparent activity
+ final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
+ .setLaunchedFromUid(mActivity.getUid())
+ .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
+ .build();
+ doReturn(true).when(translucentActivity).fillsParent();
+ mTask.addChild(translucentActivity);
+ // It should not be in SCM
+ assertFalse(translucentActivity.inSizeCompatMode());
+ // We rotate again
+ rotateDisplay(translucentActivity.mDisplayContent, ROTATION_90);
+ assertFalse(translucentActivity.inSizeCompatMode());
+ }
+
+ @Test
public void testRestartProcessIfVisible() {
setUpDisplaySizeWithApp(1000, 2500);
doNothing().when(mSupervisor).scheduleRestartTimeout(mActivity);