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

Commit 9dd318ea authored by Eghosa Ewansiha-Vlachavas's avatar Eghosa Ewansiha-Vlachavas
Browse files

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
parent fda1f432
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -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 {
+9 −0
Original line number Diff line number Diff line
@@ -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
@@ -80,6 +84,11 @@ class AppCompatController {
        return mAppCompatAspectRatioPolicy;
    }

    @NonNull
    DesktopAppCompatAspectRatioPolicy getDesktopAppCompatAspectRatioPolicy() {
        return mDesktopAppCompatAspectRatioPolicy;
    }

    @NonNull
    AppCompatOverrides getAppCompatOverrides() {
        return mAppCompatOverrides;
+294 −0
Original line number Diff line number Diff line
/*
 * 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;
    }
}
+16 −9
Original line number Diff line number Diff line
@@ -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) {
+5 −0
Original line number Diff line number Diff line
@@ -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();
Loading