summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicy.kt28
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt41
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt30
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java52
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FixedAspectRatioTaskPositionerDecorator.kt6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java30
-rw-r--r--libs/WindowManager/Shell/tests/unittest/Android.bp1
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt60
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicyTest.kt54
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java108
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java48
-rw-r--r--services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java9
14 files changed, 393 insertions, 79 deletions
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 835456b2868e..f234ff5c2c84 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
@@ -18,9 +18,12 @@ package com.android.wm.shell.shared.desktopmode
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.window.DesktopModeFlags
import com.android.internal.R
-import java.util.ArrayList
+import com.android.window.flags.Flags
/**
* Class to decide whether to apply app compat policies in desktop mode.
@@ -51,6 +54,21 @@ class DesktopModeCompatPolicy(private val context: Context) {
&& !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.
+ */
+ fun shouldExcludeCaptionFromAppBounds(taskInfo: TaskInfo): Boolean =
+ Flags.excludeCaptionFromAppBounds()
+ && isAnyForceConsumptionFlagsEnabled()
+ && taskInfo.topActivityInfo?.let {
+ isInsetsCoupledWithConfiguration(it) && !taskInfo.isResizeable
+ } ?: false
+
+ /**
* Returns true if all activities in a tasks stack are transparent. If there are no activities
* will return false.
*/
@@ -67,4 +85,12 @@ class DesktopModeCompatPolicy(private val context: Context) {
*/
private fun isPartOfDefaultHomePackage(packageName: String?) =
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/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
index 90191345147c..9b850de6fede 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
@@ -18,8 +18,10 @@
package com.android.wm.shell.desktopmode
+import android.annotation.DimenRes
import android.app.ActivityManager.RunningTaskInfo
import android.app.TaskInfo
+import android.content.Context
import android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
import android.content.pm.ActivityInfo.isFixedOrientationLandscape
import android.content.pm.ActivityInfo.isFixedOrientationPortrait
@@ -28,6 +30,7 @@ import android.content.res.Configuration.ORIENTATION_PORTRAIT
import android.graphics.Rect
import android.os.SystemProperties
import android.util.Size
+import com.android.wm.shell.R
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayLayout
@@ -53,10 +56,12 @@ fun calculateDefaultDesktopTaskBounds(displayLayout: DisplayLayout): Rect {
* aspect ratio, orientation and resizability to calculate an area this is compatible with the
* applications previous configuration.
*/
+@JvmOverloads
fun calculateInitialBounds(
displayLayout: DisplayLayout,
taskInfo: RunningTaskInfo,
scale: Float = DESKTOP_MODE_INITIAL_BOUNDS_SCALE,
+ captionInsets: Int = 0,
): Rect {
val screenBounds = Rect(0, 0, displayLayout.width(), displayLayout.height())
val appAspectRatio = calculateAspectRatio(taskInfo)
@@ -92,7 +97,7 @@ fun calculateInitialBounds(
} else {
// If activity is unresizeable, regardless of orientation, calculate maximum
// size (within the ideal size) maintaining original aspect ratio.
- maximizeSizeGivenAspectRatio(taskInfo, idealSize, appAspectRatio)
+ maximizeSizeGivenAspectRatio(taskInfo, idealSize, appAspectRatio, captionInsets)
}
}
ORIENTATION_PORTRAIT -> {
@@ -119,11 +124,17 @@ fun calculateInitialBounds(
taskInfo,
Size(customPortraitWidthForLandscapeApp, idealSize.height),
appAspectRatio,
+ captionInsets,
)
} else {
// For portrait unresizeable activities, calculate maximum size (within the
// ideal size) maintaining original aspect ratio.
- maximizeSizeGivenAspectRatio(taskInfo, idealSize, appAspectRatio)
+ maximizeSizeGivenAspectRatio(
+ taskInfo,
+ idealSize,
+ appAspectRatio,
+ captionInsets,
+ )
}
}
}
@@ -148,11 +159,16 @@ fun calculateMaximizeBounds(displayLayout: DisplayLayout, taskInfo: RunningTaskI
} else {
// if non-resizable then calculate max bounds according to aspect ratio
val activityAspectRatio = calculateAspectRatio(taskInfo)
+ val captionInsets =
+ taskInfo.configuration.windowConfiguration.appBounds?.let {
+ it.top - taskInfo.configuration.windowConfiguration.bounds.top
+ } ?: 0
val newSize =
maximizeSizeGivenAspectRatio(
taskInfo,
Size(stableBounds.width(), stableBounds.height()),
activityAspectRatio,
+ captionInsets,
)
return centerInArea(newSize, stableBounds, stableBounds.left, stableBounds.top)
}
@@ -166,8 +182,9 @@ fun maximizeSizeGivenAspectRatio(
taskInfo: RunningTaskInfo,
targetArea: Size,
aspectRatio: Float,
+ captionInsets: Int = 0,
): Size {
- val targetHeight = targetArea.height
+ val targetHeight = targetArea.height - captionInsets
val targetWidth = targetArea.width
val finalHeight: Int
val finalWidth: Int
@@ -191,13 +208,18 @@ fun maximizeSizeGivenAspectRatio(
finalHeight = (finalWidth / aspectRatio).toInt()
}
}
- return Size(finalWidth, finalHeight)
+ return Size(finalWidth, finalHeight + captionInsets)
}
/** Calculates the aspect ratio of an activity from its fullscreen bounds. */
fun calculateAspectRatio(taskInfo: RunningTaskInfo): Float {
- if (taskInfo.appCompatTaskInfo.topActivityAppBounds.isEmpty) return 1f
- val appBounds = taskInfo.appCompatTaskInfo.topActivityAppBounds
+ val appBounds =
+ if (taskInfo.appCompatTaskInfo.topActivityAppBounds.isEmpty) {
+ taskInfo.configuration.windowConfiguration.appBounds
+ ?: taskInfo.configuration.windowConfiguration.bounds
+ } else {
+ taskInfo.appCompatTaskInfo.topActivityAppBounds
+ }
return maxOf(appBounds.height(), appBounds.width()) /
minOf(appBounds.height(), appBounds.width()).toFloat()
}
@@ -233,6 +255,13 @@ fun isTaskBoundsEqual(taskBounds: Rect, stableBounds: Rect): Boolean {
return taskBounds == stableBounds
}
+/** Returns the app header height in desktop mode in pixels. */
+fun getAppHeaderHeight(context: Context): Int =
+ context.resources.getDimensionPixelSize(getAppHeaderHeightId())
+
+/** Returns the resource id of the app header height in desktop mode. */
+@DimenRes fun getAppHeaderHeightId(): Int = R.dimen.desktop_mode_freeform_decor_caption_height
+
/**
* Calculates the desired initial bounds for applications in desktop windowing. This is done as a
* scale of the screen bounds.
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 7b0fb1d89557..df1335109e8c 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
@@ -41,7 +41,6 @@ import android.os.Handler
import android.os.IBinder
import android.os.SystemProperties
import android.os.UserHandle
-import android.util.Size
import android.view.Display.DEFAULT_DISPLAY
import android.view.DragEvent
import android.view.MotionEvent
@@ -1175,7 +1174,6 @@ class DesktopTasksController(
// and toggle to the stable bounds.
desktopTilingDecorViewModel.removeTaskIfTiled(taskInfo.displayId, taskInfo.taskId)
taskRepository.saveBoundsBeforeMaximize(taskInfo.taskId, currentTaskBounds)
-
destinationBounds.set(calculateMaximizeBounds(displayLayout, taskInfo))
}
@@ -1240,23 +1238,6 @@ class DesktopTasksController(
)
}
- private fun getMaximizeBounds(taskInfo: RunningTaskInfo, stableBounds: Rect): Rect {
- if (taskInfo.isResizeable) {
- // if resizable then expand to entire stable bounds (full display minus insets)
- return Rect(stableBounds)
- } else {
- // if non-resizable then calculate max bounds according to aspect ratio
- val activityAspectRatio = calculateAspectRatio(taskInfo)
- val newSize =
- maximizeSizeGivenAspectRatio(
- taskInfo,
- Size(stableBounds.width(), stableBounds.height()),
- activityAspectRatio,
- )
- return centerInArea(newSize, stableBounds, stableBounds.left, stableBounds.top)
- }
- }
-
private fun isMaximizedToStableBoundsEdges(
taskInfo: RunningTaskInfo,
stableBounds: Rect,
@@ -2337,7 +2318,16 @@ class DesktopTasksController(
): Rect {
val bounds =
if (ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isTrue) {
- calculateInitialBounds(displayLayout, taskInfo)
+ // If caption insets should be excluded from app bounds, ensure caption insets
+ // are excluded from the ideal initial bounds when scaling non-resizeable apps.
+ // Caption insets stay fixed and don't scale with bounds.
+ val captionInsets =
+ if (desktopModeCompatPolicy.shouldExcludeCaptionFromAppBounds(taskInfo)) {
+ getAppHeaderHeight(context)
+ } else {
+ 0
+ }
+ calculateInitialBounds(displayLayout, taskInfo, captionInsets = captionInsets)
} else {
calculateDefaultDesktopTaskBounds(displayLayout)
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index dd1edeafde5d..2d1cd260d903 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -1711,7 +1711,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
mWindowDecorViewHostSupplier,
mMultiInstanceHelper,
mWindowDecorCaptionHandleRepository,
- mDesktopModeEventLogger);
+ mDesktopModeEventLogger,
+ mDesktopModeCompatPolicy);
mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);
final TaskPositioner taskPositioner = mTaskPositionerFactory.create(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index 2232e6865b00..4f83f9f02c21 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -94,6 +94,7 @@ import com.android.wm.shell.desktopmode.DesktopUserRepositories;
import com.android.wm.shell.desktopmode.WindowDecorCaptionHandleRepository;
import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
import com.android.wm.shell.shared.annotations.ShellMainThread;
+import com.android.wm.shell.shared.desktopmode.DesktopModeCompatPolicy;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource;
import com.android.wm.shell.shared.multiinstance.ManageWindowsViewContainer;
@@ -111,14 +112,14 @@ import kotlin.Unit;
import kotlin.jvm.functions.Function0;
import kotlin.jvm.functions.Function1;
+import kotlinx.coroutines.CoroutineScope;
+import kotlinx.coroutines.MainCoroutineDispatcher;
+
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Supplier;
-import kotlinx.coroutines.CoroutineScope;
-import kotlinx.coroutines.MainCoroutineDispatcher;
-
/**
* Defines visuals and behaviors of a window decoration of a caption bar and shadows. It works with
* {@link DesktopModeWindowDecorViewModel}.
@@ -191,6 +192,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
private final HandleMenuFactory mHandleMenuFactory;
private final AppToWebGenericLinksParser mGenericLinksParser;
private final AssistContentRequester mAssistContentRequester;
+ private final DesktopModeCompatPolicy mDesktopModeCompatPolicy;
// Hover state for the maximize menu and button. The menu will remain open as long as either of
// these is true. See {@link #onMaximizeHoverStateChanged()}.
@@ -232,7 +234,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
@NonNull WindowDecorViewHostSupplier<WindowDecorViewHost> windowDecorViewHostSupplier,
MultiInstanceHelper multiInstanceHelper,
WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository,
- DesktopModeEventLogger desktopModeEventLogger) {
+ DesktopModeEventLogger desktopModeEventLogger,
+ DesktopModeCompatPolicy desktopModeCompatPolicy) {
this (context, userContext, displayController, taskResourceLoader, splitScreenController,
desktopUserRepositories, taskOrganizer, taskInfo, taskSurface, handler,
mainExecutor, mainDispatcher, bgScope, bgExecutor, choreographer, syncQueue,
@@ -245,7 +248,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
windowDecorViewHostSupplier,
DefaultMaximizeMenuFactory.INSTANCE,
DefaultHandleMenuFactory.INSTANCE, multiInstanceHelper,
- windowDecorCaptionHandleRepository, desktopModeEventLogger);
+ windowDecorCaptionHandleRepository, desktopModeEventLogger,
+ desktopModeCompatPolicy);
}
DesktopModeWindowDecoration(
@@ -280,7 +284,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
HandleMenuFactory handleMenuFactory,
MultiInstanceHelper multiInstanceHelper,
WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository,
- DesktopModeEventLogger desktopModeEventLogger) {
+ DesktopModeEventLogger desktopModeEventLogger,
+ DesktopModeCompatPolicy desktopModeCompatPolicy) {
super(context, userContext, displayController, taskOrganizer, taskInfo,
taskSurface, surfaceControlBuilderSupplier, surfaceControlTransactionSupplier,
windowContainerTransactionSupplier, surfaceControlSupplier,
@@ -305,6 +310,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
mDesktopUserRepositories = desktopUserRepositories;
mTaskResourceLoader = taskResourceLoader;
mTaskResourceLoader.onWindowDecorCreated(taskInfo);
+ mDesktopModeCompatPolicy = desktopModeCompatPolicy;
}
/**
@@ -507,7 +513,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
applyStartTransactionOnDraw, shouldSetTaskVisibilityPositionAndCrop,
mIsStatusBarVisible, mIsKeyguardVisibleAndOccluded, inFullImmersive,
mDisplayController.getInsetsState(taskInfo.displayId), hasGlobalFocus,
- displayExclusionRegion, mIsRecentsTransitionRunning);
+ displayExclusionRegion, mIsRecentsTransitionRunning,
+ mDesktopModeCompatPolicy.shouldExcludeCaptionFromAppBounds(taskInfo));
final WindowDecorLinearLayout oldRootView = mResult.mRootView;
final SurfaceControl oldDecorationSurface = mDecorationContainerSurface;
@@ -888,7 +895,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
@NonNull InsetsState displayInsetsState,
boolean hasGlobalFocus,
@NonNull Region displayExclusionRegion,
- boolean shouldIgnoreCornerRadius) {
+ boolean shouldIgnoreCornerRadius,
+ boolean shouldExcludeCaptionFromAppBounds) {
final int captionLayoutId = getDesktopModeWindowDecorLayoutId(taskInfo.getWindowingMode());
final boolean isAppHeader =
captionLayoutId == R.layout.desktop_mode_app_header;
@@ -948,15 +956,23 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
}
} else {
if (ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION.isTrue()) {
- // Force-consume the caption bar insets when the app tries to hide the caption.
- // This improves app compatibility of immersive apps.
- relayoutParams.mInsetSourceFlags |= FLAG_FORCE_CONSUMING;
+ if (shouldExcludeCaptionFromAppBounds) {
+ relayoutParams.mShouldSetAppBounds = true;
+ } else {
+ // Force-consume the caption bar insets when the app tries to hide the
+ // caption. This improves app compatibility of immersive apps.
+ relayoutParams.mInsetSourceFlags |= FLAG_FORCE_CONSUMING;
+ }
}
}
if (ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS.isTrue()) {
- // Always force-consume the caption bar insets for maximum app compatibility,
- // including non-immersive apps that just don't handle caption insets properly.
- relayoutParams.mInsetSourceFlags |= FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR;
+ if (shouldExcludeCaptionFromAppBounds) {
+ relayoutParams.mShouldSetAppBounds = true;
+ } else {
+ // Always force-consume the caption bar insets for maximum app compatibility,
+ // including non-immersive apps that just don't handle caption insets properly.
+ relayoutParams.mInsetSourceFlags |= FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR;
+ }
}
if (Flags.enableFullyImmersiveInDesktop() && inFullImmersiveMode) {
final Insets systemBarInsets = displayInsetsState.calculateInsets(
@@ -1739,7 +1755,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
private static int getCaptionHeightIdStatic(@WindowingMode int windowingMode) {
return windowingMode == WINDOWING_MODE_FULLSCREEN
? com.android.internal.R.dimen.status_bar_height_default
- : R.dimen.desktop_mode_freeform_decor_caption_height;
+ : DesktopModeUtils.getAppHeaderHeightId();
}
private int getCaptionHeight(@WindowingMode int windowingMode) {
@@ -1837,7 +1853,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
windowDecorViewHostSupplier,
MultiInstanceHelper multiInstanceHelper,
WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository,
- DesktopModeEventLogger desktopModeEventLogger) {
+ DesktopModeEventLogger desktopModeEventLogger,
+ DesktopModeCompatPolicy desktopModeCompatPolicy) {
return new DesktopModeWindowDecoration(
context,
userContext,
@@ -1862,7 +1879,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
windowDecorViewHostSupplier,
multiInstanceHelper,
windowDecorCaptionHandleRepository,
- desktopModeEventLogger);
+ desktopModeEventLogger,
+ desktopModeCompatPolicy);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FixedAspectRatioTaskPositionerDecorator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FixedAspectRatioTaskPositionerDecorator.kt
index ab30d617af54..cfd068860589 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FixedAspectRatioTaskPositionerDecorator.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FixedAspectRatioTaskPositionerDecorator.kt
@@ -20,6 +20,7 @@ import android.app.ActivityManager.RunningTaskInfo
import android.graphics.PointF
import android.graphics.Rect
import com.android.internal.annotations.VisibleForTesting
+import com.android.wm.shell.desktopmode.calculateAspectRatio
import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_BOTTOM
import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_LEFT
import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_RIGHT
@@ -27,8 +28,6 @@ import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_TOP
import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_UNDEFINED
import com.android.wm.shell.windowdecor.DragPositioningCallback.CtrlType
import kotlin.math.abs
-import kotlin.math.max
-import kotlin.math.min
/**
* [AbstractTaskPositionerDecorator] implementation for validating the coordinates associated with a
@@ -59,8 +58,7 @@ class FixedAspectRatioTaskPositionerDecorator (
lastValidPoint.set(x, y)
val startingBoundWidth = lastRepositionedBounds.width()
val startingBoundHeight = lastRepositionedBounds.height()
- startingAspectRatio = max(startingBoundWidth, startingBoundHeight).toFloat() /
- min(startingBoundWidth, startingBoundHeight).toFloat()
+ startingAspectRatio = calculateAspectRatio(windowDecoration.mTaskInfo)
isTaskPortrait = startingBoundWidth <= startingBoundHeight
lastRepositionedBounds.set(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index 3fcb09349033..6033b505f772 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -470,8 +470,8 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
}
final WindowDecorationInsets newInsets = new WindowDecorationInsets(
- mTaskInfo.token, mOwner, captionInsetsRect, boundingRects,
- params.mInsetSourceFlags, params.mIsInsetSource);
+ mTaskInfo.token, mOwner, captionInsetsRect, taskBounds, boundingRects,
+ params.mInsetSourceFlags, params.mIsInsetSource, params.mShouldSetAppBounds);
if (!newInsets.equals(mWindowDecorationInsets)) {
// Add or update this caption as an insets source.
mWindowDecorationInsets = newInsets;
@@ -793,8 +793,8 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
final int captionHeight = loadDimensionPixelSize(mContext.getResources(), captionHeightId);
final Rect captionInsets = new Rect(0, 0, 0, captionHeight);
final WindowDecorationInsets newInsets = new WindowDecorationInsets(mTaskInfo.token,
- mOwner, captionInsets, null /* boundingRets */, 0 /* flags */,
- true /* shouldAddCaptionInset */);
+ mOwner, captionInsets, null /* taskFrame */, null /* boundingRects */,
+ 0 /* flags */, true /* shouldAddCaptionInset */, false /* excludedFromAppBounds */);
if (!newInsets.equals(mWindowDecorationInsets)) {
mWindowDecorationInsets = newInsets;
mWindowDecorationInsets.update(wct);
@@ -825,6 +825,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
boolean mApplyStartTransactionOnDraw;
boolean mSetTaskVisibilityPositionAndCrop;
boolean mHasGlobalFocus;
+ boolean mShouldSetAppBounds;
void reset() {
mLayoutResId = Resources.ID_NULL;
@@ -848,6 +849,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
mWindowDecorConfig = null;
mAsyncViewHost = false;
mHasGlobalFocus = false;
+ mShouldSetAppBounds = false;
}
boolean hasInputFeatureSpy() {
@@ -921,19 +923,23 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
private final WindowContainerToken mToken;
private final Binder mOwner;
private final Rect mFrame;
+ private final Rect mTaskFrame;
private final Rect[] mBoundingRects;
private final @InsetsSource.Flags int mFlags;
private final boolean mShouldAddCaptionInset;
+ private final boolean mExcludedFromAppBounds;
private WindowDecorationInsets(WindowContainerToken token, Binder owner, Rect frame,
- Rect[] boundingRects, @InsetsSource.Flags int flags,
- boolean shouldAddCaptionInset) {
+ Rect taskFrame, Rect[] boundingRects, @InsetsSource.Flags int flags,
+ boolean shouldAddCaptionInset, boolean excludedFromAppBounds) {
mToken = token;
mOwner = owner;
mFrame = frame;
+ mTaskFrame = taskFrame;
mBoundingRects = boundingRects;
mFlags = flags;
mShouldAddCaptionInset = shouldAddCaptionInset;
+ mExcludedFromAppBounds = excludedFromAppBounds;
}
void update(WindowContainerTransaction wct) {
@@ -942,12 +948,20 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
mFlags);
wct.addInsetsSource(mToken, mOwner, INDEX, mandatorySystemGestures(), mFrame,
mBoundingRects, 0 /* flags */);
+ if (mExcludedFromAppBounds) {
+ final Rect appBounds = new Rect(mTaskFrame);
+ appBounds.top += mFrame.height();
+ wct.setAppBounds(mToken, appBounds);
+ }
}
}
void remove(WindowContainerTransaction wct) {
wct.removeInsetsSource(mToken, mOwner, INDEX, captionBar());
wct.removeInsetsSource(mToken, mOwner, INDEX, mandatorySystemGestures());
+ if (mExcludedFromAppBounds) {
+ wct.setAppBounds(mToken, new Rect());
+ }
}
@Override
@@ -956,9 +970,11 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
if (!(o instanceof WindowDecoration.WindowDecorationInsets that)) return false;
return Objects.equals(mToken, that.mToken) && Objects.equals(mOwner,
that.mOwner) && Objects.equals(mFrame, that.mFrame)
+ && Objects.equals(mTaskFrame, that.mTaskFrame)
&& Objects.deepEquals(mBoundingRects, that.mBoundingRects)
&& mFlags == that.mFlags
- && mShouldAddCaptionInset == that.mShouldAddCaptionInset;
+ && mShouldAddCaptionInset == that.mShouldAddCaptionInset
+ && mExcludedFromAppBounds == that.mExcludedFromAppBounds;
}
@Override
diff --git a/libs/WindowManager/Shell/tests/unittest/Android.bp b/libs/WindowManager/Shell/tests/unittest/Android.bp
index 91be5f58b1f7..bf5e374c7607 100644
--- a/libs/WindowManager/Shell/tests/unittest/Android.bp
+++ b/libs/WindowManager/Shell/tests/unittest/Android.bp
@@ -64,6 +64,7 @@ android_test {
"platform-test-annotations",
"flag-junit",
"platform-parametric-runner-lib",
+ "platform-compat-test-rules",
],
libs: [
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index aa7944cc837f..1837c983f9e1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -35,6 +35,7 @@ import android.content.pm.ActivityInfo.CONFIG_DENSITY
import android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
import android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
import android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
+import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.content.res.Configuration.ORIENTATION_LANDSCAPE
import android.content.res.Configuration.ORIENTATION_PORTRAIT
@@ -315,7 +316,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
mContext,
mockHandler,
)
- desktopModeCompatPolicy = DesktopModeCompatPolicy(context)
+ desktopModeCompatPolicy = spy(DesktopModeCompatPolicy(context))
whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenAnswer { runningTasks }
whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() }
@@ -1303,6 +1304,50 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
+ fun addMoveToDesktopChanges_excludeCaptionFromAppBounds_nonResizableLandscape() {
+ setUpLandscapeDisplay()
+ val task =
+ setUpFullscreenTask(
+ isResizable = false,
+ screenOrientation = SCREEN_ORIENTATION_LANDSCAPE,
+ )
+ whenever(desktopModeCompatPolicy.shouldExcludeCaptionFromAppBounds(task)).thenReturn(true)
+ val initialAspectRatio = calculateAspectRatio(task)
+ val wct = WindowContainerTransaction()
+ controller.addMoveToDesktopChanges(wct, task)
+
+ val finalBounds = findBoundsChange(wct, task)
+ val captionInsets = getAppHeaderHeight(context)
+ finalBounds!!.top += captionInsets
+ val finalAspectRatio =
+ maxOf(finalBounds.height(), finalBounds.width()) /
+ minOf(finalBounds.height(), finalBounds.width()).toFloat()
+ assertThat(finalAspectRatio).isWithin(FLOAT_TOLERANCE).of(initialAspectRatio)
+ }
+
+ @Test
+ fun addMoveToDesktopChanges_excludeCaptionFromAppBounds_nonResizablePortrait() {
+ setUpLandscapeDisplay()
+ val task =
+ setUpFullscreenTask(
+ isResizable = false,
+ screenOrientation = SCREEN_ORIENTATION_PORTRAIT,
+ )
+ whenever(desktopModeCompatPolicy.shouldExcludeCaptionFromAppBounds(task)).thenReturn(true)
+ val initialAspectRatio = calculateAspectRatio(task)
+ val wct = WindowContainerTransaction()
+ controller.addMoveToDesktopChanges(wct, task)
+
+ val finalBounds = findBoundsChange(wct, task)
+ val captionInsets = getAppHeaderHeight(context)
+ finalBounds!!.top += captionInsets
+ val finalAspectRatio =
+ maxOf(finalBounds.height(), finalBounds.width()) /
+ minOf(finalBounds.height(), finalBounds.width()).toFloat()
+ assertThat(finalAspectRatio).isWithin(FLOAT_TOLERANCE).of(initialAspectRatio)
+ }
+
+ @Test
fun launchIntent_taskInDesktopMode_transitionStarted() {
setUpLandscapeDisplay()
val freeformTask = setUpFreeformTask()
@@ -4124,7 +4169,6 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
shouldLetterbox = true,
)
setUpLandscapeDisplay()
-
spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
val wct = getLatestDragToDesktopWct()
assertThat(findBoundsChange(wct, task)).isEqualTo(UNRESIZABLE_PORTRAIT_BOUNDS)
@@ -5088,11 +5132,10 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
val bounds = Rect(0, 0, 200, 100)
val task =
setUpFreeformTask(DEFAULT_DISPLAY, bounds).apply {
- topActivityInfo =
- ActivityInfo().apply {
- screenOrientation = SCREEN_ORIENTATION_LANDSCAPE
- configuration.windowConfiguration.appBounds = bounds
- }
+ topActivityInfo.apply {
+ this?.screenOrientation = SCREEN_ORIENTATION_LANDSCAPE
+ configuration.windowConfiguration.appBounds = bounds
+ }
appCompatTaskInfo.topActivityAppBounds.set(0, 0, bounds.width(), bounds.height())
isResizeable = false
}
@@ -5790,6 +5833,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
): RunningTaskInfo {
val task = createFreeformTask(displayId, bounds)
val activityInfo = ActivityInfo()
+ activityInfo.applicationInfo = ApplicationInfo()
task.topActivityInfo = activityInfo
if (background) {
whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(null)
@@ -5835,6 +5879,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
val activityInfo = ActivityInfo()
activityInfo.screenOrientation = screenOrientation
activityInfo.windowLayout = ActivityInfo.WindowLayout(0, 0F, 0, 0F, gravity, 0, 0)
+ activityInfo.applicationInfo = ApplicationInfo()
with(task) {
topActivityInfo = activityInfo
isResizeable = isResizable
@@ -6008,6 +6053,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
val STABLE_BOUNDS = Rect(0, 0, 1000, 1000)
const val MAX_TASK_LIMIT = 6
private const val TASKBAR_FRAME_HEIGHT = 200
+ private const val FLOAT_TOLERANCE = 0.005f
@JvmStatic
@Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicyTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicyTest.kt
index d9791bb43489..55e9de5eff5f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicyTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicyTest.kt
@@ -16,16 +16,26 @@
package com.android.wm.shell.shared.desktopmode
+import android.app.TaskInfo
+import android.compat.testing.PlatformCompatChangeRule
import android.content.ComponentName
+import android.content.pm.ActivityInfo
+import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
+import android.os.Process
+import android.platform.test.annotations.EnableFlags
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.internal.R
+import com.android.window.flags.Flags
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask
+import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
@@ -40,6 +50,7 @@ import org.mockito.kotlin.whenever
@RunWith(AndroidTestingRunner::class)
@SmallTest
class DesktopModeCompatPolicyTest : ShellTestCase() {
+ @get:Rule val compatRule = PlatformCompatChangeRule()
private lateinit var desktopModeCompatPolicy: DesktopModeCompatPolicy
@Before
@@ -142,4 +153,47 @@ class DesktopModeCompatPolicyTest : ShellTestCase() {
isTopActivityNoDisplay = true
}))
}
+
+ @Test
+ @EnableFlags(Flags.FLAG_EXCLUDE_CAPTION_FROM_APP_BOUNDS)
+ @DisableCompatChanges(ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED)
+ fun testShouldExcludeCaptionFromAppBounds_resizeable_false() {
+ assertFalse(desktopModeCompatPolicy.shouldExcludeCaptionFromAppBounds(
+ setUpFreeformTask().apply { isResizeable = true })
+ )
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_EXCLUDE_CAPTION_FROM_APP_BOUNDS)
+ @DisableCompatChanges(ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED)
+ fun testShouldExcludeCaptionFromAppBounds_nonResizeable_true() {
+ assertTrue(desktopModeCompatPolicy.shouldExcludeCaptionFromAppBounds(
+ setUpFreeformTask().apply { isResizeable = false })
+ )
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_EXCLUDE_CAPTION_FROM_APP_BOUNDS)
+ @EnableCompatChanges(ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED)
+ fun testShouldExcludeCaptionFromAppBounds_nonResizeable_sdk35_false() {
+ assertFalse(desktopModeCompatPolicy.shouldExcludeCaptionFromAppBounds(
+ setUpFreeformTask().apply { isResizeable = false })
+ )
+ }
+
+ fun setUpFreeformTask(): TaskInfo =
+ createFreeformTask().apply {
+ val componentName =
+ ComponentName.createRelative(
+ mContext,
+ DesktopModeCompatPolicyTest::class.java.simpleName
+ )
+ baseActivity = componentName
+ topActivityInfo = ActivityInfo().apply {
+ applicationInfo = ApplicationInfo().apply {
+ packageName = componentName.packageName
+ uid = Process.myUid()
+ }
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
index 7468c54321a0..9f106da9f4b9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
@@ -310,7 +310,7 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() {
mockDesktopModeWindowDecorFactory.create(
any(), any(), any(), any(), any(), any(), any(), eq(task), any(), any(), any(),
any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(),
- any(), any())
+ any(), any(), any())
).thenReturn(decoration)
decoration.mTaskInfo = task
whenever(decoration.user).thenReturn(mockUserHandle)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
index 18a780bbb07c..71c821dd9b71 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
@@ -108,6 +108,7 @@ import com.android.wm.shell.desktopmode.DesktopModeEventLogger;
import com.android.wm.shell.desktopmode.DesktopRepository;
import com.android.wm.shell.desktopmode.DesktopUserRepositories;
import com.android.wm.shell.desktopmode.WindowDecorCaptionHandleRepository;
+import com.android.wm.shell.shared.desktopmode.DesktopModeCompatPolicy;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.windowdecor.WindowDecoration.RelayoutParams;
@@ -167,6 +168,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
private static final boolean DEFAULT_IS_IN_FULL_IMMERSIVE_MODE = false;
private static final boolean DEFAULT_HAS_GLOBAL_FOCUS = true;
private static final boolean DEFAULT_SHOULD_IGNORE_CORNER_RADIUS = false;
+ private static final boolean DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS = false;
@Mock
private DisplayController mMockDisplayController;
@@ -236,6 +238,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
private DesktopRepository mDesktopRepository;
@Mock
private WindowDecorTaskResourceLoader mMockTaskResourceLoader;
+ @Mock
+ private DesktopModeCompatPolicy mDesktopModeCompatPolicy;
@Captor
private ArgumentCaptor<Function1<Boolean, Unit>> mOnMaxMenuHoverChangeListener;
@Captor
@@ -413,7 +417,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
new InsetsState(),
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
- /* shouldIgnoreCornerRadius= */ true);
+ /* shouldIgnoreCornerRadius= */ true,
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
assertThat(relayoutParams.mCornerRadius).isEqualTo(INVALID_CORNER_RADIUS);
}
@@ -592,6 +597,73 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
}
@Test
+ @EnableFlags({Flags.FLAG_ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION,
+ Flags.FLAG_ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS})
+ public void updateRelayoutParams_excludeCaptionTrue_forceConsumptionFalse() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ taskInfo.taskDescription.setTopOpaqueSystemBarsAppearance(0);
+ final RelayoutParams relayoutParams = new RelayoutParams();
+
+ DesktopModeWindowDecoration.updateRelayoutParams(
+ relayoutParams,
+ mTestableContext,
+ taskInfo,
+ mMockSplitScreenController,
+ DEFAULT_APPLY_START_TRANSACTION_ON_DRAW,
+ DEFAULT_SHOULD_SET_TASK_POSITIONING_AND_CROP,
+ DEFAULT_IS_STATUSBAR_VISIBLE,
+ DEFAULT_IS_KEYGUARD_VISIBLE_AND_OCCLUDED,
+ DEFAULT_IS_IN_FULL_IMMERSIVE_MODE,
+ new InsetsState(),
+ DEFAULT_HAS_GLOBAL_FOCUS,
+ mExclusionRegion,
+ DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
+ /* shouldExcludeCaptionFromAppBounds */ true);
+
+ // Force consuming flags are disabled.
+ assertThat((relayoutParams.mInsetSourceFlags & FLAG_FORCE_CONSUMING) == 0).isTrue();
+ assertThat(
+ (relayoutParams.mInsetSourceFlags & FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR) == 0)
+ .isTrue();
+ // Exclude caption from app bounds is true.
+ assertThat(relayoutParams.mShouldSetAppBounds).isTrue();
+ }
+
+ @Test
+ @EnableFlags({Flags.FLAG_ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION,
+ Flags.FLAG_ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS})
+ public void updateRelayoutParams_excludeCaptionFalse_forceConsumptionTrue() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ taskInfo.taskDescription.setTopOpaqueSystemBarsAppearance(0);
+ final RelayoutParams relayoutParams = new RelayoutParams();
+
+ DesktopModeWindowDecoration.updateRelayoutParams(
+ relayoutParams,
+ mTestableContext,
+ taskInfo,
+ mMockSplitScreenController,
+ DEFAULT_APPLY_START_TRANSACTION_ON_DRAW,
+ DEFAULT_SHOULD_SET_TASK_POSITIONING_AND_CROP,
+ DEFAULT_IS_STATUSBAR_VISIBLE,
+ DEFAULT_IS_KEYGUARD_VISIBLE_AND_OCCLUDED,
+ DEFAULT_IS_IN_FULL_IMMERSIVE_MODE,
+ new InsetsState(),
+ DEFAULT_HAS_GLOBAL_FOCUS,
+ mExclusionRegion,
+ DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
+
+ assertThat((relayoutParams.mInsetSourceFlags & FLAG_FORCE_CONSUMING) != 0).isTrue();
+ assertThat(
+ (relayoutParams.mInsetSourceFlags & FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR) != 0)
+ .isTrue();
+ // Exclude caption from app bounds is false.
+ assertThat(relayoutParams.mShouldSetAppBounds).isFalse();
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS)
public void updateRelayoutParams_header_addsForceConsumingCaptionBar() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
@@ -658,7 +730,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
insetsState,
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
- DEFAULT_SHOULD_IGNORE_CORNER_RADIUS);
+ DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
// Takes status bar inset as padding, ignores caption bar inset.
assertThat(relayoutParams.mCaptionTopPadding).isEqualTo(50);
@@ -684,7 +757,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
new InsetsState(),
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
- DEFAULT_SHOULD_IGNORE_CORNER_RADIUS);
+ DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
assertThat(relayoutParams.mIsInsetSource).isFalse();
}
@@ -709,7 +783,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
new InsetsState(),
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
- DEFAULT_SHOULD_IGNORE_CORNER_RADIUS);
+ DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
// Header is always shown because it's assumed the status bar is always visible.
assertThat(relayoutParams.mIsCaptionVisible).isTrue();
@@ -734,7 +809,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
new InsetsState(),
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
- DEFAULT_SHOULD_IGNORE_CORNER_RADIUS);
+ DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
assertThat(relayoutParams.mIsCaptionVisible).isTrue();
}
@@ -758,7 +834,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
new InsetsState(),
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
- DEFAULT_SHOULD_IGNORE_CORNER_RADIUS);
+ DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
assertThat(relayoutParams.mIsCaptionVisible).isFalse();
}
@@ -782,7 +859,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
new InsetsState(),
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
- DEFAULT_SHOULD_IGNORE_CORNER_RADIUS);
+ DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
assertThat(relayoutParams.mIsCaptionVisible).isFalse();
}
@@ -807,7 +885,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
new InsetsState(),
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
- DEFAULT_SHOULD_IGNORE_CORNER_RADIUS);
+ DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
assertThat(relayoutParams.mIsCaptionVisible).isTrue();
@@ -824,7 +903,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
new InsetsState(),
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
- DEFAULT_SHOULD_IGNORE_CORNER_RADIUS);
+ DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
assertThat(relayoutParams.mIsCaptionVisible).isFalse();
}
@@ -849,7 +929,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
new InsetsState(),
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
- DEFAULT_SHOULD_IGNORE_CORNER_RADIUS);
+ DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
assertThat(relayoutParams.mIsCaptionVisible).isFalse();
}
@@ -1513,7 +1594,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
new InsetsState(),
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
- DEFAULT_SHOULD_IGNORE_CORNER_RADIUS);
+ DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
}
private DesktopModeWindowDecoration createWindowDecoration(
@@ -1561,7 +1643,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
WindowContainerTransaction::new, SurfaceControl::new,
new WindowManagerWrapper(mMockWindowManager), mMockSurfaceControlViewHostFactory,
mMockWindowDecorViewHostSupplier, maximizeMenuFactory, mMockHandleMenuFactory,
- mMockMultiInstanceHelper, mMockCaptionHandleRepository, mDesktopModeEventLogger);
+ mMockMultiInstanceHelper, mMockCaptionHandleRepository, mDesktopModeEventLogger,
+ mDesktopModeCompatPolicy);
windowDecor.setCaptionListeners(mMockTouchEventListener, mMockTouchEventListener,
mMockTouchEventListener, mMockTouchEventListener);
windowDecor.setExclusionRegionListener(mMockExclusionRegionListener);
@@ -1592,6 +1675,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
"DesktopModeWindowDecorationTests");
taskInfo.baseActivity = new ComponentName("com.android.wm.shell.windowdecor",
"DesktopModeWindowDecorationTests");
+ taskInfo.topActivityInfo = createActivityInfo();
return taskInfo;
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index 3a8dcd674b74..aa1f82e3e4d8 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -831,6 +831,54 @@ public class WindowDecorationTests extends ShellTestCase {
}
@Test
+ public void testRelayout_setAppBoundsIfNeeded() {
+ final Display defaultDisplay = mock(Display.class);
+ doReturn(defaultDisplay).when(mMockDisplayController).getDisplay(Display.DEFAULT_DISPLAY);
+ final WindowContainerToken token = TestRunningTaskInfoBuilder.createMockWCToken();
+ final TestRunningTaskInfoBuilder builder = new TestRunningTaskInfoBuilder()
+ .setDisplayId(Display.DEFAULT_DISPLAY)
+ .setVisible(true);
+
+ final ActivityManager.RunningTaskInfo taskInfo =
+ builder.setToken(token).setBounds(TASK_BOUNDS).build();
+ final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo);
+ mRelayoutParams.mIsCaptionVisible = true;
+ mRelayoutParams.mShouldSetAppBounds = true;
+
+ windowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
+ final Rect appBounds = new Rect(TASK_BOUNDS);
+ appBounds.top += WindowDecoration.loadDimensionPixelSize(
+ windowDecor.mDecorWindowContext.getResources(), mRelayoutParams.mCaptionHeightId);
+ verify(mMockWindowContainerTransaction).setAppBounds(eq(token), eq(appBounds));
+ }
+
+ @Test
+ public void testRelayout_setAppBoundsIfNeeded_reset() {
+ final Display defaultDisplay = mock(Display.class);
+ doReturn(defaultDisplay).when(mMockDisplayController).getDisplay(Display.DEFAULT_DISPLAY);
+ final WindowContainerToken token = TestRunningTaskInfoBuilder.createMockWCToken();
+ final TestRunningTaskInfoBuilder builder = new TestRunningTaskInfoBuilder()
+ .setDisplayId(Display.DEFAULT_DISPLAY)
+ .setVisible(true);
+
+ final ActivityManager.RunningTaskInfo taskInfo =
+ builder.setToken(token).setBounds(TASK_BOUNDS).build();
+ final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo);
+
+ mRelayoutParams.mIsCaptionVisible = true;
+ mRelayoutParams.mIsInsetSource = true;
+ mRelayoutParams.mShouldSetAppBounds = true;
+ windowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
+
+ mRelayoutParams.mIsCaptionVisible = true;
+ mRelayoutParams.mIsInsetSource = false;
+ mRelayoutParams.mShouldSetAppBounds = false;
+ windowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
+
+ verify(mMockWindowContainerTransaction).setAppBounds(eq(token), eq(new Rect()));
+ }
+
+ @Test
public void testTaskPositionAndCropNotSetWhenFalse() {
final Display defaultDisplay = mock(Display.class);
doReturn(defaultDisplay).when(mMockDisplayController)
diff --git a/services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java b/services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java
index c6831881ea48..bbc33004ee54 100644
--- a/services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java
@@ -228,10 +228,11 @@ class AppCompatSizeCompatModePolicy {
int rotation = newParentConfiguration.windowConfiguration.getRotation();
final boolean isFixedToUserRotation = mActivityRecord.mDisplayContent == null
|| mActivityRecord.mDisplayContent.getDisplayRotation().isFixedToUserRotation();
+ final boolean tasksAreFloating =
+ newParentConfiguration.windowConfiguration.tasksAreFloating();
// Ignore parent rotation for floating tasks as window rotation is independent of its parent
// and thus will remain, and so should be reconfigured, in its original rotation.
- if (!isFixedToUserRotation
- && !newParentConfiguration.windowConfiguration.tasksAreFloating()) {
+ if (!isFixedToUserRotation && !tasksAreFloating) {
// Use parent rotation because the original display can be rotated.
resolvedConfig.windowConfiguration.setRotation(rotation);
} else {
@@ -297,7 +298,9 @@ class AppCompatSizeCompatModePolicy {
final float lastSizeCompatScale = mSizeCompatScale;
updateSizeCompatScale(resolvedAppBounds, containerAppBounds, newParentConfiguration);
- final int containerTopInset = containerAppBounds.top - containerBounds.top;
+ // Container top inset when floating is not included in height of bounds.
+ final int containerTopInset = tasksAreFloating ? 0
+ : containerAppBounds.top - containerBounds.top;
final boolean topNotAligned =
containerTopInset != resolvedAppBounds.top - resolvedBounds.top;
if (mSizeCompatScale != 1f || topNotAligned) {