diff options
Diffstat (limited to 'libs')
19 files changed, 401 insertions, 55 deletions
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/util/AcceptOnceConsumer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/AcceptOnceConsumer.java index 63828ab2e62b..c2f827a22fc2 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/util/AcceptOnceConsumer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/common/AcceptOnceConsumer.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package androidx.window.util; +package androidx.window.common; import android.annotation.NonNull; diff --git a/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDataProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/BaseDataProducer.java index cd26efd4fdb6..e7099dc3a281 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDataProducer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/common/BaseDataProducer.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package androidx.window.util; +package androidx.window.common; import androidx.annotation.GuardedBy; import androidx.annotation.NonNull; @@ -125,4 +125,4 @@ public abstract class BaseDataProducer<T> implements mCallbacksToRemove.add(callback); } } -}
\ No newline at end of file +} diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java b/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java index e37dea4dfd69..b95bca16ef5b 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java @@ -16,7 +16,7 @@ package androidx.window.common; -import static androidx.window.util.ExtensionHelper.isZero; +import static androidx.window.common.ExtensionHelper.isZero; import android.annotation.IntDef; import android.annotation.Nullable; diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java index 98935e95deaf..b2bc3de1e7f5 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java @@ -31,9 +31,6 @@ import android.text.TextUtils; import android.util.Log; import android.util.SparseIntArray; -import androidx.window.util.AcceptOnceConsumer; -import androidx.window.util.BaseDataProducer; - import com.android.internal.R; import java.util.ArrayList; @@ -44,7 +41,7 @@ import java.util.Optional; import java.util.function.Consumer; /** - * An implementation of {@link androidx.window.util.BaseDataProducer} that returns + * An implementation of {@link BaseDataProducer} that returns * the device's posture by mapping the state returned from {@link DeviceStateManager} to * values provided in the resources' config at {@link R.array#config_device_state_postures}. */ diff --git a/libs/WindowManager/Jetpack/src/androidx/window/util/ExtensionHelper.java b/libs/WindowManager/Jetpack/src/androidx/window/common/ExtensionHelper.java index a08db7939eca..f466d603bda3 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/util/ExtensionHelper.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/common/ExtensionHelper.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package androidx.window.util; +package androidx.window.common; import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_90; diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/RawFoldingFeatureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/RawFoldingFeatureProducer.java index 88264f383153..6d758f1fb3c1 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/common/RawFoldingFeatureProducer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/common/RawFoldingFeatureProducer.java @@ -26,15 +26,13 @@ import android.os.Looper; import android.provider.Settings; import android.text.TextUtils; -import androidx.window.util.BaseDataProducer; - import com.android.internal.R; import java.util.Optional; import java.util.function.Consumer; /** - * Implementation of {@link androidx.window.util.BaseDataProducer} that produces a + * Implementation of {@link BaseDataProducer} that produces a * {@link String} that can be parsed to a {@link CommonFoldingFeature}. * {@link RawFoldingFeatureProducer} searches for the value in two places. The first check is in * settings where the {@link String} property is saved with the key diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java index 84984a9f8c7b..a3ef68a15196 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java @@ -21,9 +21,9 @@ import static android.view.Display.INVALID_DISPLAY; import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_FLAT; import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_HALF_OPENED; -import static androidx.window.util.ExtensionHelper.isZero; -import static androidx.window.util.ExtensionHelper.rotateRectToDisplayRotation; -import static androidx.window.util.ExtensionHelper.transformToWindowSpaceRect; +import static androidx.window.common.ExtensionHelper.isZero; +import static androidx.window.common.ExtensionHelper.rotateRectToDisplayRotation; +import static androidx.window.common.ExtensionHelper.transformToWindowSpaceRect; import android.app.Activity; import android.app.ActivityThread; diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java index 339908a3a9a4..b63fd0802e5f 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java @@ -25,11 +25,11 @@ import android.os.Bundle; import android.os.IBinder; import androidx.annotation.NonNull; +import androidx.window.common.BaseDataProducer; import androidx.window.common.CommonFoldingFeature; import androidx.window.common.DeviceStateManagerFoldingFeatureProducer; import androidx.window.common.EmptyLifecycleCallbacksAdapter; import androidx.window.common.RawFoldingFeatureProducer; -import androidx.window.util.BaseDataProducer; import java.util.ArrayList; import java.util.List; diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarHelper.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarHelper.java index bb6ab47b144d..4fd03e4bdc0b 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarHelper.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarHelper.java @@ -17,8 +17,8 @@ package androidx.window.sidecar; import static android.view.Display.DEFAULT_DISPLAY; -import static androidx.window.util.ExtensionHelper.rotateRectToDisplayRotation; -import static androidx.window.util.ExtensionHelper.transformToWindowSpaceRect; +import static androidx.window.common.ExtensionHelper.rotateRectToDisplayRotation; +import static androidx.window.common.ExtensionHelper.transformToWindowSpaceRect; import android.annotation.NonNull; import android.app.Activity; diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/util/ExtensionHelperTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/common/ExtensionHelperTest.java index 3278cdf1c337..b6e951961a69 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/util/ExtensionHelperTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/common/ExtensionHelperTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package androidx.window.util; +package androidx.window.common; import static org.junit.Assert.assertEquals; diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt index b1c9a775913a..2b01eac3c210 100644 --- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt @@ -35,6 +35,7 @@ enum class DesktopModeFlags( ) { // All desktop mode related flags will be added here DESKTOP_WINDOWING_MODE(Flags::enableDesktopWindowingMode, true), + CASCADING_WINDOWS(Flags::enableCascadingWindows, true), WALLPAPER_ACTIVITY(Flags::enableDesktopWindowingWallpaperActivity, true), MODALS_POLICY(Flags::enableDesktopWindowingModalsPolicy, true), THEMED_APP_HEADERS(Flags::enableThemedAppHeaders, true), diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java index 972dce51e02b..24c568c23bf2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java @@ -169,6 +169,8 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView R.layout.bubble_overflow_container, null /* root */); mOverflowView.initialize(expandedViewManager, positioner); addView(mOverflowView); + // Don't show handle for overflow + mHandleView.setVisibility(View.GONE); } else { mTaskView = bubbleTaskView.getTaskView(); mBubbleTaskViewHelper = new BubbleTaskViewHelper(mContext, expandedViewManager, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java index 4fbb5744b64b..d7d19f7b8bbd 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java @@ -315,7 +315,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged } } if (!mImeShowing) { - removeImeSurface(); + removeImeSurface(mDisplayId); } } } else if (!android.view.inputmethod.Flags.refactorInsetsController() @@ -617,7 +617,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged || hasLeash) { t.hide(mImeSourceControl.getLeash()); } - removeImeSurface(); + removeImeSurface(mDisplayId); ImeTracker.forLogging().onHidden(mStatsToken); } else if (mAnimationDirection == DIRECTION_SHOW && !mCancelled) { ImeTracker.forLogging().onShown(mStatsToken); @@ -671,10 +671,10 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged } } - void removeImeSurface() { + void removeImeSurface(int displayId) { // Remove the IME surface to make the insets invisible for // non-client controlled insets. - InputMethodManagerGlobal.removeImeSurface( + InputMethodManagerGlobal.removeImeSurface(displayId, e -> Slog.e(TAG, "Failed to remove IME surface.", e)); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java index afe46f500e51..35d387632f03 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java @@ -569,7 +569,7 @@ public abstract class WMShellModule { ShellTaskOrganizer shellTaskOrganizer) { int maxTaskLimit = DesktopModeStatus.getMaxTaskLimit(context); if (!DesktopModeStatus.canEnterDesktopMode(context) - || DESKTOP_WINDOWING_MODE.isEnabled(context) + || !DESKTOP_WINDOWING_MODE.isEnabled(context) || maxTaskLimit <= 0) { return Optional.empty(); } 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 da212e704b24..9fcf73d2c375 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 @@ -52,8 +52,10 @@ fun calculateInitialBounds( val idealSize = calculateIdealSize(screenBounds, scale) // If no top activity exists, apps fullscreen bounds and aspect ratio cannot be calculated. // Instead default to the desired initial bounds. + val stableBounds = Rect() + displayLayout.getStableBoundsForDesktopMode(stableBounds) val topActivityInfo = - taskInfo.topActivityInfo ?: return positionInScreen(idealSize, screenBounds) + taskInfo.topActivityInfo ?: return positionInScreen(idealSize, stableBounds) val initialSize: Size = when (taskInfo.configuration.orientation) { @@ -100,7 +102,7 @@ fun calculateInitialBounds( } } - return positionInScreen(initialSize, screenBounds) + return positionInScreen(initialSize, stableBounds) } /** @@ -163,17 +165,11 @@ private fun calculateIdealSize(screenBounds: Rect, scale: Float): Size { } /** Adjusts bounds to be positioned in the middle of the screen. */ -private fun positionInScreen(desiredSize: Size, screenBounds: Rect): Rect { - // TODO(b/325240051): Position apps with bottom heavy offset - val heightOffset = (screenBounds.height() - desiredSize.height) / 2 - val widthOffset = (screenBounds.width() - desiredSize.width) / 2 - return Rect( - widthOffset, - heightOffset, - desiredSize.width + widthOffset, - desiredSize.height + heightOffset - ) -} +private fun positionInScreen(desiredSize: Size, stableBounds: Rect): Rect = + Rect(0, 0, desiredSize.width, desiredSize.height).apply { + val offset = DesktopTaskPosition.Center.getTopLeftCoordinates(stableBounds, this) + offsetTo(offset.x, offset.y) + } /** * Adjusts bounds to be positioned in the middle of the area provided, not necessarily the diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskPosition.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskPosition.kt new file mode 100644 index 000000000000..97abda81d12d --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskPosition.kt @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.desktopmode + +import android.app.TaskInfo +import android.content.res.Resources +import android.graphics.Point +import android.graphics.Rect +import android.view.Gravity +import com.android.internal.annotations.VisibleForTesting +import com.android.wm.shell.desktopmode.DesktopTaskPosition.BottomLeft +import com.android.wm.shell.desktopmode.DesktopTaskPosition.BottomRight +import com.android.wm.shell.desktopmode.DesktopTaskPosition.Center +import com.android.wm.shell.desktopmode.DesktopTaskPosition.TopLeft +import com.android.wm.shell.desktopmode.DesktopTaskPosition.TopRight +import com.android.wm.shell.R + +/** + * The position of a task window in desktop mode. + */ +sealed class DesktopTaskPosition { + data object Center : DesktopTaskPosition() { + private const val WINDOW_HEIGHT_PROPORTION = 0.375 + + override fun getTopLeftCoordinates(frame: Rect, window: Rect): Point { + val x = (frame.width() - window.width()) / 2 + // Position with more margin at the bottom. + val y = (frame.height() - window.height()) * WINDOW_HEIGHT_PROPORTION + frame.top + return Point(x, y.toInt()) + } + + override fun next(): DesktopTaskPosition { + return BottomRight + } + } + + data object BottomRight : DesktopTaskPosition() { + override fun getTopLeftCoordinates(frame: Rect, window: Rect): Point { + return Point(frame.right - window.width(), frame.bottom - window.height()) + } + + override fun next(): DesktopTaskPosition { + return TopLeft + } + } + + data object TopLeft : DesktopTaskPosition() { + override fun getTopLeftCoordinates(frame: Rect, window: Rect): Point { + return Point(frame.left, frame.top) + } + + override fun next(): DesktopTaskPosition { + return BottomLeft + } + } + + data object BottomLeft : DesktopTaskPosition() { + override fun getTopLeftCoordinates(frame: Rect, window: Rect): Point { + return Point(frame.left, frame.bottom - window.height()) + } + + override fun next(): DesktopTaskPosition { + return TopRight + } + } + + data object TopRight : DesktopTaskPosition() { + override fun getTopLeftCoordinates(frame: Rect, window: Rect): Point { + return Point(frame.right - window.width(), frame.top) + } + + override fun next(): DesktopTaskPosition { + return Center + } + } + + /** + * Returns the top left coordinates for the window to be placed in the given + * DesktopTaskPosition in the frame. + */ + abstract fun getTopLeftCoordinates(frame: Rect, window: Rect): Point + + abstract fun next(): DesktopTaskPosition +} + +/** + * If the app has specified horizontal or vertical gravity layout, don't change the + * task position for cascading effect. + */ +fun canChangeTaskPosition(taskInfo: TaskInfo): Boolean { + taskInfo.topActivityInfo?.windowLayout?.let { + val horizontalGravityApplied = it.gravity.and(Gravity.HORIZONTAL_GRAVITY_MASK) + val verticalGravityApplied = it.gravity.and(Gravity.VERTICAL_GRAVITY_MASK) + return horizontalGravityApplied == 0 && verticalGravityApplied == 0 + } + return true +} + +/** + * Returns the current DesktopTaskPosition for a given window in the frame. + */ +@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) +fun Rect.getDesktopTaskPosition(bounds: Rect): DesktopTaskPosition { + return when { + top == bounds.top && left == bounds.left -> TopLeft + top == bounds.top && right == bounds.right -> TopRight + bottom == bounds.bottom && left == bounds.left -> BottomLeft + bottom == bounds.bottom && right == bounds.right -> BottomRight + else -> Center + } +} + +internal fun cascadeWindow(res: Resources, frame: Rect, prev: Rect, dest: Rect) { + val candidateBounds = Rect(dest) + val lastPos = frame.getDesktopTaskPosition(prev) + var destCoord = Center.getTopLeftCoordinates(frame, candidateBounds) + candidateBounds.offsetTo(destCoord.x, destCoord.y) + // If the default center position is not free or if last focused window is not at the + // center, get the next cascading window position. + if (!prevBoundsMovedAboveThreshold(res, prev, candidateBounds) || Center != lastPos) { + val nextCascadingPos = lastPos.next() + destCoord = nextCascadingPos.getTopLeftCoordinates(frame, dest) + } + dest.offsetTo(destCoord.x, destCoord.y) +} + +internal fun prevBoundsMovedAboveThreshold(res: Resources, prev: Rect, newBounds: Rect): Boolean { + // This is the required minimum dp for a task to be touchable. + val moveThresholdPx = res.getDimensionPixelSize( + R.dimen.freeform_required_visible_empty_space_in_header) + val leftFar = newBounds.left - prev.left > moveThresholdPx + val topFar = newBounds.top - prev.top > moveThresholdPx + val rightFar = prev.right - newBounds.right > moveThresholdPx + val bottomFar = prev.bottom - newBounds.bottom > moveThresholdPx + + return leftFar || topFar || rightFar || bottomFar +} 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 9d3b2e5d4a32..84d2aad94a78 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 @@ -403,7 +403,7 @@ class DesktopTasksController( * The second part of the animated drag to desktop transition, called after * [startDragToDesktop]. */ - private fun finalizeDragToDesktop(taskInfo: RunningTaskInfo, freeformBounds: Rect) { + private fun finalizeDragToDesktop(taskInfo: RunningTaskInfo) { ProtoLog.v( WM_SHELL_DESKTOP_MODE, "DesktopTasksController: finalizeDragToDesktop taskId=%d", @@ -415,7 +415,6 @@ class DesktopTasksController( val taskToMinimize = bringDesktopAppsToFrontBeforeShowingNewTask(taskInfo.displayId, wct, taskInfo.taskId) addMoveToDesktopChanges(wct, taskInfo) - wct.setBounds(taskInfo.token, freeformBounds) val transition = dragToDesktopTransitionHandler.finishDragToDesktopTransition(wct) transition?.let { addPendingMinimizeTransition(it, taskToMinimize) } } @@ -1071,10 +1070,12 @@ class DesktopTasksController( return if (wct.isEmpty) null else wct } - private fun addMoveToDesktopChanges( + @VisibleForTesting + fun addMoveToDesktopChanges( wct: WindowContainerTransaction, taskInfo: RunningTaskInfo ) { + val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return val tdaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(taskInfo.displayId)!! val tdaWindowingMode = tdaInfo.configuration.windowConfiguration.windowingMode val targetWindowingMode = @@ -1084,6 +1085,28 @@ class DesktopTasksController( } else { WINDOWING_MODE_FREEFORM } + val initialBounds = if (DesktopModeFlags.DYNAMIC_INITIAL_BOUNDS.isEnabled(context)) { + calculateInitialBounds(displayLayout, taskInfo) + } else { + getDefaultDesktopTaskBounds(displayLayout) + } + + if (DesktopModeFlags.CASCADING_WINDOWS.isEnabled(context)) { + val stableBounds = Rect() + displayLayout.getStableBoundsForDesktopMode(stableBounds) + + val activeTasks = desktopModeTaskRepository + .getActiveNonMinimizedOrderedTasks(taskInfo.displayId) + activeTasks.firstOrNull()?.let { activeTask -> + shellTaskOrganizer.getRunningTaskInfo(activeTask)?.let { + cascadeWindow(context.resources, stableBounds, + it.configuration.windowConfiguration.bounds, initialBounds) + } + } + } + if (canChangeTaskPosition(taskInfo)) { + wct.setBounds(taskInfo.token, initialBounds) + } wct.setWindowingMode(taskInfo.token, targetWindowingMode) wct.reorder(taskInfo.token, true /* onTop */) if (useDesktopOverrideDensity()) { @@ -1343,16 +1366,10 @@ class DesktopTasksController( val indicatorType = indicator.updateIndicatorType(inputCoordinates, taskInfo.windowingMode) when (indicatorType) { IndicatorType.TO_DESKTOP_INDICATOR -> { - val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) - ?: return IndicatorType.NO_INDICATOR // Start a new jank interaction for the drag release to desktop window animation. interactionJankMonitor.begin(taskSurface, context, CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE, "to_desktop") - if (DesktopModeFlags.DYNAMIC_INITIAL_BOUNDS.isEnabled(context)) { - finalizeDragToDesktop(taskInfo, calculateInitialBounds(displayLayout, taskInfo)) - } else { - finalizeDragToDesktop(taskInfo, getDefaultDesktopTaskBounds(displayLayout)) - } + finalizeDragToDesktop(taskInfo) } IndicatorType.NO_INDICATOR, IndicatorType.TO_FULLSCREEN_INDICATOR -> { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java index 2c0aa12f22d2..764d5a97e3e1 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java @@ -83,7 +83,7 @@ public class DisplayImeControllerTest extends ShellTestCase { } }, mExecutor) { @Override - void removeImeSurface() { } + void removeImeSurface(int displayId) { } }.new PerDisplay(DEFAULT_DISPLAY, ROTATION_0); } 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 e66018f26d46..2368ceff368e 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 @@ -43,6 +43,7 @@ import android.platform.test.annotations.EnableFlags import android.platform.test.flag.junit.SetFlagsRule import android.testing.AndroidTestingRunner import android.view.Display.DEFAULT_DISPLAY +import android.view.Gravity import android.view.SurfaceControl import android.view.WindowManager import android.view.WindowManager.TRANSIT_CHANGE @@ -70,6 +71,7 @@ import com.android.internal.jank.InteractionJankMonitor import com.android.window.flags.Flags import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE import com.android.wm.shell.MockToken +import com.android.wm.shell.R import com.android.wm.shell.RootTaskDisplayAreaOrganizer import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.ShellTestCase @@ -185,12 +187,12 @@ class DesktopTasksControllerTest : ShellTestCase() { private val DISPLAY_DIMENSION_SHORT = 1600 private val DISPLAY_DIMENSION_LONG = 2560 - private val DEFAULT_LANDSCAPE_BOUNDS = Rect(320, 200, 2240, 1400) - private val DEFAULT_PORTRAIT_BOUNDS = Rect(200, 320, 1400, 2240) - private val RESIZABLE_LANDSCAPE_BOUNDS = Rect(25, 680, 1575, 1880) - private val RESIZABLE_PORTRAIT_BOUNDS = Rect(680, 200, 1880, 1400) - private val UNRESIZABLE_LANDSCAPE_BOUNDS = Rect(25, 699, 1575, 1861) - private val UNRESIZABLE_PORTRAIT_BOUNDS = Rect(830, 200, 1730, 1400) + private val DEFAULT_LANDSCAPE_BOUNDS = Rect(320, 75, 2240, 1275) + private val DEFAULT_PORTRAIT_BOUNDS = Rect(200, 165, 1400, 2085) + private val RESIZABLE_LANDSCAPE_BOUNDS = Rect(25, 435, 1575, 1635) + private val RESIZABLE_PORTRAIT_BOUNDS = Rect(680, 75, 1880, 1275) + private val UNRESIZABLE_LANDSCAPE_BOUNDS = Rect(25, 449, 1575, 1611) + private val UNRESIZABLE_PORTRAIT_BOUNDS = Rect(830, 75, 1730, 1275) @Before fun setUp() { @@ -590,6 +592,161 @@ class DesktopTasksControllerTest : ShellTestCase() { } @Test + fun addMoveToDesktopChanges_gravityLeft_noBoundsApplied() { + setUpLandscapeDisplay() + val task = setUpFullscreenTask(gravity = Gravity.LEFT) + val wct = WindowContainerTransaction() + controller.addMoveToDesktopChanges(wct, task) + + val finalBounds = findBoundsChange(wct, task) + assertThat(finalBounds).isEqualTo(Rect()) + } + + @Test + fun addMoveToDesktopChanges_gravityRight_noBoundsApplied() { + setUpLandscapeDisplay() + val task = setUpFullscreenTask(gravity = Gravity.RIGHT) + val wct = WindowContainerTransaction() + controller.addMoveToDesktopChanges(wct, task) + + val finalBounds = findBoundsChange(wct, task) + assertThat(finalBounds).isEqualTo(Rect()) + } + + @Test + fun addMoveToDesktopChanges_gravityTop_noBoundsApplied() { + setUpLandscapeDisplay() + val task = setUpFullscreenTask(gravity = Gravity.TOP) + val wct = WindowContainerTransaction() + controller.addMoveToDesktopChanges(wct, task) + + val finalBounds = findBoundsChange(wct, task) + assertThat(finalBounds).isEqualTo(Rect()) + } + + @Test + fun addMoveToDesktopChanges_gravityBottom_noBoundsApplied() { + setUpLandscapeDisplay() + val task = setUpFullscreenTask(gravity = Gravity.BOTTOM) + val wct = WindowContainerTransaction() + controller.addMoveToDesktopChanges(wct, task) + + val finalBounds = findBoundsChange(wct, task) + assertThat(finalBounds).isEqualTo(Rect()) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS) + fun addMoveToDesktopChanges_positionBottomRight() { + setUpLandscapeDisplay() + val stableBounds = Rect() + displayLayout.getStableBoundsForDesktopMode(stableBounds) + + setUpFreeformTask(bounds = DEFAULT_LANDSCAPE_BOUNDS) + + val task = setUpFullscreenTask() + val wct = WindowContainerTransaction() + controller.addMoveToDesktopChanges(wct, task) + + val finalBounds = findBoundsChange(wct, task) + assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!)) + .isEqualTo(DesktopTaskPosition.BottomRight) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS) + fun addMoveToDesktopChanges_positionTopLeft() { + setUpLandscapeDisplay() + val stableBounds = Rect() + displayLayout.getStableBoundsForDesktopMode(stableBounds) + + addFreeformTaskAtPosition(DesktopTaskPosition.BottomRight, stableBounds) + + val task = setUpFullscreenTask() + val wct = WindowContainerTransaction() + controller.addMoveToDesktopChanges(wct, task) + + val finalBounds = findBoundsChange(wct, task) + assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!)) + .isEqualTo(DesktopTaskPosition.TopLeft) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS) + fun addMoveToDesktopChanges_positionBottomLeft() { + setUpLandscapeDisplay() + val stableBounds = Rect() + displayLayout.getStableBoundsForDesktopMode(stableBounds) + + addFreeformTaskAtPosition(DesktopTaskPosition.TopLeft, stableBounds) + + val task = setUpFullscreenTask() + val wct = WindowContainerTransaction() + controller.addMoveToDesktopChanges(wct, task) + + val finalBounds = findBoundsChange(wct, task) + assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!)) + .isEqualTo(DesktopTaskPosition.BottomLeft) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS) + fun addMoveToDesktopChanges_positionTopRight() { + setUpLandscapeDisplay() + val stableBounds = Rect() + displayLayout.getStableBoundsForDesktopMode(stableBounds) + + addFreeformTaskAtPosition(DesktopTaskPosition.BottomLeft, stableBounds) + + val task = setUpFullscreenTask() + val wct = WindowContainerTransaction() + controller.addMoveToDesktopChanges(wct, task) + + val finalBounds = findBoundsChange(wct, task) + assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!)) + .isEqualTo(DesktopTaskPosition.TopRight) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS) + fun addMoveToDesktopChanges_positionResetsToCenter() { + setUpLandscapeDisplay() + val stableBounds = Rect() + displayLayout.getStableBoundsForDesktopMode(stableBounds) + + addFreeformTaskAtPosition(DesktopTaskPosition.TopRight, stableBounds) + + val task = setUpFullscreenTask() + val wct = WindowContainerTransaction() + controller.addMoveToDesktopChanges(wct, task) + + val finalBounds = findBoundsChange(wct, task) + assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!)) + .isEqualTo(DesktopTaskPosition.Center) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS) + fun addMoveToDesktopChanges_defaultToCenterIfFree() { + setUpLandscapeDisplay() + val stableBounds = Rect() + displayLayout.getStableBoundsForDesktopMode(stableBounds) + + val minTouchTarget = context.resources.getDimensionPixelSize( + R.dimen.freeform_required_visible_empty_space_in_header) + addFreeformTaskAtPosition(DesktopTaskPosition.Center, stableBounds, + Rect(0, 0, 1600, 1200), Point(0, minTouchTarget + 1)) + + val task = setUpFullscreenTask() + val wct = WindowContainerTransaction() + controller.addMoveToDesktopChanges(wct, task) + + val finalBounds = findBoundsChange(wct, task) + assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!)) + .isEqualTo(DesktopTaskPosition.Center) + } + + @Test fun moveToDesktop_tdaFullscreen_windowingModeSetToFreeform() { val task = setUpFullscreenTask() val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!! @@ -2406,6 +2563,18 @@ class DesktopTasksControllerTest : ShellTestCase() { private val desktopWallpaperIntent: Intent get() = Intent(context, DesktopWallpaperActivity::class.java) + private fun addFreeformTaskAtPosition( + pos: DesktopTaskPosition, + stableBounds: Rect, + bounds: Rect = DEFAULT_LANDSCAPE_BOUNDS, + offsetPos: Point = Point(0, 0) + ): RunningTaskInfo { + val offset = pos.getTopLeftCoordinates(stableBounds, bounds) + val prevTaskBounds = Rect(bounds) + prevTaskBounds.offsetTo(offset.x + offsetPos.x, offset.y + offsetPos.y) + return setUpFreeformTask(bounds = prevTaskBounds) + } + private fun setUpFreeformTask( displayId: Int = DEFAULT_DISPLAY, bounds: Rect? = null @@ -2434,11 +2603,13 @@ class DesktopTasksControllerTest : ShellTestCase() { windowingMode: Int = WINDOWING_MODE_FULLSCREEN, deviceOrientation: Int = ORIENTATION_LANDSCAPE, screenOrientation: Int = SCREEN_ORIENTATION_UNSPECIFIED, - shouldLetterbox: Boolean = false + shouldLetterbox: Boolean = false, + gravity: Int = Gravity.NO_GRAVITY ): RunningTaskInfo { val task = createFullscreenTask(displayId) val activityInfo = ActivityInfo() activityInfo.screenOrientation = screenOrientation + activityInfo.windowLayout = ActivityInfo.WindowLayout(0, 0F, 0, 0F, gravity, 0, 0) with(task) { topActivityInfo = activityInfo isResizeable = isResizable @@ -2479,11 +2650,23 @@ class DesktopTasksControllerTest : ShellTestCase() { private fun setUpLandscapeDisplay() { whenever(displayLayout.width()).thenReturn(DISPLAY_DIMENSION_LONG) whenever(displayLayout.height()).thenReturn(DISPLAY_DIMENSION_SHORT) + val stableBounds = Rect(0, 0, DISPLAY_DIMENSION_LONG, + DISPLAY_DIMENSION_SHORT - Companion.TASKBAR_FRAME_HEIGHT + ) + whenever(displayLayout.getStableBoundsForDesktopMode(any())).thenAnswer { i -> + (i.arguments.first() as Rect).set(stableBounds) + } } private fun setUpPortraitDisplay() { whenever(displayLayout.width()).thenReturn(DISPLAY_DIMENSION_SHORT) whenever(displayLayout.height()).thenReturn(DISPLAY_DIMENSION_LONG) + val stableBounds = Rect(0, 0, DISPLAY_DIMENSION_SHORT, + DISPLAY_DIMENSION_LONG - Companion.TASKBAR_FRAME_HEIGHT + ) + whenever(displayLayout.getStableBoundsForDesktopMode(any())).thenAnswer { i -> + (i.arguments.first() as Rect).set(stableBounds) + } } private fun setUpSplitScreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo { @@ -2601,6 +2784,7 @@ class DesktopTasksControllerTest : ShellTestCase() { const val SECOND_DISPLAY = 2 val STABLE_BOUNDS = Rect(0, 0, 1000, 1000) const val MAX_TASK_LIMIT = 6 + private const val TASKBAR_FRAME_HEIGHT = 200 } } |