Merge changes Ib0099b0f,I4c4de052 into main
* changes:
[1/n] Enforce Shell desktop cascading in Launch Params
[0/n] Create bug fix flag for Shell initial bounds regression
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index b8c20bd..cc37456 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -488,6 +488,8 @@
private static final String KEY_ALLOW_PASS_THROUGH_ON_TOUCH_OUTSIDE =
"android.activity.allowPassThroughOnTouchOutside";
+ private static final String KEY_FLEXIBLE_LAUNCH_SIZE = "android.activity.flexibleLaunchSize";
+
/**
* @see #setLaunchCookie
* @hide
@@ -588,6 +590,7 @@
@BackgroundActivityStartMode
private int mPendingIntentCreatorBackgroundActivityStartMode =
MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED;
+ private boolean mFlexibleLaunchSize = false;
private boolean mDisableStartingWindow;
private boolean mAllowPassThroughOnTouchOutside;
@@ -1451,6 +1454,7 @@
mPendingIntentCreatorBackgroundActivityStartMode = opts.getInt(
KEY_PENDING_INTENT_CREATOR_BACKGROUND_ACTIVITY_START_MODE,
MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED);
+ mFlexibleLaunchSize = opts.getBoolean(KEY_FLEXIBLE_LAUNCH_SIZE, /* defaultValue = */ false);
mDisableStartingWindow = opts.getBoolean(KEY_DISABLE_STARTING_WINDOW);
mAllowPassThroughOnTouchOutside = opts.getBoolean(KEY_ALLOW_PASS_THROUGH_ON_TOUCH_OUTSIDE);
mAnimationAbortListener = IRemoteCallback.Stub.asInterface(
@@ -2346,6 +2350,24 @@
}
/**
+ * Sets whether the size of the launch bounds is flexible, meaning it can be overridden to a
+ * different size during the launch params calculation.
+ * @hide
+ */
+ public ActivityOptions setFlexibleLaunchSize(boolean isFlexible) {
+ mFlexibleLaunchSize = isFlexible;
+ return this;
+ }
+
+ /**
+ * Gets whether the size of the launch bounds is flexible.
+ * @hide
+ */
+ public boolean getFlexibleLaunchSize() {
+ return mFlexibleLaunchSize;
+ }
+
+ /**
* Update the current values in this ActivityOptions from those supplied
* in <var>otherOptions</var>. Any values
* defined in <var>otherOptions</var> replace those in the base options.
@@ -2601,6 +2623,9 @@
b.putInt(KEY_PENDING_INTENT_CREATOR_BACKGROUND_ACTIVITY_START_MODE,
mPendingIntentCreatorBackgroundActivityStartMode);
}
+ if (mFlexibleLaunchSize) {
+ b.putBoolean(KEY_FLEXIBLE_LAUNCH_SIZE, mFlexibleLaunchSize);
+ }
if (mDisableStartingWindow) {
b.putBoolean(KEY_DISABLE_STARTING_WINDOW, mDisableStartingWindow);
}
diff --git a/core/java/android/window/DesktopModeFlags.java b/core/java/android/window/DesktopModeFlags.java
index 3cdf290..696b7b8 100644
--- a/core/java/android/window/DesktopModeFlags.java
+++ b/core/java/android/window/DesktopModeFlags.java
@@ -114,6 +114,8 @@
ENABLE_RESIZING_METRICS(Flags::enableResizingMetrics, true),
ENABLE_RESTORE_TO_PREVIOUS_SIZE_FROM_DESKTOP_IMMERSIVE(
Flags::enableRestoreToPreviousSizeFromDesktopImmersive, true),
+ ENABLE_SHELL_INITIAL_BOUNDS_REGRESSION_BUG_FIX(
+ Flags::enableShellInitialBoundsRegressionBugFix, false),
ENABLE_START_LAUNCH_TRANSITION_FROM_TASKBAR_BUGFIX(
Flags::enableStartLaunchTransitionFromTaskbarBugfix, true),
ENABLE_TASKBAR_OVERFLOW(Flags::enableTaskbarOverflow, false),
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index 355a87d..09f458b 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -38,6 +38,18 @@
}
flag {
+ name: "enable_shell_initial_bounds_regression_bug_fix"
+ namespace: "lse_desktop_experience"
+ description: "Enables fix for Shell initial bounds regression, forcing core to calculate /n"
+ "initial bounds in desktop launch params while respecting cascading position /n"
+ "passed by Shell."
+ bug: "396075922"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "enable_windowing_dynamic_initial_bounds"
namespace: "lse_desktop_experience"
description: "Enables new initial bounds for desktop windowing which adjust depending on app constraints"
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 7e63250..5de3be4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -1246,6 +1246,10 @@
pendingIntentBackgroundActivityStartMode =
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS
launchBounds = bounds
+ if (DesktopModeFlags.ENABLE_SHELL_INITIAL_BOUNDS_REGRESSION_BUG_FIX.isTrue) {
+ // Sets launch bounds size as flexible so core can recalculate.
+ flexibleLaunchSize = true
+ }
}
wct.sendPendingIntent(pendingIntent, intent, ops.toBundle())
diff --git a/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java b/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java
index 0106a64..7a959c1 100644
--- a/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java
+++ b/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java
@@ -52,6 +52,13 @@
.getInt("persist.wm.debug.desktop_mode_landscape_app_padding", 25);
/**
+ * Proportion of window height top offset with respect to bottom offset, used for central task
+ * positioning. Should be kept in sync with constant in
+ * {@link com.android.wm.shell.desktopmode.DesktopTaskPosition}
+ */
+ public static final float WINDOW_HEIGHT_PROPORTION = 0.375f;
+
+ /**
* Updates launch bounds for an activity with respect to its activity options, window layout,
* android manifest and task configuration.
*/
@@ -63,7 +70,16 @@
final Rect stableBounds = new Rect();
task.getDisplayArea().getStableRect(stableBounds);
- if (options != null && options.getLaunchBounds() != null) {
+ // If the options bounds size is flexible, update size with calculated desired size.
+ final boolean updateOptionBoundsSize = options != null
+ && options.getFlexibleLaunchSize();
+ // If cascading is also enabled, the position of the options bounds must be respected
+ // during the size update.
+ final boolean shouldRespectOptionPosition =
+ updateOptionBoundsSize && DesktopModeFlags.ENABLE_CASCADING_WINDOWS.isTrue();
+
+ if (options != null && options.getLaunchBounds() != null
+ && !updateOptionBoundsSize) {
outBounds.set(options.getLaunchBounds());
logger.accept("inherit-from-options=" + outBounds);
} else if (layout != null) {
@@ -76,26 +92,34 @@
stableBounds);
logger.accept("layout specifies sizes, inheriting size and applying gravity");
} else if (verticalGravity > 0 || horizontalGravity > 0) {
- outBounds.set(calculateInitialBounds(task, activity, stableBounds));
+ outBounds.set(calculateInitialBounds(task, activity, stableBounds, options,
+ shouldRespectOptionPosition));
applyLayoutGravity(verticalGravity, horizontalGravity, outBounds,
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));
+ outBounds.set(calculateInitialBounds(task, activity, stableBounds, options,
+ shouldRespectOptionPosition));
logger.accept("layout not specified, applying desired bounds");
+ logger.accept("respecting option bounds cascaded position="
+ + shouldRespectOptionPosition);
}
}
/**
* Calculates the initial bounds required for an application to fill a scale of the display
* bounds without any letterboxing. This is done by taking into account the applications
- * fullscreen size, aspect ratio, orientation and resizability to calculate an area this is
- * compatible with the applications previous configuration.
+ * fullscreen size, aspect ratio, orientation and resizability to calculate an area that is
+ * compatible with the applications previous configuration. These bounds are then cascaded to
+ * either the center or to a cascading position supplied by Shell via the option bounds.
*/
@NonNull
private static Rect calculateInitialBounds(@NonNull Task task,
- @NonNull ActivityRecord activity, @NonNull Rect stableBounds
+ @NonNull ActivityRecord activity, @NonNull Rect stableBounds,
+ @Nullable ActivityOptions options, boolean shouldRespectOptionPosition
) {
// Display bounds not taking into account insets.
final TaskDisplayArea displayArea = task.getDisplayArea();
@@ -172,6 +196,9 @@
}
default -> idealSize;
};
+ if (shouldRespectOptionPosition) {
+ return respectShellCascading(initialSize, stableBounds, options.getLaunchBounds());
+ }
return centerInScreen(initialSize, screenBounds);
}
@@ -266,14 +293,65 @@
* Adjusts bounds to be positioned in the middle of the screen.
*/
@NonNull
- private static Rect centerInScreen(@NonNull Size desiredSize,
+ static Rect centerInScreen(@NonNull Size desiredSize,
@NonNull Rect screenBounds) {
- // TODO(b/325240051): Position apps with bottom heavy offset
- final int heightOffset = (screenBounds.height() - desiredSize.getHeight()) / 2;
+ final int heightOffset = (int)
+ ((screenBounds.height() - desiredSize.getHeight()) * WINDOW_HEIGHT_PROPORTION);
final int widthOffset = (screenBounds.width() - desiredSize.getWidth()) / 2;
final Rect resultBounds = new Rect(0, 0,
desiredSize.getWidth(), desiredSize.getHeight());
resultBounds.offset(screenBounds.left + widthOffset, screenBounds.top + heightOffset);
return resultBounds;
}
+
+ /**
+ * Calculates final initial bounds based on the task's desired size and the cascading anchor
+ * point extracted from the option bounds. Cascading behaviour should be kept in sync with
+ * logic in {@link com.android.wm.shell.desktopmode.DesktopTaskPosition}.
+ */
+ @NonNull
+ private static Rect respectShellCascading(@NonNull Size desiredSize, @NonNull Rect stableBounds,
+ @NonNull Rect optionBounds) {
+ final boolean leftAligned = stableBounds.left == optionBounds.left;
+ final boolean rightAligned = stableBounds.right == optionBounds.right;
+ final boolean topAligned = stableBounds.top == optionBounds.top;
+ final boolean bottomAligned = stableBounds.bottom == optionBounds.bottom;
+ if (leftAligned && topAligned) {
+ // Bounds cascaded to top left, respect top left position anchor point.
+ return new Rect(
+ optionBounds.left,
+ optionBounds.top,
+ optionBounds.left + desiredSize.getWidth(),
+ optionBounds.top + desiredSize.getHeight()
+ );
+ }
+ if (leftAligned && bottomAligned) {
+ // Bounds cascaded to bottom left, respect bottom left position anchor point.
+ return new Rect(
+ optionBounds.left,
+ optionBounds.bottom - desiredSize.getHeight(),
+ optionBounds.left + desiredSize.getWidth(),
+ optionBounds.bottom
+ );
+ }
+ if (rightAligned && topAligned) {
+ // Bounds cascaded to top right, respect top right position anchor point.
+ return new Rect(
+ optionBounds.right - desiredSize.getWidth(),
+ optionBounds.top,
+ optionBounds.right,
+ optionBounds.top + desiredSize.getHeight()
+ );
+ }
+ if (rightAligned && bottomAligned) {
+ // Bounds cascaded to bottom right, respect bottom right position anchor point.
+ return new Rect(
+ optionBounds.right - desiredSize.getWidth(),
+ optionBounds.bottom - desiredSize.getHeight(),
+ optionBounds.right,
+ optionBounds.bottom);
+ }
+ // Bounds not cascaded to any corner, default to center position.
+ return centerInScreen(desiredSize, stableBounds);
+ }
}
diff --git a/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java b/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
index a4eeb68..d466a64 100644
--- a/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
@@ -42,6 +42,7 @@
private static final String TAG =
TAG_WITH_CLASS_NAME ? "DesktopModeLaunchParamsModifier" : TAG_ATM;
+
private static final boolean DEBUG = false;
private StringBuilder mLogBuilder;
@@ -133,6 +134,14 @@
DesktopModeBoundsCalculator.updateInitialBounds(task, layout, activity, options,
outParams.mBounds, 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
+ // applying further cascading. Since other modifiers are being skipped in this case,
+ // this modifier is now also responsible to respecting the options launch windowing
+ // mode.
+ outParams.mWindowingMode = options.getLaunchWindowingMode();
+ return RESULT_DONE;
+ }
return RESULT_CONTINUE;
}
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 3a06fff..bc37496 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
@@ -42,8 +42,10 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
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;
import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_DISPLAY;
import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE;
+import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_DONE;
import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_SKIP;
import static org.junit.Assert.assertEquals;
@@ -60,6 +62,7 @@
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
+import android.util.Size;
import android.view.Gravity;
import androidx.test.filters.SmallTest;
@@ -1099,6 +1102,110 @@
}
@Test
+ @EnableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+ Flags.FLAG_ENABLE_SHELL_INITIAL_BOUNDS_REGRESSION_BUG_FIX})
+ public void testOptionsBoundsSet_flexibleLaunchSize_windowingModeSet() {
+ setupDesktopModeLaunchParamsModifier();
+
+ final TestDisplayContent display = createNewDisplayContent(WINDOWING_MODE_FULLSCREEN);
+ final Task task = new TaskBuilder(mSupervisor).setActivityType(
+ ACTIVITY_TYPE_STANDARD).setDisplay(display).build();
+ final ActivityOptions options = ActivityOptions.makeBasic()
+ .setLaunchBounds(new Rect(
+ DISPLAY_STABLE_BOUNDS.left,
+ DISPLAY_STABLE_BOUNDS.top,
+ /* right = */ 500,
+ /* bottom = */ 500))
+ .setFlexibleLaunchSize(true);
+ options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM);
+
+ assertEquals(RESULT_DONE,
+ new CalculateRequestBuilder().setTask(task).setOptions(options).calculate());
+ assertEquals(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode);
+ }
+
+ @Test
+ @EnableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+ Flags.FLAG_ENABLE_SHELL_INITIAL_BOUNDS_REGRESSION_BUG_FIX})
+ public void testOptionsBoundsSet_flexibleLaunchSize_boundsSizeModified() {
+ setupDesktopModeLaunchParamsModifier();
+
+ final TestDisplayContent display = createNewDisplayContent(WINDOWING_MODE_FULLSCREEN);
+ final Task task = new TaskBuilder(mSupervisor).setActivityType(
+ ACTIVITY_TYPE_STANDARD).setDisplay(display).build();
+ final ActivityOptions options = ActivityOptions.makeBasic()
+ .setLaunchBounds(new Rect(
+ DISPLAY_STABLE_BOUNDS.left,
+ DISPLAY_STABLE_BOUNDS.top,
+ /* right = */ 500,
+ /* bottom = */ 500))
+ .setFlexibleLaunchSize(true);
+ final int modifiedWidth =
+ (int) (DISPLAY_BOUNDS.width() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
+ final int modifiedHeight =
+ (int) (DISPLAY_BOUNDS.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
+
+ assertEquals(RESULT_DONE,
+ new CalculateRequestBuilder().setTask(task).setOptions(options).calculate());
+ assertEquals(modifiedWidth, mResult.mBounds.width());
+ assertEquals(modifiedHeight, mResult.mBounds.height());
+ }
+
+ @Test
+ @EnableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+ Flags.FLAG_ENABLE_SHELL_INITIAL_BOUNDS_REGRESSION_BUG_FIX})
+ public void testOptionsBoundsSet_flexibleLaunchSizeWithCascading_cornerCascadeRespected() {
+ setupDesktopModeLaunchParamsModifier();
+
+ final TestDisplayContent display = createNewDisplayContent(WINDOWING_MODE_FULLSCREEN);
+ final Task task = new TaskBuilder(mSupervisor).setActivityType(
+ ACTIVITY_TYPE_STANDARD).setDisplay(display).build();
+ // Set launch bounds with corner cascade.
+ final ActivityOptions options = ActivityOptions.makeBasic()
+ .setLaunchBounds(new Rect(
+ DISPLAY_STABLE_BOUNDS.left,
+ DISPLAY_STABLE_BOUNDS.top,
+ /* right = */ 500,
+ /* bottom = */ 500))
+ .setFlexibleLaunchSize(true);
+
+ assertEquals(RESULT_DONE,
+ new CalculateRequestBuilder().setTask(task).setOptions(options).calculate());
+ assertEquals(DISPLAY_STABLE_BOUNDS.left, mResult.mBounds.left);
+ assertEquals(DISPLAY_STABLE_BOUNDS.top, mResult.mBounds.top);
+ }
+
+ @Test
+ @EnableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+ Flags.FLAG_ENABLE_SHELL_INITIAL_BOUNDS_REGRESSION_BUG_FIX})
+ public void testOptionsBoundsSet_flexibleLaunchSize_centerCascadeRespected() {
+ setupDesktopModeLaunchParamsModifier();
+
+ final TestDisplayContent display = createNewDisplayContent(WINDOWING_MODE_FULLSCREEN);
+ final Task task = new TaskBuilder(mSupervisor).setActivityType(
+ ACTIVITY_TYPE_STANDARD).setDisplay(display).build();
+ // Set launch bounds with center cascade.
+ final ActivityOptions options = ActivityOptions.makeBasic()
+ .setLaunchBounds(new Rect(
+ /* left = */ 320,
+ /* top = */ 100,
+ /* right = */ 640,
+ /* bottom = */ 200))
+ .setFlexibleLaunchSize(true);
+ final int modifiedWidth =
+ (int) (DISPLAY_BOUNDS.width() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
+ final int modifiedHeight =
+ (int) (DISPLAY_BOUNDS.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
+ final Rect centerCascadedBounds = centerInScreen(
+ new Size(modifiedWidth, modifiedHeight), DISPLAY_STABLE_BOUNDS);
+
+ assertEquals(RESULT_DONE,
+ new CalculateRequestBuilder().setTask(task).setOptions(options).calculate());
+ assertEquals(centerCascadedBounds, mResult.mBounds);
+ assertEquals(centerCascadedBounds.top, mResult.mBounds.top);
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
public void testNonEmptyLayoutBounds_CenterToDisplay() {
setupDesktopModeLaunchParamsModifier();