diff options
| author | 2024-08-09 07:12:00 +0000 | |
|---|---|---|
| committer | 2024-08-15 06:55:42 +0000 | |
| commit | 9dd318ea87df9dd21cde4b53b4d37b482443994f (patch) | |
| tree | fa31a7b24b12646c3a3e6f6a88bff823c1dab766 | |
| parent | fda1f4323268706a0d9e11fc96ca237f0bce84c7 (diff) | |
Introduce DWAppCompatAspectRatioPolicy
Create a new app compat policy which contains desktop windowing specific
aspect ratio logic for the inital bounds calculation. Since we know a
lot of information about the final state of the application, we can make
some assumption compared to the existing `AppCompatAspectRatioPolicy`.
For example, we can assume the windowing mode will be freefrom, that we
are not in table top or book mode and that the camera activity is not
active. This also differs from the `AppCompatAspectRatioPolicy` through
the way we access display information. As the activity record is not
fully populated at the point the initial bounds for desktop windowing
are created, we must access the display information through the task
instead.
Flag: NONE(unused code)
Bug: 353457301
Test: atest WmTests:DesktopAppCompatAspectRatioPolicyTests
Change-Id: I297f7a17f54871c134336282e839f3c0ce0c5182
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); + } + } +} |