Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4f1e391a authored by Massimo Carli's avatar Massimo Carli
Browse files

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 <package-name>'. For the unit tests run 'atest WmTests:SizeCompatTests'.

Fixes: 208648326

Change-Id: I0232d6d98b10e546b840c08ffbf6e7be9c465965
Merged-In: I0232d6d98b10e546b840c08ffbf6e7be9c465965
parent e04531c8
Loading
Loading
Loading
Loading
+22 −29
Original line number Diff line number Diff line
@@ -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");
+28 −3
Original line number Diff line number Diff line
@@ -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;
    }

    /**
+4 −0
Original line number Diff line number Diff line
@@ -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 =
+4 −3
Original line number Diff line number Diff line
@@ -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));
+104 −2
Original line number Diff line number Diff line
@@ -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.