diff options
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); + } + } +} |