diff options
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) { |