summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
Diffstat (limited to 'libs')
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/common/AcceptOnceConsumer.java (renamed from libs/WindowManager/Jetpack/src/androidx/window/util/AcceptOnceConsumer.java)2
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/common/BaseDataProducer.java (renamed from libs/WindowManager/Jetpack/src/androidx/window/util/BaseDataProducer.java)4
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java2
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java5
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/common/ExtensionHelper.java (renamed from libs/WindowManager/Jetpack/src/androidx/window/util/ExtensionHelper.java)2
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/common/RawFoldingFeatureProducer.java4
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java6
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java2
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarHelper.java4
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/common/ExtensionHelperTest.java (renamed from libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/util/ExtensionHelperTest.java)2
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt22
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskPosition.kt151
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt37
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt198
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
}
}