summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/wm/AppCompatAspectRatioOverrides.java7
-rw-r--r--services/core/java/com/android/server/wm/AppCompatController.java9
-rw-r--r--services/core/java/com/android/server/wm/DesktopAppCompatAspectRatioPolicy.java294
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java25
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppCompatConfigurationRobot.java5
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DesktopAppCompatAspectRatioPolicyTests.java494
6 files changed, 825 insertions, 9 deletions
diff --git a/services/core/java/com/android/server/wm/AppCompatAspectRatioOverrides.java b/services/core/java/com/android/server/wm/AppCompatAspectRatioOverrides.java
index d2f3d1db16a7..44276055f346 100644
--- a/services/core/java/com/android/server/wm/AppCompatAspectRatioOverrides.java
+++ b/services/core/java/com/android/server/wm/AppCompatAspectRatioOverrides.java
@@ -263,6 +263,13 @@ class AppCompatAspectRatioOverrides {
&& cameraPolicy.isTreatmentEnabledForActivity(mActivityRecord));
}
+ /**
+ * Returns the value of the user aspect ratio override property. If unset, return {@code true}.
+ */
+ boolean getAllowUserAspectRatioOverridePropertyValue() {
+ return !mAllowUserAspectRatioOverrideOptProp.isFalse();
+ }
+
@VisibleForTesting
int getUserMinAspectRatioOverrideCode() {
try {
diff --git a/services/core/java/com/android/server/wm/AppCompatController.java b/services/core/java/com/android/server/wm/AppCompatController.java
index 906ee201d8e8..3c3b77374d70 100644
--- a/services/core/java/com/android/server/wm/AppCompatController.java
+++ b/services/core/java/com/android/server/wm/AppCompatController.java
@@ -39,6 +39,8 @@ class AppCompatController {
@NonNull
private final AppCompatReachabilityPolicy mAppCompatReachabilityPolicy;
@NonNull
+ private final DesktopAppCompatAspectRatioPolicy mDesktopAppCompatAspectRatioPolicy;
+ @NonNull
private final AppCompatOverrides mAppCompatOverrides;
@NonNull
private final AppCompatDeviceStateQuery mAppCompatDeviceStateQuery;
@@ -63,6 +65,8 @@ class AppCompatController {
wmService.mAppCompatConfiguration);
mAppCompatLetterboxPolicy = new AppCompatLetterboxPolicy(mActivityRecord,
wmService.mAppCompatConfiguration);
+ mDesktopAppCompatAspectRatioPolicy = new DesktopAppCompatAspectRatioPolicy(activityRecord,
+ mAppCompatOverrides, mTransparentPolicy, wmService.mAppCompatConfiguration);
}
@NonNull
@@ -81,6 +85,11 @@ class AppCompatController {
}
@NonNull
+ DesktopAppCompatAspectRatioPolicy getDesktopAppCompatAspectRatioPolicy() {
+ return mDesktopAppCompatAspectRatioPolicy;
+ }
+
+ @NonNull
AppCompatOverrides getAppCompatOverrides() {
return mAppCompatOverrides;
}
diff --git a/services/core/java/com/android/server/wm/DesktopAppCompatAspectRatioPolicy.java b/services/core/java/com/android/server/wm/DesktopAppCompatAspectRatioPolicy.java
new file mode 100644
index 000000000000..8477c6c5b1da
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DesktopAppCompatAspectRatioPolicy.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_EXCLUDE_PORTRAIT_FULLSCREEN;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_SMALL;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_APP_DEFAULT;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_FULLSCREEN;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_UNSET;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+
+import static com.android.server.wm.AppCompatConfiguration.DEFAULT_LETTERBOX_ASPECT_RATIO_FOR_MULTI_WINDOW;
+import static com.android.server.wm.AppCompatConfiguration.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO;
+
+import android.annotation.NonNull;
+import android.app.WindowConfiguration;
+import android.content.pm.ActivityInfo;
+import android.graphics.Rect;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+
+// TODO(b/359217664): Consider refactoring into AppCompatAspectRatioPolicy.
+/**
+ * Encapsulate app compat aspect ratio policy logic specific for desktop windowing initial bounds
+ * calculation.
+ */
+public class DesktopAppCompatAspectRatioPolicy {
+
+ @NonNull
+ private final AppCompatOverrides mAppCompatOverrides;
+ @NonNull
+ private final AppCompatConfiguration mAppCompatConfiguration;
+ @NonNull
+ private final ActivityRecord mActivityRecord;
+ @NonNull
+ private final TransparentPolicy mTransparentPolicy;
+
+ DesktopAppCompatAspectRatioPolicy(@NonNull ActivityRecord activityRecord,
+ @NonNull AppCompatOverrides appCompatOverrides,
+ @NonNull TransparentPolicy transparentPolicy,
+ @NonNull AppCompatConfiguration appCompatConfiguration) {
+ mActivityRecord = activityRecord;
+ mAppCompatOverrides = appCompatOverrides;
+ mTransparentPolicy = transparentPolicy;
+ mAppCompatConfiguration = appCompatConfiguration;
+ }
+
+ /**
+ * Calculates the final aspect ratio of an launching activity based on the task it will be
+ * launched in. Takes into account any min or max aspect ratio constraints.
+ */
+ float calculateAspectRatio(@NonNull Task task) {
+ final float maxAspectRatio = getMaxAspectRatio();
+ final float minAspectRatio = getMinAspectRatio(task);
+ float desiredAspectRatio = 0;
+ desiredAspectRatio = getDesiredAspectRatio(task);
+ if (maxAspectRatio >= 1 && desiredAspectRatio > maxAspectRatio) {
+ desiredAspectRatio = maxAspectRatio;
+ } else if (minAspectRatio >= 1 && desiredAspectRatio < minAspectRatio) {
+ desiredAspectRatio = minAspectRatio;
+ }
+ return desiredAspectRatio;
+ }
+
+ /**
+ * Returns the aspect ratio desired by the system for current activity, not taking into account
+ * any min or max aspect ratio constraints.
+ */
+ @VisibleForTesting
+ float getDesiredAspectRatio(@NonNull Task task) {
+ final float letterboxAspectRatioOverride = getFixedOrientationLetterboxAspectRatio(task);
+ // Aspect ratio as suggested by the system. Apps requested mix/max aspect ratio will
+ // be respected in #calculateAspectRatio.
+ if (isDefaultMultiWindowLetterboxAspectRatioDesired(task)) {
+ return DEFAULT_LETTERBOX_ASPECT_RATIO_FOR_MULTI_WINDOW;
+ } else if (letterboxAspectRatioOverride > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO) {
+ return letterboxAspectRatioOverride;
+ }
+ return AppCompatUtils.computeAspectRatio(task.getDisplayArea().getBounds());
+ }
+
+ /**
+ * Determines the letterbox aspect ratio for an application based on its orientation and
+ * resizability.
+ */
+ private float getFixedOrientationLetterboxAspectRatio(@NonNull Task task) {
+ return mActivityRecord.shouldCreateCompatDisplayInsets()
+ ? getDefaultMinAspectRatioForUnresizableApps(task)
+ : getDefaultMinAspectRatio(task);
+ }
+
+ /**
+ * Calculates the aspect ratio of the available display area when an app enters split-screen on
+ * a given device, taking into account any dividers and insets.
+ */
+ private float getSplitScreenAspectRatio(@NonNull Task task) {
+ // Getting the same aspect ratio that apps get in split screen.
+ final DisplayArea displayArea = task.getDisplayArea();
+ final int dividerWindowWidth =
+ mActivityRecord.mWmService.mContext.getResources().getDimensionPixelSize(
+ R.dimen.docked_stack_divider_thickness);
+ final int dividerInsets =
+ mActivityRecord.mWmService.mContext.getResources().getDimensionPixelSize(
+ R.dimen.docked_stack_divider_insets);
+ final int dividerSize = dividerWindowWidth - dividerInsets * 2;
+ final Rect bounds = new Rect(displayArea.getWindowConfiguration().getAppBounds());
+ if (bounds.width() >= bounds.height()) {
+ bounds.inset(/* dx */ dividerSize / 2, /* dy */ 0);
+ bounds.right = bounds.centerX();
+ } else {
+ bounds.inset(/* dx */ 0, /* dy */ dividerSize / 2);
+ bounds.bottom = bounds.centerY();
+ }
+ return AppCompatUtils.computeAspectRatio(bounds);
+ }
+
+
+ /**
+ * Returns the minimum aspect ratio for unresizable apps as determined by the system.
+ */
+ private float getDefaultMinAspectRatioForUnresizableApps(@NonNull Task task) {
+ if (!mAppCompatConfiguration.getIsSplitScreenAspectRatioForUnresizableAppsEnabled()) {
+ return mAppCompatConfiguration.getDefaultMinAspectRatioForUnresizableApps()
+ > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO
+ ? mAppCompatConfiguration.getDefaultMinAspectRatioForUnresizableApps()
+ : getDefaultMinAspectRatio(task);
+ }
+
+ return getSplitScreenAspectRatio(task);
+ }
+
+ /**
+ * Returns the default minimum aspect ratio for apps as determined by the system.
+ */
+ private float getDefaultMinAspectRatio(@NonNull Task task) {
+ if (!mAppCompatConfiguration.getIsDisplayAspectRatioEnabledForFixedOrientationLetterbox()) {
+ return mAppCompatConfiguration.getFixedOrientationLetterboxAspectRatio();
+ }
+ return getDisplayAreaMinAspectRatio(task);
+ }
+
+ /**
+ * Calculates the aspect ratio of the available display area.
+ */
+ private float getDisplayAreaMinAspectRatio(@NonNull Task task) {
+ final DisplayArea displayArea = task.getDisplayArea();
+ final Rect bounds = new Rect(displayArea.getWindowConfiguration().getAppBounds());
+ return AppCompatUtils.computeAspectRatio(bounds);
+ }
+
+ /**
+ * Returns {@code true} if the default aspect ratio for a letterboxed app in multi-window mode
+ * should be used.
+ */
+ private boolean isDefaultMultiWindowLetterboxAspectRatioDesired(@NonNull Task task) {
+ final DisplayContent dc = task.mDisplayContent;
+ final int windowingMode = task.getDisplayArea().getWindowingMode();
+ return WindowConfiguration.inMultiWindowMode(windowingMode)
+ && !dc.getIgnoreOrientationRequest();
+ }
+
+ /**
+ * Returns the min aspect ratio of this activity.
+ */
+ private float getMinAspectRatio(@NonNull Task task) {
+ if (mTransparentPolicy.isRunning()) {
+ return mTransparentPolicy.getInheritedMinAspectRatio();
+ }
+
+ final ActivityInfo info = mActivityRecord.info;
+ if (info.applicationInfo == null) {
+ return info.getMinAspectRatio();
+ }
+
+ final AppCompatAspectRatioOverrides aspectRatioOverrides =
+ mAppCompatOverrides.getAppCompatAspectRatioOverrides();
+ if (shouldApplyUserMinAspectRatioOverride(task)) {
+ return aspectRatioOverrides.getUserMinAspectRatio();
+ }
+
+ if (!aspectRatioOverrides.shouldOverrideMinAspectRatio()
+ && !mAppCompatOverrides.getAppCompatCameraOverrides()
+ .shouldOverrideMinAspectRatioForCamera()) {
+ return info.getMinAspectRatio();
+ }
+
+ if (info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY)
+ && !ActivityInfo.isFixedOrientationPortrait(
+ mActivityRecord.getOverrideOrientation())) {
+ return info.getMinAspectRatio();
+ }
+
+ if (info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_EXCLUDE_PORTRAIT_FULLSCREEN)
+ && isFullscreenPortrait(task)) {
+ return info.getMinAspectRatio();
+ }
+
+ if (info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN)) {
+ return Math.max(aspectRatioOverrides.getSplitScreenAspectRatio(),
+ info.getMinAspectRatio());
+ }
+
+ if (info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_LARGE)) {
+ return Math.max(ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE,
+ info.getMinAspectRatio());
+ }
+
+ if (info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_MEDIUM)) {
+ return Math.max(ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE,
+ info.getMinAspectRatio());
+ }
+
+ if (info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_SMALL)) {
+ return Math.max(ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_SMALL_VALUE,
+ info.getMinAspectRatio());
+ }
+ return info.getMinAspectRatio();
+ }
+
+ /**
+ * Returns the max aspect ratio of this activity.
+ */
+ private float getMaxAspectRatio() {
+ if (mTransparentPolicy.isRunning()) {
+ return mTransparentPolicy.getInheritedMaxAspectRatio();
+ }
+ return mActivityRecord.info.getMaxAspectRatio();
+ }
+
+ /**
+ * Whether an applications minimum aspect ratio has been overridden.
+ */
+ boolean hasMinAspectRatioOverride(@NonNull Task task) {
+ return mActivityRecord.info.getMinAspectRatio() < getMinAspectRatio(task);
+ }
+
+ /**
+ * Whether we should apply the user aspect ratio override to the min aspect ratio for the
+ * current app.
+ */
+ private boolean shouldApplyUserMinAspectRatioOverride(@NonNull Task task) {
+ if (!shouldEnableUserAspectRatioSettings(task)) {
+ return false;
+ }
+
+ final int userAspectRatioCode = mAppCompatOverrides.getAppCompatAspectRatioOverrides()
+ .getUserMinAspectRatioOverrideCode();
+
+ return userAspectRatioCode != USER_MIN_ASPECT_RATIO_UNSET
+ && userAspectRatioCode != USER_MIN_ASPECT_RATIO_APP_DEFAULT
+ && userAspectRatioCode != USER_MIN_ASPECT_RATIO_FULLSCREEN;
+ }
+
+ /**
+ * Whether we should enable users to resize the current app.
+ */
+ private boolean shouldEnableUserAspectRatioSettings(@NonNull Task task) {
+ // We use mBooleanPropertyAllowUserAspectRatioOverride to allow apps to opt-out which has
+ // effect only if explicitly false. If mBooleanPropertyAllowUserAspectRatioOverride is null,
+ // the current app doesn't opt-out so the first part of the predicate is true.
+ return mAppCompatOverrides.getAppCompatAspectRatioOverrides()
+ .getAllowUserAspectRatioOverridePropertyValue()
+ && mAppCompatConfiguration.isUserAppAspectRatioSettingsEnabled()
+ && task.mDisplayContent.getIgnoreOrientationRequest();
+ }
+
+ /**
+ * Returns {@code true} if the task window is portrait and fullscreen.
+ */
+ private boolean isFullscreenPortrait(@NonNull Task task) {
+ return task.getConfiguration().orientation == ORIENTATION_PORTRAIT
+ && task.getWindowConfiguration().getWindowingMode() == WINDOWING_MODE_FULLSCREEN;
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
index 1fa68686850e..fee9c6c002c4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
@@ -31,10 +31,10 @@ import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
-import android.app.WindowConfiguration;
+import android.app.WindowConfiguration.WindowingMode;
import android.content.ComponentName;
import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.UserMinAspectRatio;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.view.Surface;
@@ -176,10 +176,14 @@ class AppCompatActivityRobot {
return mActivityStack.getFromTop(fromTop);
}
- void setTaskWindowingMode(@WindowConfiguration.WindowingMode int windowingMode) {
+ void setTaskWindowingMode(@WindowingMode int windowingMode) {
mTaskStack.top().setWindowingMode(windowingMode);
}
+ void setTaskDisplayAreaWindowingMode(@WindowingMode int windowingMode) {
+ mTaskStack.top().getDisplayArea().setWindowingMode(windowingMode);
+ }
+
void setLetterboxedForFixedOrientationAndAspectRatio(boolean enabled) {
doReturn(enabled).when(mActivityStack.top().mAppCompatController
.getAppCompatAspectRatioPolicy()).isLetterboxedForFixedOrientationAndAspectRatio();
@@ -222,10 +226,14 @@ class AppCompatActivityRobot {
.getAppCompatAspectRatioOverrides()).shouldApplyUserFullscreenOverride();
}
- void setGetUserMinAspectRatioOverrideCode(@PackageManager.UserMinAspectRatio int orientation) {
- doReturn(orientation).when(mActivityStack.top()
- .mAppCompatController.getAppCompatAspectRatioOverrides())
- .getUserMinAspectRatioOverrideCode();
+ void setGetUserMinAspectRatioOverrideCode(@UserMinAspectRatio int overrideCode) {
+ doReturn(overrideCode).when(mActivityStack.top().mAppCompatController
+ .getAppCompatAspectRatioOverrides()).getUserMinAspectRatioOverrideCode();
+ }
+
+ void setGetUserMinAspectRatioOverrideValue(float overrideValue) {
+ doReturn(overrideValue).when(mActivityStack.top().mAppCompatController
+ .getAppCompatAspectRatioOverrides()).getUserMinAspectRatio();
}
void setIgnoreOrientationRequest(boolean enabled) {
@@ -233,8 +241,7 @@ class AppCompatActivityRobot {
}
void setTopTaskInMultiWindowMode(boolean inMultiWindowMode) {
- doReturn(inMultiWindowMode).when(mTaskStack.top())
- .inMultiWindowMode();
+ doReturn(inMultiWindowMode).when(mTaskStack.top()).inMultiWindowMode();
}
void setTopActivityAsEmbedded(boolean embedded) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatConfigurationRobot.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatConfigurationRobot.java
index 40a53479e9ab..776005101b70 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatConfigurationRobot.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatConfigurationRobot.java
@@ -52,6 +52,11 @@ class AppCompatConfigurationRobot {
doReturn(enabled).when(mAppCompatConfiguration).isCameraCompatTreatmentEnabled();
}
+ void enableSplitScreenAspectRatioForUnresizableApps(boolean enabled) {
+ doReturn(enabled).when(mAppCompatConfiguration)
+ .getIsSplitScreenAspectRatioForUnresizableAppsEnabled();
+ }
+
void enableCameraCompatTreatmentAtBuildTime(boolean enabled) {
doReturn(enabled).when(mAppCompatConfiguration)
.isCameraCompatTreatmentEnabledAtBuildTime();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DesktopAppCompatAspectRatioPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DesktopAppCompatAspectRatioPolicyTests.java
new file mode 100644
index 000000000000..f4e1d4967ff5
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/DesktopAppCompatAspectRatioPolicyTests.java
@@ -0,0 +1,494 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_SMALL_VALUE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_16_9;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_3_2;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_4_3;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_DISPLAY_SIZE;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_SPLIT_SCREEN;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.server.wm.AppCompatConfiguration.DEFAULT_LETTERBOX_ASPECT_RATIO_FOR_MULTI_WINDOW;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+
+import android.compat.testing.PlatformCompatChangeRule;
+import android.content.pm.ActivityInfo;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.annotation.NonNull;
+
+import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+
+import java.util.function.Consumer;
+
+/**
+ * Test class for {@link DesktopAppCompatAspectRatioPolicy}.
+ * <p>
+ * Build/Install/Run:
+ * atest WmTests:DesktopAppCompatAspectRatioPolicyTests
+ */
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class DesktopAppCompatAspectRatioPolicyTests extends WindowTestsBase {
+ @Rule
+ public TestRule compatChangeRule = new PlatformCompatChangeRule();
+
+ private static final float FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO = 1.33f;
+
+ @Test
+ public void testHasMinAspectRatioOverride_userAspectRatioEnabled_returnTrue() {
+ runTestScenario((robot)-> {
+ robot.conf().enableUserAppAspectRatioSettings(/* enabled */ true);
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setIgnoreOrientationRequest(/* enabled */ true);
+ a.setGetUserMinAspectRatioOverrideValue(3 / 2f);
+ a.setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_3_2);
+ });
+
+ robot.checkHasMinAspectRatioOverride(/* expected */ true);
+ });
+ }
+
+ @Test
+ @DisableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO})
+ public void testHasMinAspectRatioOverride_overrideDisabled_returnsFalse() {
+ runTestScenario((robot)-> {
+ robot.activity().createActivityWithComponent();
+
+ robot.checkHasMinAspectRatioOverride(/* expected */ false);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO})
+ public void testHasMinAspectRatioOverride_overrideEnabled_propertyFalse_returnsFalse() {
+ runTestScenario((robot)-> {
+ robot.activity().createActivityWithComponent();
+ robot.prop().disable(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE);
+
+ robot.checkHasMinAspectRatioOverride(/* expected */ false);
+ });
+ }
+
+ @Test
+ @DisableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO})
+ public void testHasMinAspectRatioOverride_overrideDisabled_propertyTrue_returnsFalse() {
+ runTestScenario((robot)-> {
+ robot.activity().createActivityWithComponent();
+ robot.prop().enable(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE);
+
+ robot.checkHasMinAspectRatioOverride(/* expected */ false);
+ });
+ }
+
+ @Test
+ @DisableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO})
+ public void testHasMinAspectRatioOverride_overrideEnabled_nonPortraitActivity_returnsFalse() {
+ runTestScenario((robot)-> {
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.configureUnresizableTopActivity(SCREEN_ORIENTATION_UNSPECIFIED);
+ });
+
+ robot.checkHasMinAspectRatioOverride(/* expected */ false);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN})
+ public void testHasMinAspectRatioOverride_splitScreenAspectRatioOverride_returnTrue() {
+ runTestScenario((robot)-> {
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
+ });
+
+ robot.checkHasMinAspectRatioOverride(/* expected */ true);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE})
+ public void testHasMinAspectRatioOverride_largeMinAspectRatioOverride_returnTrue() {
+ runTestScenario((robot)-> {
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
+ });
+
+ robot.checkHasMinAspectRatioOverride(/* expected */ true);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
+ public void testHasMinAspectRatioOverride_mediumMinAspectRatioOverride_returnTrue() {
+ runTestScenario((robot)-> {
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
+ });
+
+ robot.checkHasMinAspectRatioOverride(/* expected */ true);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_SMALL})
+ public void testHasMinAspectRatioOverride_smallMinAspectRatioOverride_returnTrue() {
+ runTestScenario((robot)-> {
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
+ });
+
+ robot.checkHasMinAspectRatioOverride(/* expected */ true);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN})
+ public void testCalculateAspectRatio_splitScreenAspectRatioOverride() {
+ runTestScenario((robot)-> {
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setIgnoreOrientationRequest(/* enabled */ false);
+ a.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
+ });
+ robot.setDesiredAspectRatio(1f);
+
+ robot.checkCalculateAspectRatioSplitScreenAspectRatio();
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE})
+ public void testCalculateAspectRatio_largeMinAspectRatioOverride() {
+ runTestScenario((robot)-> {
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setIgnoreOrientationRequest(/* enabled */ false);
+ a.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
+ });
+ robot.setDesiredAspectRatio(1f);
+
+ robot.checkCalculateAspectRatioLargeAspectRatioOverride();
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
+ public void testCalculateAspectRatio_mediumMinAspectRatioOverride() {
+ runTestScenario((robot)-> {
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setIgnoreOrientationRequest(/* enabled */ false);
+ a.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
+ });
+ robot.setDesiredAspectRatio(1f);
+
+ robot.checkCalculateAspectRatioMediumAspectRatioOverride();
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_SMALL})
+ public void testCalculateAspectRatio_smallMinAspectRatioOverride() {
+ runTestScenario((robot)-> {
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setIgnoreOrientationRequest(/* enabled */ false);
+ a.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
+ });
+ robot.setDesiredAspectRatio(1f);
+
+ robot.checkCalculateAspectRatioSmallAspectRatioOverride();
+ });
+ }
+
+ @Test
+ public void testCalculateAspectRatio_defaultMultiWindowLetterboxAspectRatio() {
+ runTestScenario((robot)-> {
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setIgnoreOrientationRequest(/* enabled */ false);
+ a.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
+ a.setTaskDisplayAreaWindowingMode(WINDOWING_MODE_FREEFORM);
+ });
+
+ robot.checkCalculateAspectRatioDefaultLetterboxAspectRatioForMultiWindow();
+ });
+ }
+
+ @Test
+ public void testCalculateAspectRatio_displayAspectRatioEnabledForFixedOrientationLetterbox() {
+ runTestScenario((robot)-> {
+ robot.conf().enableDisplayAspectRatioEnabledForFixedOrientationLetterbox(true);
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setIgnoreOrientationRequest(/* enabled */ true);
+ a.configureTopActivity(/* minAspect */ 0, /* maxAspect */ 0,
+ SCREEN_ORIENTATION_PORTRAIT, /* isUnresizable */ false);
+ });
+
+ robot.checkCalculateAspectRatioDisplayAreaAspectRatio();
+ });
+ }
+
+ @Test
+ public void testCalculateAspectRatio_defaultMinAspectRatio_fixedOrientationAspectRatio() {
+ runTestScenario((robot)-> {
+ robot.applyOnConf((c) -> {
+ c.enableDisplayAspectRatioEnabledForFixedOrientationLetterbox(false);
+ c.setFixedOrientationLetterboxAspectRatio(FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO);
+ });
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setIgnoreOrientationRequest(/* enabled */ true);
+ a.configureTopActivity(/* minAspect */ 0, /* maxAspect */ 0,
+ SCREEN_ORIENTATION_PORTRAIT, /* isUnresizable */ false);
+ });
+
+ robot.checkCalculateAspectRatioDefaultMinFixedOrientationAspectRatio();
+ });
+ }
+
+ @Test
+ public void testCalculateAspectRatio_splitScreenForUnresizeableEnabled() {
+ runTestScenario((robot) -> {
+ robot.conf().enableSplitScreenAspectRatioForUnresizableApps(/* isEnabled */ true);
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setIgnoreOrientationRequest(/* enabled */ true);
+ a.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
+ });
+
+ robot.checkCalculateAspectRatioSplitScreenAspectRatio();
+ });
+ }
+
+ @Test
+ public void testCalculateAspectRatio_user3By2AspectRatioOverride() {
+ runTestScenario((robot)-> {
+ robot.conf().enableUserAppAspectRatioSettings(/* enabled */ true);
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setIgnoreOrientationRequest(/* enabled */ true);
+ a.setGetUserMinAspectRatioOverrideValue(3 / 2f);
+ a.setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_3_2);
+ });
+ robot.setDesiredAspectRatio(1f);
+
+ robot.checkCalculateAspectRatioUser3By2AspectRatiOverride();
+ });
+ }
+
+ @Test
+ public void testCalculateAspectRatio_user4By3AspectRatioOverride() {
+ runTestScenario((robot)-> {
+ robot.conf().enableUserAppAspectRatioSettings(/* enabled */ true);
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setIgnoreOrientationRequest(/* enabled */ true);
+ a.setGetUserMinAspectRatioOverrideValue(4 / 3f);
+ a.setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_4_3);
+ });
+ robot.setDesiredAspectRatio(1f);
+
+ robot.checkCalculateAspectRatioUser4By3AspectRatiOverride();
+ });
+ }
+
+ @Test
+ public void testCalculateAspectRatio_user16By9AspectRatioOverride() {
+ runTestScenario((robot)-> {
+ robot.conf().enableUserAppAspectRatioSettings(/* enabled */ true);
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setIgnoreOrientationRequest(/* enabled */ true);
+ a.setGetUserMinAspectRatioOverrideValue(16 / 9f);
+ a.setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_16_9);
+ });
+ robot.setDesiredAspectRatio(1f);
+
+ robot.checkCalculateAspectRatioUser16By9AspectRatioOverride();
+ });
+ }
+
+ @Test
+ public void testCalculateAspectRatio_userSplitScreenAspectRatioOverride() {
+ runTestScenario((robot)-> {
+ robot.conf().enableUserAppAspectRatioSettings(/* enabled */ true);
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setIgnoreOrientationRequest(/* enabled */ true);
+ a.setGetUserMinAspectRatioOverrideValue(robot.getSplitScreenAspectRatio());
+ a.setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_SPLIT_SCREEN);
+ });
+ robot.setDesiredAspectRatio(1f);
+
+ robot.checkCalculateAspectRatioSplitScreenAspectRatio();
+ });
+ }
+
+ @Test
+ public void testCalculateAspectRatio_userDisplayAreaAspectRatioOverride() {
+ runTestScenario((robot)-> {
+ robot.conf().enableUserAppAspectRatioSettings(/* enabled */ true);
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setIgnoreOrientationRequest(/* enabled */ true);
+ a.setGetUserMinAspectRatioOverrideValue(robot.getDisplayAreaAspectRatio());
+ a.setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_DISPLAY_SIZE);
+ });
+ robot.setDesiredAspectRatio(1f);
+
+ robot.checkCalculateAspectRatioDisplayAreaAspectRatio();
+ });
+ }
+
+ /**
+ * Runs a test scenario providing a Robot.
+ */
+ void runTestScenario(@NonNull Consumer<DesktopAppCompatAspectRatioPolicyRobotTest> consumer) {
+ final DesktopAppCompatAspectRatioPolicyRobotTest robot =
+ new DesktopAppCompatAspectRatioPolicyRobotTest(mWm, mAtm, mSupervisor);
+ consumer.accept(robot);
+ }
+
+ private static class DesktopAppCompatAspectRatioPolicyRobotTest extends AppCompatRobotBase {
+ DesktopAppCompatAspectRatioPolicyRobotTest(@NonNull WindowManagerService wm,
+ @NonNull ActivityTaskManagerService atm,
+ @NonNull ActivityTaskSupervisor supervisor) {
+ super(wm, atm, supervisor);
+ }
+
+ @Override
+ void onPostActivityCreation(@NonNull ActivityRecord activity) {
+ super.onPostActivityCreation(activity);
+ spyOn(activity.mAppCompatController.getAppCompatAspectRatioOverrides());
+ spyOn(activity.mAppCompatController.getDesktopAppCompatAspectRatioPolicy());
+ }
+
+ void setDesiredAspectRatio(float aspectRatio) {
+ doReturn(aspectRatio).when(getDesktopAppCompatAspectRatioPolicy())
+ .getDesiredAspectRatio(any());
+ }
+
+ DesktopAppCompatAspectRatioPolicy getDesktopAppCompatAspectRatioPolicy() {
+ return getTopActivity().mAppCompatController.getDesktopAppCompatAspectRatioPolicy();
+ }
+
+ float calculateAspectRatio() {
+ return getDesktopAppCompatAspectRatioPolicy().calculateAspectRatio(
+ getTopActivity().getTask());
+ }
+
+ ActivityRecord getTopActivity() {
+ return this.activity().top();
+ }
+
+ float getSplitScreenAspectRatio() {
+ return getTopActivity().mAppCompatController.getAppCompatAspectRatioOverrides()
+ .getSplitScreenAspectRatio();
+ }
+
+ float getDisplayAreaAspectRatio() {
+ final Rect appBounds = getTopActivity().getDisplayArea().getWindowConfiguration()
+ .getAppBounds();
+ return AppCompatUtils.computeAspectRatio(appBounds);
+ }
+
+ void checkHasMinAspectRatioOverride(boolean expected) {
+ assertEquals(expected, this.activity().top().mAppCompatController
+ .getDesktopAppCompatAspectRatioPolicy().hasMinAspectRatioOverride(
+ this.activity().top().getTask()));
+ }
+
+ void checkCalculateAspectRatioSplitScreenAspectRatio() {
+ assertEquals(getSplitScreenAspectRatio(), calculateAspectRatio(), FLOAT_TOLLERANCE);
+ }
+
+ void checkCalculateAspectRatioLargeAspectRatioOverride() {
+ assertEquals(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE, calculateAspectRatio(),
+ FLOAT_TOLLERANCE);
+ }
+
+ void checkCalculateAspectRatioMediumAspectRatioOverride() {
+ assertEquals(OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE, calculateAspectRatio(),
+ FLOAT_TOLLERANCE);
+ }
+
+ void checkCalculateAspectRatioSmallAspectRatioOverride() {
+ assertEquals(OVERRIDE_MIN_ASPECT_RATIO_SMALL_VALUE, calculateAspectRatio(),
+ FLOAT_TOLLERANCE);
+ }
+
+ void checkCalculateAspectRatioDefaultLetterboxAspectRatioForMultiWindow() {
+ assertEquals(DEFAULT_LETTERBOX_ASPECT_RATIO_FOR_MULTI_WINDOW, calculateAspectRatio(),
+ FLOAT_TOLLERANCE);
+ }
+
+ void checkCalculateAspectRatioDisplayAreaAspectRatio() {
+ assertEquals(getDisplayAreaAspectRatio(), calculateAspectRatio(), FLOAT_TOLLERANCE);
+ }
+
+ void checkCalculateAspectRatioDefaultMinFixedOrientationAspectRatio() {
+ assertEquals(FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO, calculateAspectRatio(),
+ FLOAT_TOLLERANCE);
+ }
+
+ void checkCalculateAspectRatioUser3By2AspectRatiOverride() {
+ assertEquals(3 / 2f, calculateAspectRatio(), FLOAT_TOLLERANCE);
+ }
+
+ void checkCalculateAspectRatioUser4By3AspectRatiOverride() {
+ assertEquals(4 / 3f, calculateAspectRatio(), FLOAT_TOLLERANCE);
+ }
+
+ void checkCalculateAspectRatioUser16By9AspectRatioOverride() {
+ assertEquals(16 / 9f, calculateAspectRatio(), FLOAT_TOLLERANCE);
+ }
+ }
+}