diff options
10 files changed, 192 insertions, 62 deletions
diff --git a/core/java/android/app/AppCompatTaskInfo.java b/core/java/android/app/AppCompatTaskInfo.java index ea4646aa9eb9..3fd9d8b26611 100644 --- a/core/java/android/app/AppCompatTaskInfo.java +++ b/core/java/android/app/AppCompatTaskInfo.java @@ -104,6 +104,8 @@ public class AppCompatTaskInfo implements Parcelable { public static final int FLAG_HAS_MIN_ASPECT_RATIO_OVERRIDE = FLAG_BASE << 9; /** Top activity flag for whether restart menu is shown due to display move. */ private static final int FLAG_ENABLE_RESTART_MENU_FOR_DISPLAY_MOVE = FLAG_BASE << 10; + /** Top activity flag for whether activity opted out of edge to edge. */ + public static final int FLAG_OPT_OUT_EDGE_TO_EDGE = FLAG_BASE << 11; @Retention(RetentionPolicy.SOURCE) @IntDef(flag = true, value = { @@ -118,7 +120,8 @@ public class AppCompatTaskInfo implements Parcelable { FLAG_FULLSCREEN_OVERRIDE_SYSTEM, FLAG_FULLSCREEN_OVERRIDE_USER, FLAG_HAS_MIN_ASPECT_RATIO_OVERRIDE, - FLAG_ENABLE_RESTART_MENU_FOR_DISPLAY_MOVE + FLAG_ENABLE_RESTART_MENU_FOR_DISPLAY_MOVE, + FLAG_OPT_OUT_EDGE_TO_EDGE }) public @interface TopActivityFlag {} @@ -132,7 +135,8 @@ public class AppCompatTaskInfo implements Parcelable { @TopActivityFlag private static final int FLAGS_ORGANIZER_INTERESTED = FLAG_IS_FROM_LETTERBOX_DOUBLE_TAP | FLAG_ELIGIBLE_FOR_USER_ASPECT_RATIO_BUTTON | FLAG_FULLSCREEN_OVERRIDE_SYSTEM - | FLAG_FULLSCREEN_OVERRIDE_USER | FLAG_HAS_MIN_ASPECT_RATIO_OVERRIDE; + | FLAG_FULLSCREEN_OVERRIDE_USER | FLAG_HAS_MIN_ASPECT_RATIO_OVERRIDE + | FLAG_OPT_OUT_EDGE_TO_EDGE; @TopActivityFlag private static final int FLAGS_COMPAT_UI_INTERESTED = FLAGS_ORGANIZER_INTERESTED @@ -347,6 +351,20 @@ public class AppCompatTaskInfo implements Parcelable { setTopActivityFlag(FLAG_HAS_MIN_ASPECT_RATIO_OVERRIDE, enable); } + /** + * Sets the top activity flag for whether the activity has opted out of edge to edge. + */ + public void setOptOutEdgeToEdge(boolean enable) { + setTopActivityFlag(FLAG_OPT_OUT_EDGE_TO_EDGE, enable); + } + + /** + * @return {@code true} if the top activity has opted out of edge to edge. + */ + public boolean hasOptOutEdgeToEdge() { + return isTopActivityFlagEnabled(FLAG_OPT_OUT_EDGE_TO_EDGE); + } + /** Clear all top activity flags and set to false. */ public void clearTopActivityFlags() { mTopActivityFlags = FLAG_UNDEFINED; diff --git a/core/java/com/android/internal/policy/DesktopModeCompatUtils.java b/core/java/com/android/internal/policy/DesktopModeCompatUtils.java new file mode 100644 index 000000000000..d7cfbdfed99c --- /dev/null +++ b/core/java/com/android/internal/policy/DesktopModeCompatUtils.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2025 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.internal.policy; + +import static android.content.pm.ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED; +import static android.content.pm.ActivityInfo.OVERRIDE_EXCLUDE_CAPTION_INSETS_FROM_APP_BOUNDS; + +import android.annotation.NonNull; +import android.content.pm.ActivityInfo; +import android.window.DesktopModeFlags; + +/** + * Utility functions for app compat in desktop windowing used by both window manager and System UI. + * @hide + */ +public final class DesktopModeCompatUtils { + + /** + * Whether the caption insets should be excluded from configuration for system to handle. + * The caller should ensure the activity is in or entering desktop view. + * + * <p> The treatment is enabled when all the of the following is true: + * <li> Any flags to forcibly consume caption insets are enabled. + * <li> Top activity have configuration coupled with insets. + * <li> Task is not resizeable or per-app override + * {@link ActivityInfo#OVERRIDE_EXCLUDE_CAPTION_INSETS_FROM_APP_BOUNDS} is enabled. + */ + public static boolean shouldExcludeCaptionFromAppBounds(@NonNull ActivityInfo info, + boolean isResizeable, boolean optOutEdgeToEdge) { + return DesktopModeFlags.EXCLUDE_CAPTION_FROM_APP_BOUNDS.isTrue() + && isAnyForceConsumptionFlagsEnabled() + && !isConfigurationDecoupled(info, optOutEdgeToEdge) + && (!isResizeable + || info.isChangeEnabled(OVERRIDE_EXCLUDE_CAPTION_INSETS_FROM_APP_BOUNDS)); + } + + private static boolean isConfigurationDecoupled(@NonNull ActivityInfo info, + boolean optOutEdgeToEdge) { + return info.isChangeEnabled(INSETS_DECOUPLED_CONFIGURATION_ENFORCED) && !optOutEdgeToEdge; + } + + private static boolean isAnyForceConsumptionFlagsEnabled() { + return DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS.isTrue() + || DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION.isTrue(); + } +} diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicy.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicy.kt index 9ea0532f9450..b87c2054bea6 100644 --- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicy.kt +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicy.kt @@ -19,13 +19,10 @@ package com.android.wm.shell.shared.desktopmode import android.Manifest.permission.SYSTEM_ALERT_WINDOW import android.app.TaskInfo import android.content.Context -import android.content.pm.ActivityInfo -import android.content.pm.ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED -import android.content.pm.ActivityInfo.OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION -import android.content.pm.ActivityInfo.OVERRIDE_EXCLUDE_CAPTION_INSETS_FROM_APP_BOUNDS import android.content.pm.PackageManager import android.window.DesktopModeFlags import com.android.internal.R +import com.android.internal.policy.DesktopModeCompatUtils /** * Class to decide whether to apply app compat policies in desktop mode. @@ -60,22 +57,11 @@ class DesktopModeCompatPolicy(private val context: Context) { hasFullscreenTransparentPermission(packageName))) && !isTopActivityNoDisplay) - /** - * Whether the caption insets should be excluded from configuration for system to handle. - * - * The treatment is enabled when all the of the following is true: - * * Any flags to forcibly consume caption insets are enabled. - * * Top activity have configuration coupled with insets. - * * Task is not resizeable or [ActivityInfo.OVERRIDE_EXCLUDE_CAPTION_INSETS_FROM_APP_BOUNDS] - * is enabled. - */ + /** @see DesktopModeCompatUtils.shouldExcludeCaptionFromAppBounds */ fun shouldExcludeCaptionFromAppBounds(taskInfo: TaskInfo): Boolean = - DesktopModeFlags.EXCLUDE_CAPTION_FROM_APP_BOUNDS.isTrue - && isAnyForceConsumptionFlagsEnabled() - && taskInfo.topActivityInfo?.let { - isInsetsCoupledWithConfiguration(it) && (!taskInfo.isResizeable || it.isChangeEnabled( - OVERRIDE_EXCLUDE_CAPTION_INSETS_FROM_APP_BOUNDS - )) + taskInfo.topActivityInfo?.let { + DesktopModeCompatUtils.shouldExcludeCaptionFromAppBounds(it, taskInfo.isResizeable, + taskInfo.appCompatTaskInfo.hasOptOutEdgeToEdge()) } ?: false /** @@ -118,12 +104,4 @@ class DesktopModeCompatPolicy(private val context: Context) { */ private fun isPartOfDefaultHomePackageOrNoHomeAvailable(packageName: String?) = defaultHomePackage == null || (packageName != null && packageName == defaultHomePackage) - - private fun isAnyForceConsumptionFlagsEnabled(): Boolean = - DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS.isTrue - || DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION.isTrue - - private fun isInsetsCoupledWithConfiguration(info: ActivityInfo): Boolean = - !(info.isChangeEnabled(OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION) - || info.isChangeEnabled(INSETS_DECOUPLED_CONFIGURATION_ENFORCED)) } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index e91d88901751..919b14b6db62 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -625,7 +625,7 @@ final class ActivityRecord extends WindowToken { @VisibleForTesting final TaskFragment.ConfigOverrideHint mResolveConfigHint; - private final boolean mOptOutEdgeToEdge; + final boolean mOptOutEdgeToEdge; private static ConstrainDisplayApisConfig sConstrainDisplayApisConfig; diff --git a/services/core/java/com/android/server/wm/AppCompatUtils.java b/services/core/java/com/android/server/wm/AppCompatUtils.java index b91a12598e01..80df081a6271 100644 --- a/services/core/java/com/android/server/wm/AppCompatUtils.java +++ b/services/core/java/com/android/server/wm/AppCompatUtils.java @@ -216,6 +216,7 @@ final class AppCompatUtils { AppCompatCameraPolicy.getCameraCompatFreeformMode(top); appCompatTaskInfo.setHasMinAspectRatioOverride(top.mAppCompatController .getDesktopAspectRatioPolicy().hasMinAspectRatioOverride(task)); + appCompatTaskInfo.setOptOutEdgeToEdge(top.mOptOutEdgeToEdge); } /** diff --git a/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java b/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java index 7a959c14fbd2..ce3ad889a308 100644 --- a/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java +++ b/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java @@ -24,6 +24,8 @@ import static android.content.pm.ActivityInfo.isFixedOrientationPortrait; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; +import static com.android.internal.policy.SystemBarUtils.getDesktopViewAppHeaderHeightPx; +import static com.android.internal.policy.DesktopModeCompatUtils.shouldExcludeCaptionFromAppBounds; import static com.android.server.wm.LaunchParamsUtil.applyLayoutGravity; import static com.android.server.wm.LaunchParamsUtil.calculateLayoutBounds; @@ -64,7 +66,8 @@ public final class DesktopModeBoundsCalculator { */ static void updateInitialBounds(@NonNull Task task, @Nullable WindowLayout layout, @Nullable ActivityRecord activity, @Nullable ActivityOptions options, - @NonNull Rect outBounds, @NonNull Consumer<String> logger) { + @NonNull LaunchParamsController.LaunchParams outParams, + @NonNull Consumer<String> logger) { // Use stable frame instead of raw frame to avoid launching freeform windows on top of // stable insets, which usually are system widgets such as sysbar & navbar. final Rect stableBounds = new Rect(); @@ -77,36 +80,44 @@ public final class DesktopModeBoundsCalculator { // during the size update. final boolean shouldRespectOptionPosition = updateOptionBoundsSize && DesktopModeFlags.ENABLE_CASCADING_WINDOWS.isTrue(); + final int captionHeight = activity != null && shouldExcludeCaptionFromAppBounds( + activity.info, task.isResizeable(), activity.mOptOutEdgeToEdge) + ? getDesktopViewAppHeaderHeightPx(activity.mWmService.mContext) : 0; if (options != null && options.getLaunchBounds() != null && !updateOptionBoundsSize) { - outBounds.set(options.getLaunchBounds()); - logger.accept("inherit-from-options=" + outBounds); + outParams.mBounds.set(options.getLaunchBounds()); + logger.accept("inherit-from-options=" + outParams.mBounds); } else if (layout != null) { final int verticalGravity = layout.gravity & Gravity.VERTICAL_GRAVITY_MASK; final int horizontalGravity = layout.gravity & Gravity.HORIZONTAL_GRAVITY_MASK; if (layout.hasSpecifiedSize()) { - calculateLayoutBounds(stableBounds, layout, outBounds, + calculateLayoutBounds(stableBounds, layout, outParams.mBounds, calculateIdealSize(stableBounds, DESKTOP_MODE_INITIAL_BOUNDS_SCALE)); - applyLayoutGravity(verticalGravity, horizontalGravity, outBounds, + applyLayoutGravity(verticalGravity, horizontalGravity, outParams.mBounds, stableBounds); logger.accept("layout specifies sizes, inheriting size and applying gravity"); } else if (verticalGravity > 0 || horizontalGravity > 0) { - outBounds.set(calculateInitialBounds(task, activity, stableBounds, options, - shouldRespectOptionPosition)); - applyLayoutGravity(verticalGravity, horizontalGravity, outBounds, + outParams.mBounds.set(calculateInitialBounds(task, activity, stableBounds, options, + shouldRespectOptionPosition, captionHeight)); + applyLayoutGravity(verticalGravity, horizontalGravity, outParams.mBounds, stableBounds); logger.accept("layout specifies gravity, applying desired bounds and gravity"); logger.accept("respecting option bounds cascaded position=" + shouldRespectOptionPosition); } } else { - outBounds.set(calculateInitialBounds(task, activity, stableBounds, options, - shouldRespectOptionPosition)); + outParams.mBounds.set(calculateInitialBounds(task, activity, stableBounds, options, + shouldRespectOptionPosition, captionHeight)); logger.accept("layout not specified, applying desired bounds"); logger.accept("respecting option bounds cascaded position=" + shouldRespectOptionPosition); } + if (updateOptionBoundsSize && captionHeight != 0) { + outParams.mAppBounds.set(outParams.mBounds); + outParams.mAppBounds.top += captionHeight; + logger.accept("excluding caption height from app bounds"); + } } /** @@ -119,7 +130,8 @@ public final class DesktopModeBoundsCalculator { @NonNull private static Rect calculateInitialBounds(@NonNull Task task, @NonNull ActivityRecord activity, @NonNull Rect stableBounds, - @Nullable ActivityOptions options, boolean shouldRespectOptionPosition + @Nullable ActivityOptions options, boolean shouldRespectOptionPosition, + int captionHeight ) { // Display bounds not taking into account insets. final TaskDisplayArea displayArea = task.getDisplayArea(); @@ -160,7 +172,8 @@ public final class DesktopModeBoundsCalculator { } // If activity is unresizeable, regardless of orientation, calculate maximum size // (within the ideal size) maintaining original aspect ratio. - yield maximizeSizeGivenAspectRatio(activityOrientation, idealSize, appAspectRatio); + yield maximizeSizeGivenAspectRatio(activityOrientation, idealSize, appAspectRatio, + captionHeight); } case ORIENTATION_PORTRAIT -> { // Device in portrait orientation. @@ -188,11 +201,12 @@ public final class DesktopModeBoundsCalculator { // ratio. yield maximizeSizeGivenAspectRatio(activityOrientation, new Size(customPortraitWidthForLandscapeApp, idealSize.getHeight()), - appAspectRatio); + appAspectRatio, captionHeight); } // For portrait unresizeable activities, calculate maximum size (within the ideal // size) maintaining original aspect ratio. - yield maximizeSizeGivenAspectRatio(activityOrientation, idealSize, appAspectRatio); + yield maximizeSizeGivenAspectRatio(activityOrientation, idealSize, appAspectRatio, + captionHeight); } default -> idealSize; }; @@ -232,13 +246,15 @@ public final class DesktopModeBoundsCalculator { * Calculates the largest size that can fit in a given area while maintaining a specific aspect * ratio. */ + // TODO(b/400617906): Merge duplicate initial bounds calculations to shared class. @NonNull private static Size maximizeSizeGivenAspectRatio( @ScreenOrientation int orientation, @NonNull Size targetArea, - float aspectRatio + float aspectRatio, + int captionHeight ) { - final int targetHeight = targetArea.getHeight(); + final int targetHeight = targetArea.getHeight() - captionHeight; final int targetWidth = targetArea.getWidth(); final int finalHeight; final int finalWidth; @@ -275,7 +291,7 @@ public final class DesktopModeBoundsCalculator { finalHeight = (int) (finalWidth / aspectRatio); } } - return new Size(finalWidth, finalHeight); + return new Size(finalWidth, finalHeight + captionHeight); } /** diff --git a/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java b/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java index ddcb5eccb1d8..6698d2ec7cc4 100644 --- a/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java +++ b/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java @@ -150,7 +150,7 @@ class DesktopModeLaunchParamsModifier implements LaunchParamsModifier { } DesktopModeBoundsCalculator.updateInitialBounds(task, layout, activity, options, - outParams.mBounds, this::appendLog); + outParams, this::appendLog); appendLog("final desktop mode task bounds set to %s", outParams.mBounds); if (options != null && options.getFlexibleLaunchSize()) { // Return result done to prevent other modifiers from respecting option bounds and diff --git a/services/core/java/com/android/server/wm/LaunchParamsController.java b/services/core/java/com/android/server/wm/LaunchParamsController.java index fa65bda7104d..8eec98cb5fcc 100644 --- a/services/core/java/com/android/server/wm/LaunchParamsController.java +++ b/services/core/java/com/android/server/wm/LaunchParamsController.java @@ -26,6 +26,7 @@ import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier. import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_SKIP; import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityOptions; import android.app.WindowConfiguration.WindowingMode; @@ -138,6 +139,10 @@ class LaunchParamsController { mService.deferWindowLayout(); try { if (task.getRootTask().inMultiWindowMode()) { + if (!mTmpParams.mAppBounds.isEmpty()) { + task.getRequestedOverrideConfiguration().windowConfiguration.setAppBounds( + mTmpParams.mAppBounds); + } task.setBounds(mTmpParams.mBounds); return true; } @@ -169,6 +174,9 @@ class LaunchParamsController { static class LaunchParams { /** The bounds within the parent container. */ final Rect mBounds = new Rect(); + /** The bounds within the parent container respecting insets. Usually empty. */ + @NonNull + final Rect mAppBounds = new Rect(); /** The display area the {@link Task} would prefer to be on. */ @Nullable @@ -181,6 +189,7 @@ class LaunchParamsController { /** Sets values back to default. {@link #isEmpty} will return {@code true} once called. */ void reset() { mBounds.setEmpty(); + mAppBounds.setEmpty(); mPreferredTaskDisplayArea = null; mWindowingMode = WINDOWING_MODE_UNDEFINED; } @@ -188,13 +197,14 @@ class LaunchParamsController { /** Copies the values set on the passed in {@link LaunchParams}. */ void set(LaunchParams params) { mBounds.set(params.mBounds); + mAppBounds.set(params.mAppBounds); mPreferredTaskDisplayArea = params.mPreferredTaskDisplayArea; mWindowingMode = params.mWindowingMode; } /** Returns {@code true} if no values have been explicitly set. */ boolean isEmpty() { - return mBounds.isEmpty() && mPreferredTaskDisplayArea == null + return mBounds.isEmpty() && mAppBounds.isEmpty() && mPreferredTaskDisplayArea == null && mWindowingMode == WINDOWING_MODE_UNDEFINED; } @@ -215,12 +225,14 @@ class LaunchParamsController { if (mPreferredTaskDisplayArea != that.mPreferredTaskDisplayArea) return false; if (mWindowingMode != that.mWindowingMode) return false; + if (!mAppBounds.equals(that.mAppBounds)) return false; return mBounds != null ? mBounds.equals(that.mBounds) : that.mBounds == null; } @Override public int hashCode() { int result = mBounds != null ? mBounds.hashCode() : 0; + result = 31 * result + mAppBounds.hashCode(); result = 31 * result + (mPreferredTaskDisplayArea != null ? mPreferredTaskDisplayArea.hashCode() : 0); result = 31 * result + mWindowingMode; diff --git a/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java index e87e107cd793..3e86f7b57294 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java @@ -41,6 +41,7 @@ import static android.util.DisplayMetrics.DENSITY_DEFAULT; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; +import static com.android.internal.policy.SystemBarUtils.getDesktopViewAppHeaderHeightPx; import static com.android.server.wm.DesktopModeBoundsCalculator.DESKTOP_MODE_INITIAL_BOUNDS_SCALE; import static com.android.server.wm.DesktopModeBoundsCalculator.DESKTOP_MODE_LANDSCAPE_APP_PADDING; import static com.android.server.wm.DesktopModeBoundsCalculator.centerInScreen; @@ -893,9 +894,11 @@ public class DesktopModeLaunchParamsModifierTests extends @Test @EnableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE, - Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS}) + Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS, + Flags.FLAG_EXCLUDE_CAPTION_FROM_APP_BOUNDS}) public void testDefaultLandscapeBounds_landscapeDevice_unResizable_landscapeOrientation() { setupDesktopModeLaunchParamsModifier(); + final int captionHeight = getDesktopViewAppHeaderHeightPx(mContext); final TestDisplayContent display = createDisplayContent(ORIENTATION_LANDSCAPE, LANDSCAPE_DISPLAY_BOUNDS); @@ -903,11 +906,11 @@ public class DesktopModeLaunchParamsModifierTests extends final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_LANDSCAPE, task, /* ignoreOrientationRequest */ true); - - final int desiredWidth = - (int) (LANDSCAPE_DISPLAY_BOUNDS.width() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE); + final float displayAspectRatio = (float) LANDSCAPE_DISPLAY_BOUNDS.width() + / LANDSCAPE_DISPLAY_BOUNDS.height(); final int desiredHeight = (int) (LANDSCAPE_DISPLAY_BOUNDS.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE); + final int desiredWidth = (int) ((desiredHeight - captionHeight) * displayAspectRatio); assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task) .setActivity(activity).calculate()); @@ -916,7 +919,8 @@ public class DesktopModeLaunchParamsModifierTests extends } @Test - @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS) + @EnableFlags({Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS, + Flags.FLAG_EXCLUDE_CAPTION_FROM_APP_BOUNDS}) public void testUnResizablePortraitBounds_landscapeDevice_unResizable_portraitOrientation() { setupDesktopModeLaunchParamsModifier(); @@ -925,6 +929,7 @@ public class DesktopModeLaunchParamsModifierTests extends final Task task = createTask(display, /* isResizeable */ false); final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_PORTRAIT, task, /* ignoreOrientationRequest */ true); + final int captionHeight = getDesktopViewAppHeaderHeightPx(mContext); spyOn(activity.mAppCompatController.getDesktopAspectRatioPolicy()); doReturn(LETTERBOX_ASPECT_RATIO).when(activity.mAppCompatController @@ -932,7 +937,7 @@ public class DesktopModeLaunchParamsModifierTests extends final int desiredHeight = (int) (LANDSCAPE_DISPLAY_BOUNDS.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE); - final int desiredWidth = (int) (desiredHeight / LETTERBOX_ASPECT_RATIO); + final int desiredWidth = (int) ((desiredHeight - captionHeight) / LETTERBOX_ASPECT_RATIO); assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task) .setActivity(activity).calculate()); @@ -1070,7 +1075,8 @@ public class DesktopModeLaunchParamsModifierTests extends @Test @EnableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE, - Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS}) + Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS, + Flags.FLAG_EXCLUDE_CAPTION_FROM_APP_BOUNDS}) public void testDefaultPortraitBounds_portraitDevice_unResizable_portraitOrientation() { setupDesktopModeLaunchParamsModifier(); @@ -1079,12 +1085,14 @@ public class DesktopModeLaunchParamsModifierTests extends final Task task = createTask(display, /* isResizeable */ false); final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_PORTRAIT, task, /* ignoreOrientationRequest */ true); + final int captionHeight = getDesktopViewAppHeaderHeightPx(mContext); - - final int desiredWidth = - (int) (PORTRAIT_DISPLAY_BOUNDS.width() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE); + final float displayAspectRatio = (float) PORTRAIT_DISPLAY_BOUNDS.height() + / PORTRAIT_DISPLAY_BOUNDS.width(); final int desiredHeight = - (int) (PORTRAIT_DISPLAY_BOUNDS.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE); + (int) (PORTRAIT_DISPLAY_BOUNDS.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE); + final int desiredWidth = + (int) ((desiredHeight - captionHeight) / displayAspectRatio); assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task) .setActivity(activity).calculate()); @@ -1093,7 +1101,8 @@ public class DesktopModeLaunchParamsModifierTests extends } @Test - @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS) + @EnableFlags({Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS, + Flags.FLAG_EXCLUDE_CAPTION_FROM_APP_BOUNDS}) public void testUnResizableLandscapeBounds_portraitDevice_unResizable_landscapeOrientation() { setupDesktopModeLaunchParamsModifier(); @@ -1102,6 +1111,7 @@ public class DesktopModeLaunchParamsModifierTests extends final Task task = createTask(display, /* isResizeable */ false); final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_LANDSCAPE, task, /* ignoreOrientationRequest */ true); + final int captionHeight = getDesktopViewAppHeaderHeightPx(mContext); spyOn(activity.mAppCompatController.getDesktopAspectRatioPolicy()); doReturn(LETTERBOX_ASPECT_RATIO).when(activity.mAppCompatController @@ -1109,7 +1119,7 @@ public class DesktopModeLaunchParamsModifierTests extends final int desiredWidth = PORTRAIT_DISPLAY_BOUNDS.width() - (DESKTOP_MODE_LANDSCAPE_APP_PADDING * 2); - final int desiredHeight = (int) (desiredWidth / LETTERBOX_ASPECT_RATIO); + final int desiredHeight = (int) (desiredWidth / LETTERBOX_ASPECT_RATIO) + captionHeight; assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task) .setActivity(activity).calculate()); diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java index 67a95de8a5c1..0d5828a3c4e1 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java @@ -44,6 +44,7 @@ import android.app.ActivityOptions; import android.content.ComponentName; import android.content.pm.ActivityInfo.WindowLayout; import android.graphics.Rect; +import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.Presubmit; import android.util.ArrayMap; import android.util.SparseArray; @@ -52,6 +53,7 @@ import androidx.test.filters.MediumTest; import com.android.server.wm.LaunchParamsController.LaunchParams; import com.android.server.wm.LaunchParamsController.LaunchParamsModifier; +import com.android.window.flags.Flags; import org.junit.Before; import org.junit.Test; @@ -372,6 +374,34 @@ public class LaunchParamsControllerTests extends WindowTestsBase { assertEquals(expected, task.mLastNonFullscreenBounds); } + /** + * Ensures that app bounds are set to exclude freeform caption if window is in freeform. + */ + @Test + @EnableFlags(Flags.FLAG_EXCLUDE_CAPTION_FROM_APP_BOUNDS) + public void testLayoutTaskBoundsFreeformAppBounds() { + final Rect expected = new Rect(10, 20, 30, 40); + + final LaunchParams params = new LaunchParams(); + params.mBounds.set(expected); + params.mAppBounds.set(expected); + final InstrumentedPositioner positioner = new InstrumentedPositioner(RESULT_DONE, params); + final Task task = new TaskBuilder(mAtm.mTaskSupervisor) + .setWindowingMode(WINDOWING_MODE_FREEFORM).build(); + final ActivityOptions options = ActivityOptions.makeBasic().setFlexibleLaunchSize(true); + + mController.registerModifier(positioner); + + assertNotEquals(expected, task.getBounds()); + + layoutTask(task, options); + + // Task will make adjustments to requested bounds. We only need to guarantee that the + // requested bounds are expected. + assertEquals(expected, + task.getRequestedOverrideConfiguration().windowConfiguration.getAppBounds()); + } + public static class InstrumentedPositioner implements LaunchParamsModifier { private final int mReturnVal; @@ -473,4 +503,9 @@ public class LaunchParamsControllerTests extends WindowTestsBase { mController.layoutTask(task, null /* layout */, null /* activity */, null /* source */, null /* options */); } + + private void layoutTask(@NonNull Task task, ActivityOptions options) { + mController.layoutTask(task, null /* layout */, null /* activity */, null /* source */, + options /* options */); + } } |