Merge "Fix Letterboxing for Transparent Activities" into tm-qpr-dev

This commit is contained in:
Massimo Carli
2022-12-14 14:29:56 +00:00
committed by Android (Google) Code Review
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) {
@@ -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();
}
@@ -8060,6 +8080,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.
*
@@ -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;
}
/**

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

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

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