Fix Letterboxing for Transparent Activities

We want a not resizable transparent activity to inherits
the constraints from the activity below. This allows the
transparent activity to be letterboxed.
The specific logic for transparent activities is enabled
when the config_letterboxIsEnabledForTranslucentActivities
flag is true.

Fixes: 220703089
Test: Run `atest SizeCompatTests` with the following tests:
  testApplyStrategyToTranslucentActivities
  testTranslucentActivitiesDontGoInSizeCompactMode
  testNotApplyStrategyToTranslucentActivitiesWithDifferentUid
  testApplyStrategyToMultipleTranslucentActivities

Change-Id: I6b5e8517ae8286f7543c3f57c158276aab6cfbfc
This commit is contained in:
Massimo Carli
2022-10-07 10:54:13 +00:00
parent 1b89cdbde8
commit de53131014
8 changed files with 474 additions and 25 deletions

View File

@@ -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>

View File

@@ -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" />

View File

@@ -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) {
@@ -7676,6 +7677,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.
@@ -7803,6 +7813,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
@Nullable
CompatDisplayInsets getCompatDisplayInsets() {
if (mLetterboxUiController.hasInheritedLetterboxBehavior()) {
return mLetterboxUiController.getInheritedCompatDisplayInsets();
}
return mCompatDisplayInsets;
}
@@ -7885,6 +7898,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;
@@ -7946,6 +7963,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
@Override
float getCompatScale() {
if (mLetterboxUiController.hasInheritedLetterboxBehavior()) {
return mLetterboxUiController.getInheritedSizeCompatScale();
}
return hasSizeCompatBounds() ? mSizeCompatScale : super.getCompatScale();
}
@@ -8054,6 +8074,16 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
logAppCompatState();
}
/**
* @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.
*
@@ -8094,6 +8124,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;
}
@@ -8564,6 +8598,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();
@@ -8584,10 +8623,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;
}
@@ -8809,7 +8849,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();
@@ -8916,6 +8956,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();
}
@@ -8960,11 +9003,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;
}
/**

View File

@@ -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);
}
}

View File

@@ -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;
}
}
}

View File

@@ -3946,27 +3946,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);
}
/**

View File

@@ -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.");

View File

@@ -163,6 +163,114 @@ public class SizeCompatTests extends WindowTestsBase {
setUpApp(builder.build());
}
@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);