From 4f1e391a83e525451eb4797412954b2177ef1b84 Mon Sep 17 00:00:00 2001 From: Massimo Carli Date: Thu, 30 Jun 2022 08:53:48 +0000 Subject: [PATCH] Per-app config for split screen aspect ratio We're defining a new compat framework change id that indicates whether a given app should use the split screen aspect ratio. Test: As manual testing use 'adb shell am compat disable/enable 208648326 '. For the unit tests run 'atest WmTests:SizeCompatTests'. Fixes: 208648326 Change-Id: I0232d6d98b10e546b840c08ffbf6e7be9c465965 Merged-In: I0232d6d98b10e546b840c08ffbf6e7be9c465965 --- .../java/android/content/pm/ActivityInfo.java | 51 ++++----- .../com/android/server/wm/ActivityRecord.java | 31 ++++- .../server/wm/LetterboxUiController.java | 4 + .../server/wm/TaskLaunchParamsModifier.java | 7 +- .../android/server/wm/SizeCompatTests.java | 106 +++++++++++++++++- 5 files changed, 162 insertions(+), 37 deletions(-) diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index a3d595ef65756..0bbe8c47807f0 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -1108,6 +1108,16 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { @TestApi public static final float OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE = 16 / 9f; + /** + * Enables the use of split screen aspect ratio. This allows an app to use all the available + * space in split mode avoiding letterboxing. + * @hide + */ + @ChangeId + @Disabled + @Overridable + public static final long OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN = 208648326L; + /** * Compares activity window layout min width/height with require space for multi window to * determine if it can be put into multi window mode. @@ -1317,8 +1327,8 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { * Returns true if the activity has maximum or minimum aspect ratio. * @hide */ - public boolean hasFixedAspectRatio(@ScreenOrientation int orientation) { - return getMaxAspectRatio() != 0 || getMinAspectRatio(orientation) != 0; + public boolean hasFixedAspectRatio() { + return getMaxAspectRatio() != 0 || getMinAspectRatio() != 0; } /** @@ -1460,30 +1470,10 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { } /** - * Returns the min aspect ratio of this activity. - * - * This takes into account the minimum aspect ratio as defined in the app's manifest and - * possible overrides as per OVERRIDE_MIN_ASPECT_RATIO. - * - * In the rare cases where the manifest minimum aspect ratio is required, use - * {@code getManifestMinAspectRatio}. + * Returns the min aspect ratio of this activity as defined in the manifest file. * @hide */ - public float getMinAspectRatio(@ScreenOrientation int orientation) { - if (applicationInfo == null || !isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO) || ( - isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY) - && !isFixedOrientationPortrait(orientation))) { - return mMinAspectRatio; - } - - if (isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_LARGE)) { - return Math.max(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE, mMinAspectRatio); - } - - if (isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_MEDIUM)) { - return Math.max(OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE, mMinAspectRatio); - } - + public float getMinAspectRatio() { return mMinAspectRatio; } @@ -1512,7 +1502,13 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { } } - private boolean isChangeEnabled(long changeId) { + /** + * Checks if a changeId is enabled for the current user + * @param changeId The changeId to verify + * @return True of the changeId is enabled + * @hide + */ + public boolean isChangeEnabled(long changeId) { return CompatChanges.isChangeEnabled(changeId, applicationInfo.packageName, UserHandle.getUserHandleForUid(applicationInfo.uid)); } @@ -1633,12 +1629,9 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { if (getMaxAspectRatio() != 0) { pw.println(prefix + "maxAspectRatio=" + getMaxAspectRatio()); } - final float minAspectRatio = getMinAspectRatio(screenOrientation); + final float minAspectRatio = getMinAspectRatio(); if (minAspectRatio != 0) { pw.println(prefix + "minAspectRatio=" + minAspectRatio); - if (getManifestMinAspectRatio() != minAspectRatio) { - pw.println(prefix + "getManifestMinAspectRatio=" + getManifestMinAspectRatio()); - } } if (supportsSizeChanges) { pw.println(prefix + "supportsSizeChanges=true"); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 1f3f039a83fac..e2c0628e10f4d 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -78,6 +78,11 @@ import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS; import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT; import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED; import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER; +import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO; +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_TO_ALIGN_WITH_SPLIT_SCREEN; import static android.content.pm.ActivityInfo.PERSIST_ACROSS_REBOOTS; import static android.content.pm.ActivityInfo.PERSIST_ROOT_ONLY; import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE; @@ -8759,15 +8764,35 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A /** * Returns the min aspect ratio of this activity. */ - private float getMinAspectRatio() { - return info.getMinAspectRatio(getRequestedOrientation()); + float getMinAspectRatio() { + if (info.applicationInfo == null || !info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO) || ( + info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY) + && !ActivityInfo.isFixedOrientationPortrait(getRequestedOrientation()))) { + return info.getMinAspectRatio(); + } + + if (info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN)) { + return Math.max(mLetterboxUiController.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()); + } + return info.getMinAspectRatio(); } /** * Returns true if the activity has maximum or minimum aspect ratio. */ private boolean hasFixedAspectRatio() { - return info.hasFixedAspectRatio(getRequestedOrientation()); + return info.getMaxAspectRatio() != 0 || getMinAspectRatio() != 0; } /** diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java index ec9ee29679a46..c8ed602b0f1d7 100644 --- a/services/core/java/com/android/server/wm/LetterboxUiController.java +++ b/services/core/java/com/android/server/wm/LetterboxUiController.java @@ -257,6 +257,10 @@ final class LetterboxUiController { : mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio(); } + return getSplitScreenAspectRatio(); + } + + float getSplitScreenAspectRatio() { int dividerWindowWidth = getResources().getDimensionPixelSize(R.dimen.docked_stack_divider_thickness); int dividerInsets = diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java index 9bb02710a5bc1..8f07bb7aad028 100644 --- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java +++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java @@ -731,7 +731,7 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { } // First we get the default size we want. - getDefaultFreeformSize(root.info, displayArea, layout, orientation, mTmpBounds); + getDefaultFreeformSize(root, displayArea, layout, orientation, mTmpBounds); if (hasInitialBounds || sizeMatches(inOutBounds, mTmpBounds)) { // We're here because either input parameters specified initial bounds, or the suggested // bounds have the same size of the default freeform size. We should use the suggested @@ -799,7 +799,7 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { return orientation; } - private void getDefaultFreeformSize(@NonNull ActivityInfo info, + private void getDefaultFreeformSize(@NonNull ActivityRecord activityRecord, @NonNull TaskDisplayArea displayArea, @NonNull ActivityInfo.WindowLayout layout, int orientation, @NonNull Rect bounds) { // Default size, which is letterboxing/pillarboxing in displayArea. That's to say the large @@ -807,6 +807,7 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { // dimension of default size is calculated to keep the same aspect ratio as the // displayArea's. Here we use stable bounds of displayArea because that indicates the area // that isn't occupied by system widgets (e.g. sysbar and navbar). + final ActivityInfo info = activityRecord.info; final Rect stableBounds = mTmpStableBounds; displayArea.getStableRect(stableBounds); final int portraitHeight = Math.min(stableBounds.width(), stableBounds.height()); @@ -832,7 +833,7 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { final int layoutMinHeight = (layout == null) ? -1 : layout.minHeight; // Aspect ratio requirements. - final float minAspectRatio = info.getMinAspectRatio(orientation); + final float minAspectRatio = activityRecord.getMinAspectRatio(); final float maxAspectRatio = info.getMaxAspectRatio(); final int width = Math.min(defaultWidth, Math.max(phoneWidth, layoutMinWidth)); diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java index 21839aac4ec51..2426eb143862a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -97,12 +97,12 @@ import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges; import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges; import org.junit.After; +import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestRule; import org.junit.runner.RunWith; -import org.mockito.Mockito; /** * Tests for Size Compatibility mode. @@ -663,7 +663,7 @@ public class SizeCompatTests extends WindowTestsBase { final ActivityRecord activity = new ActivityBuilder(mAtm) .setTask(mTask) .setResizeMode(ActivityInfo.RESIZE_MODE_UNRESIZEABLE) - .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) + .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT) .build(); assertFalse(activity.shouldCreateCompatDisplayInsets()); @@ -1537,6 +1537,108 @@ public class SizeCompatTests extends WindowTestsBase { assertFitted(); } + @Test + @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO, + ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN}) + public void testOverrideSplitScreenAspectRatioForUnresizablePortraitApps() { + final int displayWidth = 1400; + final int displayHeight = 1600; + setUpDisplaySizeWithApp(displayWidth, displayHeight); + final ActivityRecord activity = new ActivityBuilder(mAtm) + .setTask(mTask) + .setComponent(ComponentName.createRelative(mContext, + SizeCompatTests.class.getName())) + .setMinAspectRatio(1.1f) + .setUid(android.os.Process.myUid()) + .build(); + // Setup Letterbox Configuration + activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + activity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(1.5f); + // Non-resizable portrait activity + prepareUnresizable(activity, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + float expectedAspectRatio = 1f * displayWidth / getExpectedSplitSize(displayHeight); + final Rect afterBounds = activity.getBounds(); + final float afterAspectRatio = (float) (afterBounds.height()) / afterBounds.width(); + Assert.assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f); + } + + @Test + @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO, + ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN}) + public void testOverrideSplitScreenAspectRatioForUnresizablePortraitAppsFromLandscape() { + final int displayWidth = 1600; + final int displayHeight = 1400; + setUpDisplaySizeWithApp(displayWidth, displayHeight); + final ActivityRecord activity = new ActivityBuilder(mAtm) + .setTask(mTask) + .setComponent(ComponentName.createRelative(mContext, + SizeCompatTests.class.getName())) + .setMinAspectRatio(1.1f) + .setUid(android.os.Process.myUid()) + .build(); + // Setup Letterbox Configuration + activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + activity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(1.5f); + // Non-resizable portrait activity + prepareUnresizable(activity, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + float expectedAspectRatio = 1f * displayHeight / getExpectedSplitSize(displayWidth); + final Rect afterBounds = activity.getBounds(); + final float afterAspectRatio = (float) (afterBounds.height()) / afterBounds.width(); + Assert.assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f); + } + + @Test + @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO, + ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN}) + @DisableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY}) + public void testOverrideSplitScreenAspectRatioForUnresizableLandscapeApps() { + final int displayWidth = 1400; + final int displayHeight = 1600; + setUpDisplaySizeWithApp(displayWidth, displayHeight); + final ActivityRecord activity = new ActivityBuilder(mAtm) + .setTask(mTask) + .setComponent(ComponentName.createRelative(mContext, + SizeCompatTests.class.getName())) + .setMinAspectRatio(1.1f) + .setUid(android.os.Process.myUid()) + .build(); + // Setup Letterbox Configuration + activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + activity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(1.5f); + // Non-resizable portrait activity + prepareUnresizable(activity, ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + float expectedAspectRatio = 1f * displayWidth / getExpectedSplitSize(displayHeight); + final Rect afterBounds = activity.getBounds(); + final float afterAspectRatio = (float) (afterBounds.width()) / afterBounds.height(); + Assert.assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f); + } + + @Test + @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO, + ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN}) + @DisableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY}) + public void testOverrideSplitScreenAspectRatioForUnresizableLandscapeAppsFromLandscape() { + final int displayWidth = 1600; + final int displayHeight = 1400; + setUpDisplaySizeWithApp(displayWidth, displayHeight); + final ActivityRecord activity = new ActivityBuilder(mAtm) + .setTask(mTask) + .setComponent(ComponentName.createRelative(mContext, + SizeCompatTests.class.getName())) + .setMinAspectRatio(1.1f) + .setUid(android.os.Process.myUid()) + .build(); + // Setup Letterbox Configuration + activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + activity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(1.5f); + // Non-resizable portrait activity + prepareUnresizable(activity, ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + float expectedAspectRatio = 1f * displayHeight / getExpectedSplitSize(displayWidth); + final Rect afterBounds = activity.getBounds(); + final float afterAspectRatio = (float) (afterBounds.width()) / afterBounds.height(); + Assert.assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f); + } + @Test public void testSplitAspectRatioForUnresizableLandscapeApps() { // Set up a display in portrait and ignoring orientation request.