summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
Diffstat (limited to 'libs')
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java12
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/Android.bp1
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java122
-rw-r--r--libs/WindowManager/Shell/Android.bp3
-rw-r--r--libs/WindowManager/Shell/AndroidManifest.xml2
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt70
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt30
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java19
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java165
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSplashWindowCreator.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInit.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java112
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java112
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt225
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/extension/TaskInfo.kt11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt80
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/WindowDecorationViewHolder.kt9
-rw-r--r--libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/SwitchToOverviewFromDesktop.kt70
-rw-r--r--libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt5
-rw-r--r--libs/WindowManager/Shell/tests/unittest/Android.bp1
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java134
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt132
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlagsTest.kt92
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java5
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java5
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java9
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt43
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java16
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt29
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainerTest.kt4
47 files changed, 1012 insertions, 614 deletions
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index ea60b1531a3f..f1e7ef5ce123 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -1359,4 +1359,16 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
return new ParentContainerInfo(taskProperties.getTaskMetrics(), configuration,
windowLayoutInfo);
}
+
+ @VisibleForTesting
+ @NonNull
+ static String positionToString(@ContainerPosition int position) {
+ return switch (position) {
+ case CONTAINER_POSITION_LEFT -> "left";
+ case CONTAINER_POSITION_TOP -> "top";
+ case CONTAINER_POSITION_RIGHT -> "right";
+ case CONTAINER_POSITION_BOTTOM -> "bottom";
+ default -> "Unknown position:" + position;
+ };
+ }
}
diff --git a/libs/WindowManager/Jetpack/tests/unittest/Android.bp b/libs/WindowManager/Jetpack/tests/unittest/Android.bp
index 61ea51a35f58..139ddda5af3c 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/Android.bp
+++ b/libs/WindowManager/Jetpack/tests/unittest/Android.bp
@@ -32,6 +32,7 @@ android_test {
],
static_libs: [
+ "TestParameterInjector",
"androidx.window.extensions",
"androidx.window.extensions.core_core",
"junit",
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
index 325750243744..1c4c8870b26f 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
@@ -35,6 +35,7 @@ import static androidx.window.extensions.embedding.SplitPresenter.CONTAINER_POSI
import static androidx.window.extensions.embedding.SplitPresenter.CONTAINER_POSITION_RIGHT;
import static androidx.window.extensions.embedding.SplitPresenter.CONTAINER_POSITION_TOP;
import static androidx.window.extensions.embedding.SplitPresenter.getOverlayPosition;
+import static androidx.window.extensions.embedding.SplitPresenter.positionToString;
import static androidx.window.extensions.embedding.SplitPresenter.sanitizeBounds;
import static androidx.window.extensions.embedding.WindowAttributes.DIM_AREA_ON_TASK;
@@ -78,7 +79,6 @@ import android.window.WindowContainerTransaction;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.test.core.app.ApplicationProvider;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import androidx.window.common.DeviceStateManagerFoldingFeatureProducer;
import androidx.window.extensions.layout.WindowLayoutComponentImpl;
@@ -86,6 +86,9 @@ import androidx.window.extensions.layout.WindowLayoutInfo;
import com.android.window.flags.Flags;
+import com.google.testing.junit.testparameterinjector.TestParameter;
+import com.google.testing.junit.testparameterinjector.TestParameterInjector;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -108,7 +111,7 @@ import java.util.List;
@SuppressWarnings("GuardedBy")
@Presubmit
@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(TestParameterInjector.class)
public class OverlayPresentationTest {
@Rule
public MockitoRule rule = MockitoJUnit.rule();
@@ -875,57 +878,70 @@ public class OverlayPresentationTest {
eq(overlayContainer.getTaskFragmentToken()), eq(activityToken));
}
- // TODO(b/243518738): Rewrite with TestParameter.
- @Test
- public void testGetOverlayPosition() {
- assertWithMessage("It must be position left for left overlay.")
- .that(getOverlayPosition(new Rect(
- TASK_BOUNDS.left,
- TASK_BOUNDS.top,
- TASK_BOUNDS.right / 2,
- TASK_BOUNDS.bottom), TASK_BOUNDS)).isEqualTo(CONTAINER_POSITION_LEFT);
- assertWithMessage("It must be position left for shrunk left overlay.")
- .that(getOverlayPosition(new Rect(
- TASK_BOUNDS.left,
- TASK_BOUNDS.top + 20,
- TASK_BOUNDS.right / 2,
- TASK_BOUNDS.bottom - 20), TASK_BOUNDS)).isEqualTo(CONTAINER_POSITION_LEFT);
- assertWithMessage("It must be position left for top overlay.")
- .that(getOverlayPosition(new Rect(
- TASK_BOUNDS.left,
- TASK_BOUNDS.top,
- TASK_BOUNDS.right,
- TASK_BOUNDS.bottom / 2), TASK_BOUNDS)).isEqualTo(CONTAINER_POSITION_TOP);
- assertWithMessage("It must be position left for shrunk top overlay.")
- .that(getOverlayPosition(new Rect(
- TASK_BOUNDS.left + 20,
- TASK_BOUNDS.top,
- TASK_BOUNDS.right - 20,
- TASK_BOUNDS.bottom / 2), TASK_BOUNDS)).isEqualTo(CONTAINER_POSITION_TOP);
- assertWithMessage("It must be position left for right overlay.")
- .that(getOverlayPosition(new Rect(
- TASK_BOUNDS.right / 2,
- TASK_BOUNDS.top,
- TASK_BOUNDS.right,
- TASK_BOUNDS.bottom), TASK_BOUNDS)).isEqualTo(CONTAINER_POSITION_RIGHT);
- assertWithMessage("It must be position left for shrunk right overlay.")
- .that(getOverlayPosition(new Rect(
- TASK_BOUNDS.right / 2,
- TASK_BOUNDS.top + 20,
- TASK_BOUNDS.right,
- TASK_BOUNDS.bottom - 20), TASK_BOUNDS)).isEqualTo(CONTAINER_POSITION_RIGHT);
- assertWithMessage("It must be position left for bottom overlay.")
- .that(getOverlayPosition(new Rect(
- TASK_BOUNDS.left,
- TASK_BOUNDS.bottom / 2,
- TASK_BOUNDS.right,
- TASK_BOUNDS.bottom), TASK_BOUNDS)).isEqualTo(CONTAINER_POSITION_BOTTOM);
- assertWithMessage("It must be position left for shrunk bottom overlay.")
- .that(getOverlayPosition(new Rect(
- TASK_BOUNDS.left + 20,
- TASK_BOUNDS.bottom / 20,
- TASK_BOUNDS.right - 20,
- TASK_BOUNDS.bottom), TASK_BOUNDS)).isEqualTo(CONTAINER_POSITION_BOTTOM);
+ @Test
+ public void testGetOverlayPosition(@TestParameter OverlayPositionTestParams params) {
+ final Rect taskBounds = new Rect(TASK_BOUNDS);
+ final Rect overlayBounds = params.toOverlayBounds();
+ final int overlayPosition = getOverlayPosition(overlayBounds, taskBounds);
+
+ assertWithMessage("The overlay position must be "
+ + positionToString(params.mPosition) + ", but is "
+ + positionToString(overlayPosition)
+ + ", parent bounds=" + taskBounds + ", overlay bounds=" + overlayBounds)
+ .that(overlayPosition).isEqualTo(params.mPosition);
+ }
+
+ private enum OverlayPositionTestParams {
+ LEFT_OVERLAY(CONTAINER_POSITION_LEFT, false /* shouldBeShrunk */),
+ LEFT_SHRUNK_OVERLAY(CONTAINER_POSITION_LEFT, true /* shouldBeShrunk */),
+ TOP_OVERLAY(CONTAINER_POSITION_TOP, false /* shouldBeShrunk */),
+ TOP_SHRUNK_OVERLAY(CONTAINER_POSITION_TOP, true /* shouldBeShrunk */),
+ RIGHT_OVERLAY(CONTAINER_POSITION_RIGHT, false /* shouldBeShrunk */),
+ RIGHT_SHRUNK_OVERLAY(CONTAINER_POSITION_RIGHT, true /* shouldBeShrunk */),
+ BOTTOM_OVERLAY(CONTAINER_POSITION_BOTTOM, false /* shouldBeShrunk */),
+ BOTTOM_SHRUNK_OVERLAY(CONTAINER_POSITION_BOTTOM, true /* shouldBeShrunk */);
+
+ @SplitPresenter.ContainerPosition
+ private final int mPosition;
+
+ private final boolean mShouldBeShrunk;
+
+ OverlayPositionTestParams(
+ @SplitPresenter.ContainerPosition int position, boolean shouldBeShrunk) {
+ mPosition = position;
+ mShouldBeShrunk = shouldBeShrunk;
+ }
+
+ @NonNull
+ private Rect toOverlayBounds() {
+ Rect r = new Rect(TASK_BOUNDS);
+ final int offset = mShouldBeShrunk ? 20 : 0;
+ switch (mPosition) {
+ case CONTAINER_POSITION_LEFT:
+ r.top += offset;
+ r.right /= 2;
+ r.bottom -= offset;
+ break;
+ case CONTAINER_POSITION_TOP:
+ r.left += offset;
+ r.right -= offset;
+ r.bottom /= 2;
+ break;
+ case CONTAINER_POSITION_RIGHT:
+ r.left = r.right / 2;
+ r.top += offset;
+ r.bottom -= offset;
+ break;
+ case CONTAINER_POSITION_BOTTOM:
+ r.left += offset;
+ r.right -= offset;
+ r.top = r.bottom / 2;
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid position: " + mPosition);
+ }
+ return r;
+ }
}
/**
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index e6cb3a08119a..5135e9ee14bc 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -239,6 +239,9 @@ android_library {
"wmshell.protolog.json.gz",
"wmshell.protolog.pb",
],
+ flags_packages: [
+ "com_android_wm_shell_flags",
+ ],
kotlincflags: ["-Xjvm-default=all"],
manifest: "AndroidManifest.xml",
plugins: ["dagger2-compiler"],
diff --git a/libs/WindowManager/Shell/AndroidManifest.xml b/libs/WindowManager/Shell/AndroidManifest.xml
index 52ae93f5ebf1..bbbc23e8b922 100644
--- a/libs/WindowManager/Shell/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/AndroidManifest.xml
@@ -34,6 +34,7 @@
<activity
android:name=".bubbles.shortcut.CreateBubbleShortcutActivity"
+ android:featureFlag="com.android.wm.shell.enable_retrievable_bubbles"
android:exported="true"
android:excludeFromRecents="true"
android:theme="@android:style/Theme.NoDisplay"
@@ -47,6 +48,7 @@
<activity
android:name=".bubbles.shortcut.ShowBubblesActivity"
+ android:featureFlag="com.android.wm.shell.enable_retrievable_bubbles"
android:exported="true"
android:excludeFromRecents="true"
android:theme="@android:style/Theme.NoDisplay" >
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 f0d80a02243a..259101a9b84f 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
@@ -17,16 +17,20 @@
package com.android.wm.shell.shared.desktopmode
import android.content.Context
+import android.os.SystemProperties
import android.provider.Settings
import android.util.Log
import com.android.window.flags.Flags
-/*
- * A shared class to check desktop mode flags state.
+/**
+ * Util to check desktop mode flags state.
+ *
+ * This utility is used to allow developer option toggles to override flags related to Desktop
+ * Windowing.
*
- * The class computes whether a Desktop Windowing flag should be enabled by using the aconfig flag
+ * Computes whether Desktop Windowing related flags should be enabled by using the aconfig flag
* value and the developer option override state (if applicable).
- **/
+ */
enum class DesktopModeFlags(
// Function called to obtain aconfig flag value.
private val flagFunction: () -> Boolean,
@@ -67,37 +71,54 @@ enum class DesktopModeFlags(
// Cache toggle override the first time we encounter context. Override does not change
// with context, as context is just used to fetch System Property and Settings.Global
cachedToggleOverride = override
- Log.d(TAG, "Toggle override initialized to: $override")
+ Log.d(TAG, "Local toggle override initialized to: $override")
override
}
return override
}
+ /**
+ * Returns [ToggleOverride] from a non-persistent system property if present. Otherwise
+ * initializes the system property by reading Settings.Global.
+ */
private fun getToggleOverrideFromSystem(context: Context): ToggleOverride {
// A non-persistent System Property is used to store override to ensure it remains
// constant till reboot.
val overrideFromSystemProperties: ToggleOverride? =
- System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, null).convertToToggleOverride()
+ SystemProperties.getInt(SYSTEM_PROPERTY_OVERRIDE_KEY, INVALID_TOGGLE_OVERRIDE_SETTING)
+ .convertToToggleOverride()
+
return overrideFromSystemProperties
?: run {
- // Read Setting Global if System Property is not present (just after reboot)
- // or not valid (user manually changed the value)
- val overrideFromSettingsGlobal =
- convertToToggleOverrideWithFallback(
- Settings.Global.getInt(
- context.contentResolver,
- Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES,
- ToggleOverride.OVERRIDE_UNSET.setting),
- ToggleOverride.OVERRIDE_UNSET)
- // Initialize System Property
- System.setProperty(
- SYSTEM_PROPERTY_OVERRIDE_KEY, overrideFromSettingsGlobal.setting.toString())
-
- overrideFromSettingsGlobal
+ // TODO(b/348193756): Remove the initialization after adding it in core. If
+ // SystemProperty is still not present then return OVERRIDE_UNSET
+ // Initialize System Property if not present (just after reboot)
+ initializeOverrideInSystemProperty(context)
}
}
+ /** Initializes Override System property based on Settings.Global set by toggle. */
+ private fun initializeOverrideInSystemProperty(context: Context): ToggleOverride {
+ val settingValue =
+ Settings.Global.getInt(
+ context.contentResolver,
+ Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES,
+ ToggleOverride.OVERRIDE_UNSET.setting)
+ try {
+ SystemProperties.set(SYSTEM_PROPERTY_OVERRIDE_KEY, settingValue.toString())
+ Log.d(TAG, "Initialized system property with override setting: $settingValue")
+ } catch (e: RuntimeException) {
+ // Thrown in device tests that don't mock SystemProperties
+ Log.w(
+ TAG,
+ "Failed to set a system property: key=$SYSTEM_PROPERTY_OVERRIDE_KEY, " +
+ "value=$settingValue, message=${e.message}")
+ }
+
+ return convertToToggleOverrideWithFallback(settingValue, ToggleOverride.OVERRIDE_UNSET)
+ }
+
/**
* Override state of desktop mode developer option toggle.
*
@@ -113,11 +134,10 @@ enum class DesktopModeFlags(
OVERRIDE_ON(1)
}
- private fun String?.convertToToggleOverride(): ToggleOverride? {
- val intValue = this?.toIntOrNull() ?: return null
- return settingToToggleOverrideMap[intValue]
+ private fun Int.convertToToggleOverride(): ToggleOverride? {
+ return settingToToggleOverrideMap[this]
?: run {
- Log.w(TAG, "Unknown toggleOverride int $intValue")
+ Log.w(TAG, "Unknown toggleOverride int $this")
null
}
}
@@ -131,6 +151,8 @@ enum class DesktopModeFlags(
*/
private const val SYSTEM_PROPERTY_OVERRIDE_KEY = "sys.wmshell.desktopmode.dev_toggle_override"
+ private const val INVALID_TOGGLE_OVERRIDE_SETTING = -2
+
/**
* Local cache for toggle override, which is initialized once on its first access. It needs to
* be refreshed only on reboots as overridden state takes effect on reboots.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index f7a5c271a729..d4d9d003bc0d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -16,7 +16,7 @@
package com.android.wm.shell.bubbles;
-import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED;
+import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
@@ -225,8 +225,7 @@ public class BubbleExpandedView extends LinearLayout {
options.setTaskAlwaysOnTop(true);
options.setLaunchedFromBubble(true);
options.setPendingIntentBackgroundActivityStartMode(
- MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
- options.setPendingIntentBackgroundActivityLaunchAllowedByPermission(true);
+ MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS);
Intent fillInIntent = new Intent();
// Apply flags to make behaviour match documentLaunchMode=always.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java
index c79d9c4942bf..5e2141aa639e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java
@@ -15,7 +15,7 @@
*/
package com.android.wm.shell.bubbles;
-import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED;
+import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
@@ -103,8 +103,7 @@ public class BubbleTaskViewHelper {
options.setTaskAlwaysOnTop(true);
options.setLaunchedFromBubble(true);
options.setPendingIntentBackgroundActivityStartMode(
- MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
- options.setPendingIntentBackgroundActivityLaunchAllowedByPermission(true);
+ MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS);
Intent fillInIntent = new Intent();
// Apply flags to make behaviour match documentLaunchMode=always.
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 e713af6a5311..80f6a637ba1c 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
@@ -211,6 +211,7 @@ public abstract class WMShellModule {
@ShellMainThread ShellExecutor mainExecutor,
@ShellMainThread Handler mainHandler,
@ShellMainThread Choreographer mainChoreographer,
+ @ShellBackgroundThread ShellExecutor bgExecutor,
ShellInit shellInit,
IWindowManager windowManager,
ShellCommandHandler shellCommandHandler,
@@ -229,6 +230,7 @@ public abstract class WMShellModule {
mainExecutor,
mainHandler,
mainChoreographer,
+ bgExecutor,
shellInit,
shellCommandHandler,
windowManager,
@@ -246,6 +248,7 @@ public abstract class WMShellModule {
context,
mainHandler,
mainExecutor,
+ bgExecutor,
mainChoreographer,
windowManager,
shellInit,
@@ -366,13 +369,14 @@ public abstract class WMShellModule {
Optional<WindowDecorViewModel> windowDecorViewModel,
Optional<DesktopTasksController> desktopTasksController,
MultiInstanceHelper multiInstanceHelper,
- @ShellMainThread ShellExecutor mainExecutor) {
+ @ShellMainThread ShellExecutor mainExecutor,
+ @ShellMainThread Handler mainHandler) {
return new SplitScreenController(context, shellInit, shellCommandHandler, shellController,
shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer, displayController,
displayImeController, displayInsetsController, dragAndDropController, transitions,
transactionPool, iconProvider, recentTasks, launchAdjacentController,
windowDecorViewModel, desktopTasksController, null /* stageCoordinator */,
- multiInstanceHelper, mainExecutor);
+ multiInstanceHelper, mainExecutor, mainHandler);
}
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
index 066b5ad39d0f..73aa7ceea68d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
@@ -347,7 +347,7 @@ class DesktopModeLoggerTransitionObserver(
else -> {
ProtoLog.w(
WM_SHELL_DESKTOP_MODE,
- "Unknown enter reason for transition type ${transitionInfo.type}",
+ "Unknown enter reason for transition type: %s",
transitionInfo.type
)
EnterReason.UNKNOWN_ENTER
@@ -368,7 +368,7 @@ class DesktopModeLoggerTransitionObserver(
else -> {
ProtoLog.w(
WM_SHELL_DESKTOP_MODE,
- "Unknown exit reason for transition type ${transitionInfo.type}",
+ "Unknown exit reason for transition type: %s",
transitionInfo.type
)
ExitReason.UNKNOWN_EXIT
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 1bf125938e6f..da212e704b24 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
@@ -66,7 +66,7 @@ fun calculateInitialBounds(
idealSize
}
} else {
- maximumSizeMaintainingAspectRatio(taskInfo, idealSize, appAspectRatio)
+ maximizeSizeGivenAspectRatio(taskInfo, idealSize, appAspectRatio)
}
}
ORIENTATION_PORTRAIT -> {
@@ -85,13 +85,13 @@ fun calculateInitialBounds(
} else {
if (isFixedOrientationLandscape(topActivityInfo.screenOrientation)) {
// Apply custom app width and calculate maximum size
- maximumSizeMaintainingAspectRatio(
+ maximizeSizeGivenAspectRatio(
taskInfo,
Size(customPortraitWidthForLandscapeApp, idealSize.height),
appAspectRatio
)
} else {
- maximumSizeMaintainingAspectRatio(taskInfo, idealSize, appAspectRatio)
+ maximizeSizeGivenAspectRatio(taskInfo, idealSize, appAspectRatio)
}
}
}
@@ -107,7 +107,7 @@ fun calculateInitialBounds(
* Calculates the largest size that can fit in a given area while maintaining a specific aspect
* ratio.
*/
-fun maximumSizeMaintainingAspectRatio(
+fun maximizeSizeGivenAspectRatio(
taskInfo: RunningTaskInfo,
targetArea: Size,
aspectRatio: Float
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 9e6099f2e4cc..de901b5dabd8 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
@@ -69,6 +69,7 @@ import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOT
import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT
import com.android.wm.shell.compatui.isTopActivityExemptFromDesktopWindowing
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository.VisibleTasksListener
+import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType
import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler.DragToDesktopStateListener
import com.android.wm.shell.draganddrop.DragAndDropController
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
@@ -671,7 +672,7 @@ class DesktopTasksController(
} else {
// if non-resizable then calculate max bounds according to aspect ratio
val activityAspectRatio = calculateAspectRatio(taskInfo)
- val newSize = maximumSizeMaintainingAspectRatio(taskInfo,
+ val newSize = maximizeSizeGivenAspectRatio(taskInfo,
Size(stableBounds.width(), stableBounds.height()), activityAspectRatio)
val newBounds = centerInArea(
newSize, stableBounds, stableBounds.left, stableBounds.top)
@@ -818,9 +819,8 @@ class DesktopTasksController(
val intent = Intent(context, DesktopWallpaperActivity::class.java)
val options =
ActivityOptions.makeBasic().apply {
- isPendingIntentBackgroundActivityLaunchAllowedByPermission = true
pendingIntentBackgroundActivityStartMode =
- ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS
}
val pendingIntent =
PendingIntent.getActivity(
@@ -1080,7 +1080,6 @@ class DesktopTasksController(
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 =
@@ -1090,9 +1089,6 @@ class DesktopTasksController(
} else {
WINDOWING_MODE_FREEFORM
}
- if (Flags.enableWindowingDynamicInitialBounds()) {
- wct.setBounds(taskInfo.token, calculateInitialBounds(displayLayout, taskInfo))
- }
wct.setWindowingMode(taskInfo.token, targetWindowingMode)
wct.reorder(taskInfo.token, true /* onTop */)
if (useDesktopOverrideDensity()) {
@@ -1339,33 +1335,36 @@ class DesktopTasksController(
*
* @param taskInfo the task being dragged.
* @param y height of drag, to be checked against status bar height.
+ * @return the [IndicatorType] used for the resulting transition
*/
fun onDragPositioningEndThroughStatusBar(
inputCoordinates: PointF,
taskInfo: RunningTaskInfo,
- ) {
- val indicator = getVisualIndicator() ?: return
+ ): IndicatorType {
+ val indicator = getVisualIndicator() ?: return IndicatorType.NO_INDICATOR
val indicatorType = indicator.updateIndicatorType(inputCoordinates, taskInfo.windowingMode)
when (indicatorType) {
- DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR -> {
- val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return
+ IndicatorType.TO_DESKTOP_INDICATOR -> {
+ val displayLayout = displayController.getDisplayLayout(taskInfo.displayId)
+ ?: return IndicatorType.NO_INDICATOR
if (Flags.enableWindowingDynamicInitialBounds()) {
finalizeDragToDesktop(taskInfo, calculateInitialBounds(displayLayout, taskInfo))
} else {
finalizeDragToDesktop(taskInfo, getDefaultDesktopTaskBounds(displayLayout))
}
}
- DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR,
- DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR -> {
+ IndicatorType.NO_INDICATOR,
+ IndicatorType.TO_FULLSCREEN_INDICATOR -> {
cancelDragToDesktop(taskInfo)
}
- DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR -> {
+ IndicatorType.TO_SPLIT_LEFT_INDICATOR -> {
requestSplit(taskInfo, leftOrTop = true)
}
- DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR -> {
+ IndicatorType.TO_SPLIT_RIGHT_INDICATOR -> {
requestSplit(taskInfo, leftOrTop = false)
}
}
+ return indicatorType
}
/** Update the exclusion region for a specified task */
@@ -1426,7 +1425,6 @@ class DesktopTasksController(
setPendingIntentBackgroundActivityStartMode(
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED
)
- isPendingIntentBackgroundActivityLaunchAllowedByPermission = true
}
val wct = WindowContainerTransaction()
wct.sendPendingIntent(launchIntent, null, opts.toBundle())
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
index 95fe8b6f1f4e..7e0362475f21 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
@@ -16,7 +16,7 @@
package com.android.wm.shell.draganddrop;
-import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED;
+import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS;
import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -280,8 +280,7 @@ public class DragAndDropPolicy {
baseActivityOpts.setDisallowEnterPictureInPictureWhileLaunching(true);
// Put BAL flags to avoid activity start aborted.
baseActivityOpts.setPendingIntentBackgroundActivityStartMode(
- MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
- baseActivityOpts.setPendingIntentBackgroundActivityLaunchAllowedByPermission(true);
+ MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS);
final Bundle opts = baseActivityOpts.toBundle();
if (session.appData.hasExtra(EXTRA_ACTIVITY_OPTIONS)) {
opts.putAll(session.appData.getBundleExtra(EXTRA_ACTIVITY_OPTIONS));
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index e5633de2a3a2..1a3201822c00 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -1173,7 +1173,12 @@ public class PipTransition extends PipTransitionController {
.setLayer(swipePipToHomeOverlay, Integer.MAX_VALUE);
}
- final Rect sourceBounds = pipTaskInfo.configuration.windowConfiguration.getBounds();
+ // Both Shell and Launcher calculate their own "adjusted" source-rect-hint values based on
+ // appBounds being source bounds when entering PiP.
+ final Rect sourceBounds = swipePipToHomeOverlay == null
+ ? pipTaskInfo.configuration.windowConfiguration.getBounds()
+ : mPipOrganizer.mAppBounds;
+
final PipAnimationController.PipTransitionAnimator animator =
mPipAnimationController.getAnimator(pipTaskInfo, leash, sourceBounds, sourceBounds,
destinationBounds, sourceHintRect, TRANSITION_DIRECTION_TO_PIP,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
index 8df287d12cbc..06c57bd7092d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
@@ -113,6 +113,9 @@ public interface SplitScreen {
/** Called when device waking up finished. */
void onFinishedWakingUp();
+ /** Called when device starts going to sleep (screen off). */
+ void onStartedGoingToSleep();
+
/** Called when requested to go to fullscreen from the current active split app. */
void goToFullscreenFromSplit();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index e659151fee7f..b8575565ef8a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -50,6 +50,7 @@ import android.content.pm.LauncherApps;
import android.content.pm.ShortcutInfo;
import android.graphics.Rect;
import android.os.Bundle;
+import android.os.Handler;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.ArrayMap;
@@ -180,6 +181,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
private final LauncherApps mLauncherApps;
private final RootTaskDisplayAreaOrganizer mRootTDAOrganizer;
private final ShellExecutor mMainExecutor;
+ private final Handler mMainHandler;
private final SplitScreenImpl mImpl = new SplitScreenImpl();
private final DisplayController mDisplayController;
private final DisplayImeController mDisplayImeController;
@@ -227,7 +229,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
Optional<DesktopTasksController> desktopTasksController,
@Nullable StageCoordinator stageCoordinator,
MultiInstanceHelper multiInstanceHelper,
- ShellExecutor mainExecutor) {
+ ShellExecutor mainExecutor,
+ Handler mainHandler) {
mShellCommandHandler = shellCommandHandler;
mShellController = shellController;
mTaskOrganizer = shellTaskOrganizer;
@@ -236,6 +239,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
mLauncherApps = context.getSystemService(LauncherApps.class);
mRootTDAOrganizer = rootTDAOrganizer;
mMainExecutor = mainExecutor;
+ mMainHandler = mainHandler;
mDisplayController = displayController;
mDisplayImeController = displayImeController;
mDisplayInsetsController = displayInsetsController;
@@ -292,7 +296,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
return new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
mTaskOrganizer, mDisplayController, mDisplayImeController,
mDisplayInsetsController, mTransitions, mTransactionPool, mIconProvider,
- mMainExecutor, mRecentTasksOptional, mLaunchAdjacentController,
+ mMainExecutor, mMainHandler, mRecentTasksOptional, mLaunchAdjacentController,
mWindowDecorViewModel);
}
@@ -448,13 +452,17 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
@Override
public void onKeyguardVisibilityChanged(boolean visible, boolean occluded,
boolean animatingDismiss) {
- mStageCoordinator.onKeyguardVisibilityChanged(visible);
+ mStageCoordinator.onKeyguardStateChanged(visible, occluded);
}
public void onFinishedWakingUp() {
mStageCoordinator.onFinishedWakingUp();
}
+ public void onStartedGoingToSleep() {
+ mStageCoordinator.onStartedGoingToSleep();
+ }
+
public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
mStageCoordinator.exitSplitScreenOnHide(exitSplitScreenOnHide);
}
@@ -1201,6 +1209,11 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
}
@Override
+ public void onStartedGoingToSleep() {
+ mMainExecutor.execute(SplitScreenController.this::onStartedGoingToSleep);
+ }
+
+ @Override
public void goToFullscreenFromSplit() {
mMainExecutor.execute(SplitScreenController.this::goToFullscreenFromSplit);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 9bcd9b0a11c8..a4f32c45c0a9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -16,7 +16,7 @@
package com.android.wm.shell.splitscreen;
-import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED;
+import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
@@ -92,6 +92,7 @@ import android.graphics.Rect;
import android.hardware.devicestate.DeviceStateManager;
import android.os.Bundle;
import android.os.Debug;
+import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -119,6 +120,7 @@ import android.window.WindowContainerTransaction;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.InstanceId;
+import com.android.internal.policy.FoldLockSettingsObserver;
import com.android.internal.protolog.ProtoLog;
import com.android.internal.util.ArrayUtils;
import com.android.launcher3.icons.IconProvider;
@@ -191,7 +193,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private SplitLayout mSplitLayout;
private ValueAnimator mDividerFadeInAnimator;
private boolean mDividerVisible;
- private boolean mKeyguardShowing;
+ private boolean mKeyguardActive;
private boolean mShowDecorImmediately;
private final SyncTransactionQueue mSyncQueue;
private final ShellTaskOrganizer mTaskOrganizer;
@@ -205,6 +207,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private SplitScreenTransitions mSplitTransitions;
private final SplitscreenEventLogger mLogger;
private final ShellExecutor mMainExecutor;
+ private final Handler mMainHandler;
// Cache live tile tasks while entering recents, evict them from stages in finish transaction
// if user is opening another task(s).
private final ArrayList<Integer> mPausingTasks = new ArrayList<>();
@@ -233,7 +236,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private boolean mIsExiting;
private boolean mIsRootTranslucent;
@VisibleForTesting
- int mTopStageAfterFoldDismiss;
+ @StageType int mLastActiveStage;
+ private boolean mBreakOnNextWake;
+ /** Used to get the Settings value for "Continue using apps on fold". */
+ private FoldLockSettingsObserver mFoldLockSettingsObserver;
private DefaultMixedHandler mMixedHandler;
private final Toast mSplitUnsupportedToast;
@@ -313,9 +319,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
ShellTaskOrganizer taskOrganizer, DisplayController displayController,
DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController, Transitions transitions,
- TransactionPool transactionPool,
- IconProvider iconProvider, ShellExecutor mainExecutor,
- Optional<RecentTasksController> recentTasks,
+ TransactionPool transactionPool, IconProvider iconProvider, ShellExecutor mainExecutor,
+ Handler mainHandler, Optional<RecentTasksController> recentTasks,
LaunchAdjacentController launchAdjacentController,
Optional<WindowDecorViewModel> windowDecorViewModel) {
mContext = context;
@@ -324,6 +329,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mTaskOrganizer = taskOrganizer;
mLogger = new SplitscreenEventLogger();
mMainExecutor = mainExecutor;
+ mMainHandler = mainHandler;
mRecentTasks = recentTasks;
mLaunchAdjacentController = launchAdjacentController;
mWindowDecorViewModel = windowDecorViewModel;
@@ -366,6 +372,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
// With shell transition, we should update recents tile each callback so set this to true by
// default.
mShouldUpdateRecents = ENABLE_SHELL_TRANSITIONS;
+ mFoldLockSettingsObserver =
+ new FoldLockSettingsObserver(mainHandler, context);
+ mFoldLockSettingsObserver.register();
}
@VisibleForTesting
@@ -373,9 +382,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
ShellTaskOrganizer taskOrganizer, MainStage mainStage, SideStage sideStage,
DisplayController displayController, DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController, SplitLayout splitLayout,
- Transitions transitions, TransactionPool transactionPool,
- ShellExecutor mainExecutor,
- Optional<RecentTasksController> recentTasks,
+ Transitions transitions, TransactionPool transactionPool, ShellExecutor mainExecutor,
+ Handler mainHandler, Optional<RecentTasksController> recentTasks,
LaunchAdjacentController launchAdjacentController,
Optional<WindowDecorViewModel> windowDecorViewModel) {
mContext = context;
@@ -393,6 +401,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
this::onTransitionAnimationComplete, this);
mLogger = new SplitscreenEventLogger();
mMainExecutor = mainExecutor;
+ mMainHandler = mainHandler;
mRecentTasks = recentTasks;
mLaunchAdjacentController = launchAdjacentController;
mWindowDecorViewModel = windowDecorViewModel;
@@ -400,6 +409,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
transitions.addHandler(this);
mSplitUnsupportedToast = Toast.makeText(mContext,
R.string.dock_non_resizeble_failed_to_dock_text, Toast.LENGTH_SHORT);
+ mFoldLockSettingsObserver =
+ new FoldLockSettingsObserver(context.getMainThreadHandler(), context);
+ mFoldLockSettingsObserver.register();
}
public void setMixedHandler(DefaultMixedHandler mixedHandler) {
@@ -1504,51 +1516,80 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
}
- void onKeyguardVisibilityChanged(boolean showing) {
- mKeyguardShowing = showing;
+ /**
+ * Runs when keyguard state changes. The booleans here are a bit complicated, so for reference:
+ * @param active {@code true} if we are in a state where the keyguard *should* be shown
+ * -- still true when keyguard is "there" but is behind an app, or
+ * screen is off.
+ * @param occludingTaskRunning {@code true} when there is a running task that has
+ * FLAG_SHOW_WHEN_LOCKED -- also true when the task is
+ * just running on its own and keyguard is not active
+ * at all.
+ */
+ void onKeyguardStateChanged(boolean active, boolean occludingTaskRunning) {
+ mKeyguardActive = active;
if (!mMainStage.isActive()) {
return;
}
- ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onKeyguardVisibilityChanged: showing=%b", showing);
- setDividerVisibility(!mKeyguardShowing, null);
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+ "onKeyguardVisibilityChanged: active=%b occludingTaskRunning=%b",
+ active, occludingTaskRunning);
+ setDividerVisibility(!mKeyguardActive, null);
+
+ if (active && occludingTaskRunning) {
+ dismissSplitKeepingLastActiveStage(EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP);
+ }
}
void onFinishedWakingUp() {
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onFinishedWakingUp");
- if (!mMainStage.isActive()) {
+ if (mBreakOnNextWake) {
+ dismissSplitKeepingLastActiveStage(EXIT_REASON_DEVICE_FOLDED);
+ }
+ }
+
+ void onStartedGoingToSleep() {
+ recordLastActiveStage();
+ }
+
+ /**
+ * Records the user's last focused stage -- main stage or side stage. Used to determine which
+ * stage of a split pair should be kept, in cases where system focus has moved elsewhere.
+ */
+ void recordLastActiveStage() {
+ if (!isSplitActive() || !isSplitScreenVisible()) {
+ mLastActiveStage = STAGE_TYPE_UNDEFINED;
+ } else if (mMainStage.isFocused()) {
+ mLastActiveStage = STAGE_TYPE_MAIN;
+ } else if (mSideStage.isFocused()) {
+ mLastActiveStage = STAGE_TYPE_SIDE;
+ }
+ }
+
+ /**
+ * Dismisses split, keeping the app that the user focused last in split screen. If the user was
+ * not in split screen, {@link #mLastActiveStage} should be set to STAGE_TYPE_UNDEFINED, and we
+ * will do a no-op.
+ */
+ void dismissSplitKeepingLastActiveStage(@ExitReason int reason) {
+ if (!mMainStage.isActive() || mLastActiveStage == STAGE_TYPE_UNDEFINED) {
+ // no-op
return;
}
- // Check if there's only one stage visible while keyguard occluded.
- final boolean mainStageVisible = mMainStage.mRootTaskInfo.isVisible;
- final boolean oneStageVisible =
- mMainStage.mRootTaskInfo.isVisible != mSideStage.mRootTaskInfo.isVisible;
- if (oneStageVisible && !ENABLE_SHELL_TRANSITIONS) {
- // Dismiss split because there's show-when-locked activity showing on top of keyguard.
- // Also make sure the task contains show-when-locked activity remains on top after split
- // dismissed.
- final StageTaskListener toTop = mainStageVisible ? mMainStage : mSideStage;
- exitSplitScreen(toTop, EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP);
- }
-
- // Dismiss split if the flag record any side of stages.
- if (mTopStageAfterFoldDismiss != STAGE_TYPE_UNDEFINED) {
- if (ENABLE_SHELL_TRANSITIONS) {
- // Need manually clear here due to this transition might be aborted due to keyguard
- // on top and lead to no visible change.
- clearSplitPairedInRecents(EXIT_REASON_DEVICE_FOLDED);
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- prepareExitSplitScreen(mTopStageAfterFoldDismiss, wct);
- mSplitTransitions.startDismissTransition(wct, this,
- mTopStageAfterFoldDismiss, EXIT_REASON_DEVICE_FOLDED);
- setSplitsVisible(false);
- } else {
- exitSplitScreen(
- mTopStageAfterFoldDismiss == STAGE_TYPE_MAIN ? mMainStage : mSideStage,
- EXIT_REASON_DEVICE_FOLDED);
- }
- mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
+ if (ENABLE_SHELL_TRANSITIONS) {
+ // Need manually clear here due to this transition might be aborted due to keyguard
+ // on top and lead to no visible change.
+ clearSplitPairedInRecents(reason);
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ prepareExitSplitScreen(mLastActiveStage, wct);
+ mSplitTransitions.startDismissTransition(wct, this, mLastActiveStage, reason);
+ setSplitsVisible(false);
+ } else {
+ exitSplitScreen(mLastActiveStage == STAGE_TYPE_MAIN ? mMainStage : mSideStage, reason);
}
+
+ mBreakOnNextWake = false;
}
void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
@@ -1909,8 +1950,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
// Put BAL flags to avoid activity start aborted. Otherwise, flows like shortcut to split
// will be canceled.
- options.setPendingIntentBackgroundActivityStartMode(MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
- options.setPendingIntentBackgroundActivityLaunchAllowedByPermission(true);
+ options.setPendingIntentBackgroundActivityStartMode(
+ MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS);
// TODO (b/336477473): Disallow enter PiP when launching a task in split by default;
// this might have to be changed as more split-to-pip cujs are defined.
@@ -2223,11 +2264,11 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
"setDividerVisibility: visible=%b keyguardShowing=%b dividerAnimating=%b caller=%s",
- visible, mKeyguardShowing, mIsDividerRemoteAnimating, Debug.getCaller());
+ visible, mKeyguardActive, mIsDividerRemoteAnimating, Debug.getCaller());
// Defer showing divider bar after keyguard dismissed, so it won't interfere with keyguard
// dismissing animation.
- if (visible && mKeyguardShowing) {
+ if (visible && mKeyguardActive) {
ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
" Defer showing divider bar due to keyguard showing.");
return;
@@ -2597,21 +2638,24 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@VisibleForTesting
void onFoldedStateChanged(boolean folded) {
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onFoldedStateChanged: folded=%b", folded);
- mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
- if (!folded) return;
-
- if (!isSplitActive() || !isSplitScreenVisible()) return;
- // To avoid split dismiss when user fold the device and unfold to use later, we only
- // record the flag here and try to dismiss on wakeUp callback to ensure split dismiss
- // when user interact on phone folded.
- if (mMainStage.isFocused()) {
- mTopStageAfterFoldDismiss = STAGE_TYPE_MAIN;
- } else if (mSideStage.isFocused()) {
- mTopStageAfterFoldDismiss = STAGE_TYPE_SIDE;
+ if (folded) {
+ recordLastActiveStage();
+ // If user folds and has the setting "Continue using apps on fold = NEVER", we assume
+ // they don't want to continue using split on the outer screen (i.e. we break split if
+ // they wake the device in its folded state).
+ mBreakOnNextWake = willSleepOnFold();
+ } else {
+ mBreakOnNextWake = false;
}
}
+ /** Returns true if the phone will sleep when it folds. */
+ @VisibleForTesting
+ boolean willSleepOnFold() {
+ return mFoldLockSettingsObserver != null && mFoldLockSettingsObserver.isSleepOnFold();
+ }
+
private Rect getSideStageBounds() {
return mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT
? mSplitLayout.getBounds1() : mSplitLayout.getBounds2();
@@ -3739,8 +3783,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSplitTransitions.startDismissTransition(wct, StageCoordinator.this, stageType,
EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW);
Log.w(TAG, splitFailureMessage("onNoLongerSupportMultiWindow",
- "app package " + taskInfo.baseActivity.getPackageName()
- + " does not support splitscreen, or is a controlled activity type"));
+ "app package " + taskInfo.baseIntent.getComponent()
+ + " does not support splitscreen, or is a controlled activity"
+ + " type"));
if (splitScreenVisible) {
handleUnsupportedSplitStart();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
index e330f3ab65ab..b65e97899f3e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
@@ -33,7 +33,6 @@ import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.common.TransactionPool;
-import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.splitscreen.StageCoordinator;
@@ -88,7 +87,8 @@ public class TvSplitScreenController extends SplitScreenController {
syncQueue, rootTDAOrganizer, displayController, displayImeController,
displayInsetsController, null, transitions, transactionPool,
iconProvider, recentTasks, launchAdjacentController, Optional.empty(),
- Optional.empty(), null /* stageCoordinator */, multiInstanceHelper, mainExecutor);
+ Optional.empty(), null /* stageCoordinator */, multiInstanceHelper, mainExecutor,
+ mainHandler);
mTaskOrganizer = shellTaskOrganizer;
mSyncQueue = syncQueue;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java
index 79476919221e..81ca48fa6b3d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java
@@ -56,7 +56,7 @@ public class TvStageCoordinator extends StageCoordinator
SystemWindows systemWindows) {
super(context, displayId, syncQueue, taskOrganizer, displayController, displayImeController,
displayInsetsController, transitions, transactionPool, iconProvider,
- mainExecutor, recentTasks, launchAdjacentController, Optional.empty());
+ mainExecutor, mainHandler, recentTasks, launchAdjacentController, Optional.empty());
mTvSplitMenuController = new TvSplitMenuController(context, this,
systemWindows, mainHandler);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java
index 5c814dcc9b16..bad5baf24651 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java
@@ -73,7 +73,7 @@ class WindowlessSnapshotWindowCreator {
final Display display = mDisplayManager.getDisplay(runningTaskInfo.displayId);
final StartingSurfaceDrawer.WindowlessStartingWindow wlw =
new StartingSurfaceDrawer.WindowlessStartingWindow(
- runningTaskInfo.configuration, rootSurface);
+ mContext.getResources().getConfiguration(), rootSurface);
final SurfaceControlViewHost mViewHost = new SurfaceControlViewHost(
mContext, display, wlw, "WindowlessSnapshotWindowCreator");
final Rect windowBounds = runningTaskInfo.configuration.windowConfiguration.getBounds();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSplashWindowCreator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSplashWindowCreator.java
index 98a803128587..f3725579bf48 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSplashWindowCreator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSplashWindowCreator.java
@@ -76,7 +76,7 @@ class WindowlessSplashWindowCreator extends AbsSplashWindowCreator {
}
final StartingSurfaceDrawer.WindowlessStartingWindow wlw =
new StartingSurfaceDrawer.WindowlessStartingWindow(
- taskInfo.configuration, rootSurface);
+ mContext.getResources().getConfiguration(), rootSurface);
final SurfaceControlViewHost viewHost = new SurfaceControlViewHost(
myContext, display, wlw, "WindowlessSplashWindowCreator");
final String title = "Windowless Splash " + taskInfo.taskId;
@@ -95,7 +95,7 @@ class WindowlessSplashWindowCreator extends AbsSplashWindowCreator {
}
final FrameLayout rootLayout = new FrameLayout(
- mSplashscreenContentDrawer.createViewContextWrapper(mContext));
+ mSplashscreenContentDrawer.createViewContextWrapper(myContext));
viewHost.setView(rootLayout, lp);
final int bgColor = taskDescription.getBackgroundColor();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInit.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInit.java
index dd4595a70211..287e779d8e24 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInit.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInit.java
@@ -48,6 +48,7 @@ public class ShellInit {
public ShellInit(ShellExecutor mainExecutor) {
mMainExecutor = mainExecutor;
+ ProtoLog.registerGroups(ShellProtoLogGroup.values());
}
/**
@@ -76,7 +77,6 @@ public class ShellInit {
*/
@VisibleForTesting
public void init() {
- ProtoLog.registerGroups(ShellProtoLogGroup.values());
ProtoLog.v(WM_SHELL_INIT, "Initializing Shell Components: %d", mInitCallbacks.size());
SurfaceControl.setDebugUsageAfterRelease(true);
// Init in order of registration
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
index b9cb6d3d5007..5c230c0a505c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -56,6 +56,7 @@ import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
+import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
@@ -72,6 +73,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
private final IWindowManager mWindowManager;
private final Context mContext;
private final Handler mMainHandler;
+ private final @ShellBackgroundThread ShellExecutor mBgExecutor;
private final ShellExecutor mMainExecutor;
private final Choreographer mMainChoreographer;
private final DisplayController mDisplayController;
@@ -108,6 +110,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
public CaptionWindowDecorViewModel(
Context context,
Handler mainHandler,
+ @ShellBackgroundThread ShellExecutor bgExecutor,
ShellExecutor shellExecutor,
Choreographer mainChoreographer,
IWindowManager windowManager,
@@ -120,6 +123,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
mContext = context;
mMainExecutor = shellExecutor;
mMainHandler = mainHandler;
+ mBgExecutor = bgExecutor;
mWindowManager = windowManager;
mMainChoreographer = mainChoreographer;
mTaskOrganizer = taskOrganizer;
@@ -289,6 +293,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
taskInfo,
taskSurface,
mMainHandler,
+ mBgExecutor,
mMainChoreographer,
mSyncQueue);
mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
index 7e1b973a98f4..cf42a49f7103 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -21,6 +21,7 @@ import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getLarge
import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getResizeEdgeHandleSize;
import android.annotation.NonNull;
+import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.WindowConfiguration;
@@ -48,7 +49,9 @@ import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
import com.android.wm.shell.windowdecor.extension.TaskInfoKt;
/**
@@ -58,6 +61,7 @@ import com.android.wm.shell.windowdecor.extension.TaskInfoKt;
*/
public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearLayout> {
private final Handler mHandler;
+ private final @ShellBackgroundThread ShellExecutor mBgExecutor;
private final Choreographer mChoreographer;
private final SyncTransactionQueue mSyncQueue;
@@ -78,10 +82,12 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
Handler handler,
+ @ShellBackgroundThread ShellExecutor bgExecutor,
Choreographer choreographer,
SyncTransactionQueue syncQueue) {
super(context, displayController, taskOrganizer, taskInfo, taskSurface);
mHandler = handler;
+ mBgExecutor = bgExecutor;
mChoreographer = choreographer;
mSyncQueue = syncQueue;
}
@@ -218,6 +224,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
relayoutParams.mOccludingCaptionElements.add(controlsElement);
}
+ @SuppressLint("MissingPermission")
void relayout(RunningTaskInfo taskInfo,
SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
boolean applyStartTransactionOnDraw, boolean setTaskCropAndPosition) {
@@ -235,7 +242,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
relayout(mRelayoutParams, startT, finishT, wct, oldRootView, mResult);
// After this line, mTaskInfo is up-to-date and should be used instead of taskInfo
- mTaskOrganizer.applyTransaction(wct);
+ mBgExecutor.execute(() -> mTaskOrganizer.applyTransaction(wct));
if (mResult.mRootView == null) {
// This means something blocks the window decor from showing, e.g. the task is hidden.
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 53976251e31d..8312aef110b9 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
@@ -22,6 +22,8 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.content.Intent.ACTION_MAIN;
+import static android.content.Intent.CATEGORY_APP_BROWSER;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.view.InputDevice.SOURCE_TOUCHSCREEN;
import static android.view.MotionEvent.ACTION_CANCEL;
@@ -36,6 +38,8 @@ import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSIT
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.compatui.AppCompatUtils.isTopActivityExemptFromDesktopWindowing;
import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR;
+import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR;
+import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
@@ -43,11 +47,8 @@ import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityTaskManager;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
@@ -98,6 +99,7 @@ import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition;
import com.android.wm.shell.desktopmode.DesktopWallpaperActivity;
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
+import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.splitscreen.SplitScreen.StageType;
@@ -132,6 +134,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
private final ShellController mShellController;
private final Context mContext;
private final Handler mMainHandler;
+ private final @ShellBackgroundThread ShellExecutor mBgExecutor;
private final Choreographer mMainChoreographer;
private final DisplayController mDisplayController;
private final SyncTransactionQueue mSyncQueue;
@@ -183,6 +186,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
ShellExecutor shellExecutor,
Handler mainHandler,
Choreographer mainChoreographer,
+ @ShellBackgroundThread ShellExecutor bgExecutor,
ShellInit shellInit,
ShellCommandHandler shellCommandHandler,
IWindowManager windowManager,
@@ -201,6 +205,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
shellExecutor,
mainHandler,
mainChoreographer,
+ bgExecutor,
shellInit,
shellCommandHandler,
windowManager,
@@ -225,6 +230,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
ShellExecutor shellExecutor,
Handler mainHandler,
Choreographer mainChoreographer,
+ @ShellBackgroundThread ShellExecutor bgExecutor,
ShellInit shellInit,
ShellCommandHandler shellCommandHandler,
IWindowManager windowManager,
@@ -245,6 +251,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
mMainExecutor = shellExecutor;
mMainHandler = mainHandler;
mMainChoreographer = mainChoreographer;
+ mBgExecutor = bgExecutor;
mActivityTaskManager = mContext.getSystemService(ActivityTaskManager.class);
mTaskOrganizer = taskOrganizer;
mShellController = shellController;
@@ -321,7 +328,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
if (decoration == null) return;
final RunningTaskInfo oldTaskInfo = decoration.mTaskInfo;
- if (taskInfo.displayId != oldTaskInfo.displayId) {
+ if (taskInfo.displayId != oldTaskInfo.displayId
+ && !Flags.enableAdditionalWindowsAboveStatusBar()) {
removeTaskFromEventReceiver(oldTaskInfo.displayId);
incrementEventReceiverTasks(taskInfo.displayId);
}
@@ -385,7 +393,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
decoration.close();
final int displayId = taskInfo.displayId;
- if (mEventReceiversByDisplay.contains(displayId)) {
+ if (mEventReceiversByDisplay.contains(displayId)
+ && !Flags.enableAdditionalWindowsAboveStatusBar()) {
removeTaskFromEventReceiver(displayId);
}
// Remove the decoration from the cache last because WindowDecoration#close could still
@@ -424,19 +433,12 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
}
private void openInBrowser(Uri uri) {
- final Intent intent = new Intent(Intent.ACTION_VIEW, uri)
- .setComponent(getDefaultBrowser())
+ final Intent intent = Intent.makeMainSelectorActivity(ACTION_MAIN, CATEGORY_APP_BROWSER)
+ .setData(uri)
.addFlags(FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);
}
- private ComponentName getDefaultBrowser() {
- final Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://"));
- final ResolveInfo info = mContext.getPackageManager()
- .resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
- return info.getComponentInfo().getComponentName();
- }
-
private class DesktopModeTouchEventListener extends GestureDetector.SimpleOnGestureListener
implements View.OnClickListener, View.OnTouchListener, View.OnLongClickListener,
View.OnGenericMotionListener, DragDetector.MotionEventHandler {
@@ -517,6 +519,9 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
}
} else if (id == R.id.split_screen_button) {
decoration.closeHandleMenu();
+ // When the app enters split-select, the handle will no longer be visible, meaning
+ // we shouldn't receive input for it any longer.
+ decoration.disposeStatusBarInputLayer();
mDesktopTasksController.requestSplit(decoration.mTaskInfo);
} else if (id == R.id.open_in_browser_button) {
// TODO(b/346441962): let the decoration handle the click gesture and only call back
@@ -653,13 +658,38 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId);
final RunningTaskInfo taskInfo = decoration.mTaskInfo;
if (DesktopModeStatus.canEnterDesktopMode(mContext)
- && taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
- return false;
+ && !taskInfo.isFreeform()) {
+ return handleNonFreeformMotionEvent(decoration, v, e);
+ } else {
+ return handleFreeformMotionEvent(decoration, taskInfo, v, e);
}
+ }
+
+ private boolean handleNonFreeformMotionEvent(DesktopModeWindowDecoration decoration,
+ View v, MotionEvent e) {
+ final int id = v.getId();
+ if (id == R.id.caption_handle) {
+ if (e.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ // Caption handle is located within the status bar region, meaning the
+ // DisplayPolicy will attempt to transfer this input to status bar if it's
+ // a swipe down. Pilfer here to keep the gesture in handle alone.
+ mInputManager.pilferPointers(v.getViewRootImpl().getInputToken());
+ }
+ handleCaptionThroughStatusBar(e, decoration);
+ final boolean wasDragging = mIsDragging;
+ updateDragStatus(e.getActionMasked());
+ // Only prevent onClick from receiving this event if it's a drag.
+ return wasDragging;
+ }
+ return false;
+ }
+
+ private boolean handleFreeformMotionEvent(DesktopModeWindowDecoration decoration,
+ RunningTaskInfo taskInfo, View v, MotionEvent e) {
+ final int id = v.getId();
if (mGestureDetector.onTouchEvent(e)) {
return true;
}
- final int id = v.getId();
final boolean touchingButton = (id == R.id.close_window || id == R.id.maximize_window
|| id == R.id.open_menu_button);
switch (e.getActionMasked()) {
@@ -668,7 +698,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
mDragPositioningCallback.onDragPositioningStart(
0 /* ctrlType */, e.getRawX(0),
e.getRawY(0));
- mIsDragging = false;
+ updateDragStatus(e.getActionMasked());
mHasLongClicked = false;
// Do not consume input event if a button is touched, otherwise it would
// prevent the button's ripple effect from showing.
@@ -688,7 +718,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
decoration.mTaskSurface,
e.getRawX(dragPointerIdx),
newTaskBounds);
- mIsDragging = true;
+ updateDragStatus(e.getActionMasked());
return true;
}
case MotionEvent.ACTION_UP:
@@ -718,7 +748,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
// onClick call that results.
return false;
} else {
- mIsDragging = false;
+ updateDragStatus(e.getActionMasked());
return true;
}
}
@@ -726,6 +756,21 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
return true;
}
+ private void updateDragStatus(int eventAction) {
+ switch (eventAction) {
+ case MotionEvent.ACTION_DOWN:
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL: {
+ mIsDragging = false;
+ break;
+ }
+ case MotionEvent.ACTION_MOVE: {
+ mIsDragging = true;
+ break;
+ }
+ }
+ }
+
/**
* Perform a task size toggle on release of the double-tap, assuming no drag event
* was handled during the double-tap.
@@ -850,6 +895,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
*
* @param relevantDecor the window decoration of the focused task's caption. This method only
* handles motion events outside this caption's bounds.
+ * TODO(b/349135068): Outside-touch detection no longer works with the
+ * enableAdditionalWindowsAboveStatusBar flag enabled. This
+ * will be fixed once we can add FLAG_WATCH_OUTSIDE_TOUCH to relevant menus,
+ * at which point, all EventReceivers and external touch logic should be removed.
*/
private void handleEventOutsideCaption(MotionEvent ev,
DesktopModeWindowDecoration relevantDecor) {
@@ -902,9 +951,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
dragFromStatusBarAllowed = windowingMode == WINDOWING_MODE_FULLSCREEN
|| windowingMode == WINDOWING_MODE_MULTI_WINDOW;
}
-
- if (dragFromStatusBarAllowed
- && relevantDecor.checkTouchEventInFocusedCaptionHandle(ev)) {
+ final boolean shouldStartTransitionDrag =
+ relevantDecor.checkTouchEventInFocusedCaptionHandle(ev)
+ || Flags.enableAdditionalWindowsAboveStatusBar();
+ if (dragFromStatusBarAllowed && shouldStartTransitionDrag) {
mTransitionDragActive = true;
}
break;
@@ -918,8 +968,15 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
// Though this isn't a hover event, we need to update handle's hover state
// as it likely will change.
relevantDecor.updateHoverAndPressStatus(ev);
- mDesktopTasksController.onDragPositioningEndThroughStatusBar(
+ DesktopModeVisualIndicator.IndicatorType resultType =
+ mDesktopTasksController.onDragPositioningEndThroughStatusBar(
new PointF(ev.getRawX(), ev.getRawY()), relevantDecor.mTaskInfo);
+ // If we are entering split select, handle will no longer be visible and
+ // should not be receiving any input.
+ if (resultType == TO_SPLIT_LEFT_INDICATOR
+ || resultType != TO_SPLIT_RIGHT_INDICATOR) {
+ relevantDecor.disposeStatusBarInputLayer();
+ }
mMoveToDesktopAnimator = null;
return;
} else {
@@ -931,7 +988,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
relevantDecor.checkTouchEvent(ev);
break;
}
-
case ACTION_MOVE: {
if (relevantDecor == null) {
return;
@@ -1091,10 +1147,12 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
mDesktopModeWindowDecorFactory.create(
mContext,
mDisplayController,
+ mSplitScreenController,
mTaskOrganizer,
taskInfo,
taskSurface,
mMainHandler,
+ mBgExecutor,
mMainChoreographer,
mSyncQueue,
mRootTaskDisplayAreaOrganizer);
@@ -1132,7 +1190,9 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
windowDecoration.setDragDetector(touchEventListener.mDragDetector);
windowDecoration.relayout(taskInfo, startT, finishT,
false /* applyStartTransactionOnDraw */, false /* shouldSetTaskPositionAndCrop */);
- incrementEventReceiverTasks(taskInfo.displayId);
+ if (!Flags.enableAdditionalWindowsAboveStatusBar()) {
+ incrementEventReceiverTasks(taskInfo.displayId);
+ }
}
private RunningTaskInfo getOtherSplitTask(int taskId) {
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 5d662b20ebb9..529def7ca3d7 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
@@ -24,11 +24,13 @@ import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_UP;
import static com.android.launcher3.icons.BaseIconFactory.MODE_DEFAULT;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getFineResizeCornerSize;
import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getLargeResizeCornerSize;
import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getResizeEdgeHandleSize;
import android.annotation.NonNull;
+import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.WindowConfiguration.WindowingMode;
import android.content.ComponentName;
@@ -68,7 +70,9 @@ import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.windowdecor.common.OnTaskActionClickListener;
@@ -95,8 +99,10 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
static final long CLOSE_MAXIMIZE_MENU_DELAY_MS = 150L;
private final Handler mHandler;
+ private final @ShellBackgroundThread ShellExecutor mBgExecutor;
private final Choreographer mChoreographer;
private final SyncTransactionQueue mSyncQueue;
+ private final SplitScreenController mSplitScreenController;
private WindowDecorationViewHolder mWindowDecorViewHolder;
private View.OnClickListener mOnCaptionButtonClickListener;
@@ -147,27 +153,32 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
DesktopModeWindowDecoration(
Context context,
DisplayController displayController,
+ SplitScreenController splitScreenController,
ShellTaskOrganizer taskOrganizer,
ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
Handler handler,
+ @ShellBackgroundThread ShellExecutor bgExecutor,
Choreographer choreographer,
SyncTransactionQueue syncQueue,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
- this (context, displayController, taskOrganizer, taskInfo, taskSurface,
- handler, choreographer, syncQueue, rootTaskDisplayAreaOrganizer,
- SurfaceControl.Builder::new, SurfaceControl.Transaction::new,
- WindowContainerTransaction::new, SurfaceControl::new,
- new SurfaceControlViewHostFactory() {}, DefaultMaximizeMenuFactory.INSTANCE);
+ this (context, displayController, splitScreenController, taskOrganizer, taskInfo,
+ taskSurface, handler, bgExecutor, choreographer, syncQueue,
+ rootTaskDisplayAreaOrganizer, SurfaceControl.Builder::new,
+ SurfaceControl.Transaction::new, WindowContainerTransaction::new,
+ SurfaceControl::new, new SurfaceControlViewHostFactory() {},
+ DefaultMaximizeMenuFactory.INSTANCE);
}
DesktopModeWindowDecoration(
Context context,
DisplayController displayController,
+ SplitScreenController splitScreenController,
ShellTaskOrganizer taskOrganizer,
ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
Handler handler,
+ @ShellBackgroundThread ShellExecutor bgExecutor,
Choreographer choreographer,
SyncTransactionQueue syncQueue,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
@@ -181,7 +192,9 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
surfaceControlBuilderSupplier, surfaceControlTransactionSupplier,
windowContainerTransactionSupplier, surfaceControlSupplier,
surfaceControlViewHostFactory);
+ mSplitScreenController = splitScreenController;
mHandler = handler;
+ mBgExecutor = bgExecutor;
mChoreographer = choreographer;
mSyncQueue = syncQueue;
mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
@@ -327,6 +340,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
mHandler.post(mCurrentViewHostRunnable);
}
+ @SuppressLint("MissingPermission")
private void updateRelayoutParamsAndSurfaces(ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop) {
@@ -337,7 +351,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
}
if (isHandleMenuActive()) {
- mHandleMenu.relayout(startT);
+ mHandleMenu.relayout(startT, mResult.mCaptionX);
}
updateRelayoutParams(mRelayoutParams, mContext, taskInfo, applyStartTransactionOnDraw,
@@ -353,33 +367,47 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
// After this line, mTaskInfo is up-to-date and should be used instead of taskInfo
Trace.beginSection("DesktopModeWindowDecoration#relayout-applyWCT");
- mTaskOrganizer.applyTransaction(wct);
+ mBgExecutor.execute(() -> mTaskOrganizer.applyTransaction(wct));
Trace.endSection();
if (mResult.mRootView == null) {
// This means something blocks the window decor from showing, e.g. the task is hidden.
// Nothing is set up in this case including the decoration surface.
+ disposeStatusBarInputLayer();
Trace.endSection(); // DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces
return;
}
if (oldRootView != mResult.mRootView) {
+ disposeStatusBarInputLayer();
mWindowDecorViewHolder = createViewHolder();
}
Trace.beginSection("DesktopModeWindowDecoration#relayout-binding");
- mWindowDecorViewHolder.bindData(mTaskInfo);
+
+ final Point position = new Point();
+ if (isAppHandle(mWindowDecorViewHolder)) {
+ position.set(determineHandlePosition());
+ }
+ mWindowDecorViewHolder.bindData(mTaskInfo,
+ position,
+ mResult.mCaptionWidth,
+ mResult.mCaptionHeight,
+ isCaptionVisible());
Trace.endSection();
if (!mTaskInfo.isFocused) {
closeHandleMenu();
closeMaximizeMenu();
}
-
updateDragResizeListener(oldDecorationSurface);
updateMaximizeMenu(startT);
Trace.endSection(); // DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces
}
+ private boolean isCaptionVisible() {
+ return mTaskInfo.isVisible && mIsCaptionVisible;
+ }
+
private void setCapturedLink(Uri capturedLink, long timeStamp) {
if (capturedLink == null
|| (mCapturedLink != null && mCapturedLink.mTimeStamp == timeStamp)) {
@@ -461,12 +489,42 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
}
}
+ private Point determineHandlePosition() {
+ final Point position = new Point(mResult.mCaptionX, 0);
+ if (mSplitScreenController.getSplitPosition(mTaskInfo.taskId)
+ == SPLIT_POSITION_BOTTOM_OR_RIGHT
+ && mDisplayController.getDisplayLayout(mTaskInfo.displayId).isLandscape()
+ ) {
+ // If this is the right split task, add left stage's width.
+ final Rect leftStageBounds = new Rect();
+ mSplitScreenController.getStageBounds(leftStageBounds, new Rect());
+ position.x += leftStageBounds.width();
+ }
+ return position;
+ }
+
+ /**
+ * Dispose of the view used to forward inputs in status bar region. Intended to be
+ * used any time handle is no longer visible.
+ */
+ void disposeStatusBarInputLayer() {
+ if (!isAppHandle(mWindowDecorViewHolder)
+ || !Flags.enableAdditionalWindowsAboveStatusBar()) {
+ return;
+ }
+ ((AppHandleViewHolder) mWindowDecorViewHolder).disposeStatusBarInputLayer();
+ }
+
private WindowDecorationViewHolder createViewHolder() {
if (mRelayoutParams.mLayoutResId == R.layout.desktop_mode_app_handle) {
return new AppHandleViewHolder(
mResult.mRootView,
mOnCaptionTouchListener,
- mOnCaptionButtonClickListener
+ mOnCaptionButtonClickListener,
+ (v, event) -> {
+ updateHoverAndPressStatus(event);
+ return true;
+ }
);
} else if (mRelayoutParams.mLayoutResId
== R.layout.desktop_mode_app_header) {
@@ -489,6 +547,10 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
throw new IllegalArgumentException("Unexpected layout resource id");
}
+ private boolean isAppHandle(WindowDecorationViewHolder viewHolder) {
+ return viewHolder instanceof AppHandleViewHolder;
+ }
+
@VisibleForTesting
static void updateRelayoutParams(
RelayoutParams relayoutParams,
@@ -863,7 +925,9 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
splitScreenController,
DesktopModeStatus.canEnterDesktopMode(mContext),
browserLinkAvailable(),
- mResult.mCaptionHeight
+ mResult.mCaptionWidth,
+ mResult.mCaptionHeight,
+ mResult.mCaptionX
);
mWindowDecorViewHolder.onHandleMenuOpened();
mHandleMenu.show();
@@ -955,10 +1019,17 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
* @return {@code true} if event is inside caption handle view, {@code false} if not
*/
boolean checkTouchEventInFocusedCaptionHandle(MotionEvent ev) {
- if (isHandleMenuActive() || !(mWindowDecorViewHolder
- instanceof AppHandleViewHolder)) {
+ if (isHandleMenuActive() || !isAppHandle(mWindowDecorViewHolder)
+ || Flags.enableAdditionalWindowsAboveStatusBar()) {
return false;
}
+ // The status bar input layer can only receive input in handle coordinates to begin with,
+ // so checking coordinates is unnecessary as input is always within handle bounds.
+ if (isAppHandle(mWindowDecorViewHolder)
+ && Flags.enableAdditionalWindowsAboveStatusBar()
+ && isCaptionVisible()) {
+ return true;
+ }
return checkTouchEventInCaption(ev);
}
@@ -992,7 +1063,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
* @param ev the MotionEvent to compare
*/
void checkTouchEvent(MotionEvent ev) {
- if (mResult.mRootView == null) return;
+ if (mResult.mRootView == null || Flags.enableAdditionalWindowsAboveStatusBar()) return;
final View caption = mResult.mRootView.findViewById(R.id.desktop_mode_caption);
final View handle = caption.findViewById(R.id.caption_handle);
final boolean inHandle = !isHandleMenuActive()
@@ -1014,7 +1085,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
* @param ev the MotionEvent to compare against.
*/
void updateHoverAndPressStatus(MotionEvent ev) {
- if (mResult.mRootView == null) return;
+ if (mResult.mRootView == null || Flags.enableAdditionalWindowsAboveStatusBar()) return;
final View handle = mResult.mRootView.findViewById(R.id.caption_handle);
final boolean inHandle = !isHandleMenuActive()
&& checkTouchEventInFocusedCaptionHandle(ev);
@@ -1024,15 +1095,11 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
// We want handle to remain pressed if the pointer moves outside of it during a drag.
handle.setPressed((inHandle && action == ACTION_DOWN)
|| (handle.isPressed() && action != ACTION_UP && action != ACTION_CANCEL));
- if (isHandleMenuActive() && !isHandleMenuAboveStatusBar()) {
+ if (isHandleMenuActive()) {
mHandleMenu.checkMotionEvent(ev);
}
}
- private boolean isHandleMenuAboveStatusBar() {
- return Flags.enableAdditionalWindowsAboveStatusBar() && !mTaskInfo.isFreeform();
- }
-
private boolean pointInView(View v, float x, float y) {
return v != null && v.getLeft() <= x && v.getRight() >= x
&& v.getTop() <= y && v.getBottom() >= y;
@@ -1044,6 +1111,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
closeHandleMenu();
mExclusionRegionListener.onExclusionRegionDismissed(mTaskInfo.taskId);
disposeResizeVeil();
+ disposeStatusBarInputLayer();
clearCurrentViewHostRunnable();
super.close();
}
@@ -1143,20 +1211,24 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
DesktopModeWindowDecoration create(
Context context,
DisplayController displayController,
+ SplitScreenController splitScreenController,
ShellTaskOrganizer taskOrganizer,
ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
Handler handler,
+ @ShellBackgroundThread ShellExecutor bgExecutor,
Choreographer choreographer,
SyncTransactionQueue syncQueue,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
return new DesktopModeWindowDecoration(
context,
displayController,
+ splitScreenController,
taskOrganizer,
taskInfo,
taskSurface,
handler,
+ bgExecutor,
choreographer,
syncQueue,
rootTaskDisplayAreaOrganizer);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
index bce233fb0b52..e174e83d11c9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
@@ -15,19 +15,15 @@
*/
package com.android.wm.shell.windowdecor
+import android.annotation.ColorInt
import android.annotation.DimenRes
import android.app.ActivityManager
-import android.app.WindowConfiguration
-import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
-import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
-import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
-import android.app.WindowConfiguration.WINDOWING_MODE_PINNED
import android.content.Context
import android.content.res.ColorStateList
-import android.content.res.Configuration
import android.content.res.Resources
import android.graphics.Bitmap
-import android.graphics.Color
+import android.graphics.BlendMode
+import android.graphics.BlendModeColorFilter
import android.graphics.Point
import android.graphics.PointF
import android.graphics.Rect
@@ -40,6 +36,8 @@ import android.widget.ImageView
import android.widget.TextView
import android.window.SurfaceSyncGroup
import androidx.annotation.VisibleForTesting
+import androidx.compose.ui.graphics.toArgb
+import androidx.core.view.isGone
import com.android.window.flags.Flags
import com.android.wm.shell.R
import com.android.wm.shell.common.DisplayController
@@ -47,7 +45,10 @@ import com.android.wm.shell.common.split.SplitScreenConstants
import com.android.wm.shell.splitscreen.SplitScreenController
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewContainer
+import com.android.wm.shell.windowdecor.common.DecorThemeUtil
import com.android.wm.shell.windowdecor.extension.isFullscreen
+import com.android.wm.shell.windowdecor.extension.isMultiWindow
+import com.android.wm.shell.windowdecor.extension.isPinned
/**
* Handle menu opened when the appropriate button is clicked on.
@@ -68,10 +69,13 @@ class HandleMenu(
private val splitScreenController: SplitScreenController,
private val shouldShowWindowingPill: Boolean,
private val shouldShowBrowserPill: Boolean,
- private val captionHeight: Int
+ private val captionWidth: Int,
+ private val captionHeight: Int,
+ captionX: Int
) {
private val context: Context = parentDecor.mDecorWindowContext
private val taskInfo: ActivityManager.RunningTaskInfo = parentDecor.mTaskInfo
+ private val decorThemeUtil = DecorThemeUtil(context)
private val isViewAboveStatusBar: Boolean
get() = (Flags.enableAdditionalWindowsAboveStatusBar() && !taskInfo.isFreeform)
@@ -102,33 +106,8 @@ class HandleMenu(
// those as well.
private val globalMenuPosition: Point = Point()
- /**
- * An a array of windowing icon color based on current UI theme. First element of the
- * array is for inactive icons and the second is for active icons.
- */
- private val windowingIconColor: Array<ColorStateList>
- get() {
- val mode = (context.resources.configuration.uiMode
- and Configuration.UI_MODE_NIGHT_MASK)
- val isNightMode = (mode == Configuration.UI_MODE_NIGHT_YES)
- val typedArray = context.obtainStyledAttributes(
- intArrayOf(
- com.android.internal.R.attr.materialColorOnSurface,
- com.android.internal.R.attr.materialColorPrimary
- )
- )
- val inActiveColor =
- typedArray.getColor(0, if (isNightMode) Color.WHITE else Color.BLACK)
- val activeColor = typedArray.getColor(1, if (isNightMode) Color.WHITE else Color.BLACK)
- typedArray.recycle()
- return arrayOf(
- ColorStateList.valueOf(inActiveColor),
- ColorStateList.valueOf(activeColor)
- )
- }
-
init {
- updateHandleMenuPillPositions()
+ updateHandleMenuPillPositions(captionX)
}
fun show() {
@@ -175,9 +154,8 @@ class HandleMenu(
* Animates the appearance of the handle menu and its three pills.
*/
private fun animateHandleMenu() {
- when (taskInfo.windowingMode) {
- WindowConfiguration.WINDOWING_MODE_FULLSCREEN,
- WINDOWING_MODE_MULTI_WINDOW -> {
+ when {
+ taskInfo.isFullscreen || taskInfo.isMultiWindow -> {
handleMenuAnimator?.animateCaptionHandleExpandToOpen()
}
else -> {
@@ -193,95 +171,104 @@ class HandleMenu(
private fun setupHandleMenu() {
val handleMenu = handleMenuViewContainer?.view ?: return
handleMenu.setOnTouchListener(onTouchListener)
- setupAppInfoPill(handleMenu)
+
+ val style = calculateMenuStyle()
+ setupAppInfoPill(handleMenu, style)
if (shouldShowWindowingPill) {
- setupWindowingPill(handleMenu)
+ setupWindowingPill(handleMenu, style)
}
- setupMoreActionsPill(handleMenu)
- setupOpenInBrowserPill(handleMenu)
+ setupMoreActionsPill(handleMenu, style)
+ setupOpenInBrowserPill(handleMenu, style)
}
/**
* Set up interactive elements of handle menu's app info pill.
*/
- private fun setupAppInfoPill(handleMenu: View) {
- val collapseBtn = handleMenu.findViewById<HandleMenuImageButton>(R.id.collapse_menu_button)
- val appIcon = handleMenu.findViewById<ImageView>(R.id.application_icon)
- val appName = handleMenu.findViewById<TextView>(R.id.application_name)
- collapseBtn.setOnClickListener(onClickListener)
- collapseBtn.taskInfo = taskInfo
- appIcon.setImageBitmap(appIconBitmap)
- appName.text = this.appName
+ private fun setupAppInfoPill(handleMenu: View, style: MenuStyle) {
+ val pill = handleMenu.requireViewById<View>(R.id.app_info_pill).apply {
+ background.colorFilter = BlendModeColorFilter(style.backgroundColor, BlendMode.MULTIPLY)
+ }
+
+ pill.requireViewById<HandleMenuImageButton>(R.id.collapse_menu_button)
+ .let { collapseBtn ->
+ collapseBtn.imageTintList = ColorStateList.valueOf(style.textColor)
+ collapseBtn.setOnClickListener(onClickListener)
+ collapseBtn.taskInfo = taskInfo
+ }
+ pill.requireViewById<ImageView>(R.id.application_icon).let { appIcon ->
+ appIcon.setImageBitmap(appIconBitmap)
+ }
+ pill.requireViewById<TextView>(R.id.application_name).let { appNameView ->
+ appNameView.text = appName
+ appNameView.setTextColor(style.textColor)
+ }
}
/**
* Set up interactive elements and color of handle menu's windowing pill.
*/
- private fun setupWindowingPill(handleMenu: View) {
- val fullscreenBtn = handleMenu.findViewById<ImageButton>(R.id.fullscreen_button)
- val splitscreenBtn = handleMenu.findViewById<ImageButton>(R.id.split_screen_button)
- val floatingBtn = handleMenu.findViewById<ImageButton>(R.id.floating_button)
+ private fun setupWindowingPill(handleMenu: View, style: MenuStyle) {
+ val pill = handleMenu.requireViewById<View>(R.id.windowing_pill).apply {
+ background.colorFilter = BlendModeColorFilter(style.backgroundColor, BlendMode.MULTIPLY)
+ }
+
+ val fullscreenBtn = pill.requireViewById<ImageButton>(R.id.fullscreen_button)
+ val splitscreenBtn = pill.requireViewById<ImageButton>(R.id.split_screen_button)
+ val floatingBtn = pill.requireViewById<ImageButton>(R.id.floating_button)
// TODO: Remove once implemented.
floatingBtn.visibility = View.GONE
+ val desktopBtn = handleMenu.requireViewById<ImageButton>(R.id.desktop_button)
- val desktopBtn = handleMenu.findViewById<ImageButton>(R.id.desktop_button)
fullscreenBtn.setOnClickListener(onClickListener)
splitscreenBtn.setOnClickListener(onClickListener)
floatingBtn.setOnClickListener(onClickListener)
desktopBtn.setOnClickListener(onClickListener)
- // The button corresponding to the windowing mode that the task is currently in uses a
- // different color than the others.
- val iconColors = windowingIconColor
- val inActiveColorStateList = iconColors[0]
- val activeColorStateList = iconColors[1]
- fullscreenBtn.imageTintList = if (taskInfo.isFullscreen) {
- activeColorStateList
- } else {
- inActiveColorStateList
- }
- splitscreenBtn.imageTintList = if (taskInfo.windowingMode == WINDOWING_MODE_MULTI_WINDOW) {
- activeColorStateList
- } else {
- inActiveColorStateList
- }
- floatingBtn.imageTintList = if (taskInfo.windowingMode == WINDOWING_MODE_PINNED) {
- activeColorStateList
- } else {
- inActiveColorStateList
- }
- desktopBtn.imageTintList = if (taskInfo.isFreeform) {
- activeColorStateList
- } else {
- inActiveColorStateList
- }
+
+ fullscreenBtn.isSelected = taskInfo.isFullscreen
+ fullscreenBtn.imageTintList = style.windowingButtonColor
+ splitscreenBtn.isSelected = taskInfo.isMultiWindow
+ splitscreenBtn.imageTintList = style.windowingButtonColor
+ floatingBtn.isSelected = taskInfo.isPinned
+ floatingBtn.imageTintList = style.windowingButtonColor
+ desktopBtn.isSelected = taskInfo.isFreeform
+ desktopBtn.imageTintList = style.windowingButtonColor
}
/**
* Set up interactive elements & height of handle menu's more actions pill
*/
- private fun setupMoreActionsPill(handleMenu: View) {
- if (!SHOULD_SHOW_MORE_ACTIONS_PILL) {
- handleMenu.findViewById<View>(R.id.more_actions_pill).visibility = View.GONE
+ private fun setupMoreActionsPill(handleMenu: View, style: MenuStyle) {
+ val pill = handleMenu.requireViewById<View>(R.id.more_actions_pill).apply {
+ isGone = !SHOULD_SHOW_MORE_ACTIONS_PILL
+ background.colorFilter = BlendModeColorFilter(style.backgroundColor, BlendMode.MULTIPLY)
+ }
+ pill.requireViewById<Button>(R.id.screenshot_button).let { screenshotBtn ->
+ screenshotBtn.setTextColor(style.textColor)
+ screenshotBtn.compoundDrawableTintList = ColorStateList.valueOf(style.textColor)
}
}
- private fun setupOpenInBrowserPill(handleMenu: View) {
- if (!shouldShowBrowserPill) {
- handleMenu.findViewById<View>(R.id.open_in_browser_pill).visibility = View.GONE
- return
+ private fun setupOpenInBrowserPill(handleMenu: View, style: MenuStyle) {
+ val pill = handleMenu.requireViewById<View>(R.id.open_in_browser_pill).apply {
+ isGone = !shouldShowBrowserPill
+ background.colorFilter = BlendModeColorFilter(style.backgroundColor, BlendMode.MULTIPLY)
+ }
+
+ pill.requireViewById<Button>(R.id.open_in_browser_button).let { browserButton ->
+ browserButton.setOnClickListener(onClickListener)
+ browserButton.setTextColor(style.textColor)
+ browserButton.compoundDrawableTintList = ColorStateList.valueOf(style.textColor)
}
- val browserButton = handleMenu.findViewById<Button>(R.id.open_in_browser_button)
- browserButton.setOnClickListener(onClickListener)
}
/**
* Updates handle menu's position variables to reflect its next position.
*/
- private fun updateHandleMenuPillPositions() {
+ private fun updateHandleMenuPillPositions(captionX: Int) {
val menuX: Int
val menuY: Int
val taskBounds = taskInfo.getConfiguration().windowConfiguration.bounds
- updateGlobalMenuPosition(taskBounds)
+ updateGlobalMenuPosition(taskBounds, captionX)
if (layoutResId == R.layout.desktop_mode_app_header) {
// Align the handle menu to the left side of the caption.
menuX = marginMenuStart
@@ -302,21 +289,22 @@ class HandleMenu(
handleMenuPosition.set(menuX.toFloat(), menuY.toFloat())
}
- private fun updateGlobalMenuPosition(taskBounds: Rect) {
- when (taskInfo.windowingMode) {
- WINDOWING_MODE_FREEFORM -> {
+ private fun updateGlobalMenuPosition(taskBounds: Rect, captionX: Int) {
+ val nonFreeformX = captionX + (captionWidth / 2) - (menuWidth / 2)
+ when {
+ taskInfo.isFreeform -> {
globalMenuPosition.set(
/* x = */ taskBounds.left + marginMenuStart,
/* y = */ taskBounds.top + marginMenuTop
)
}
- WINDOWING_MODE_FULLSCREEN -> {
+ taskInfo.isFullscreen -> {
globalMenuPosition.set(
- /* x = */ taskBounds.width() / 2 - (menuWidth / 2),
+ /* x = */ nonFreeformX,
/* y = */ marginMenuTop
)
}
- WINDOWING_MODE_MULTI_WINDOW -> {
+ taskInfo.isMultiWindow -> {
val splitPosition = splitScreenController.getSplitPosition(taskInfo.taskId)
val leftOrTopStageBounds = Rect()
val rightOrBottomStageBounds = Rect()
@@ -326,16 +314,13 @@ class HandleMenu(
when (splitPosition) {
SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT -> {
globalMenuPosition.set(
- /* x = */ leftOrTopStageBounds.width()
- + (rightOrBottomStageBounds.width() / 2)
- - (menuWidth / 2),
+ /* x = */ leftOrTopStageBounds.width() + nonFreeformX,
/* y = */ marginMenuTop
)
}
SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT -> {
globalMenuPosition.set(
- /* x = */ (leftOrTopStageBounds.width() / 2)
- - (menuWidth / 2),
+ /* x = */ nonFreeformX,
/* y = */ marginMenuTop
)
}
@@ -347,9 +332,12 @@ class HandleMenu(
/**
* Update pill layout, in case task changes have caused positioning to change.
*/
- fun relayout(t: SurfaceControl.Transaction) {
+ fun relayout(
+ t: SurfaceControl.Transaction,
+ captionX: Int
+ ) {
handleMenuViewContainer?.let { container ->
- updateHandleMenuPillPositions()
+ updateHandleMenuPillPositions(captionX)
container.setPosition(t, handleMenuPosition.x, handleMenuPosition.y)
}
}
@@ -469,14 +457,41 @@ class HandleMenu(
handleMenuViewContainer?.releaseView()
handleMenuViewContainer = null
}
- if (taskInfo.windowingMode == WINDOWING_MODE_FULLSCREEN ||
- taskInfo.windowingMode == WINDOWING_MODE_MULTI_WINDOW) {
+ if (taskInfo.isFullscreen || taskInfo.isMultiWindow) {
handleMenuAnimator?.animateCollapseIntoHandleClose(after)
} else {
handleMenuAnimator?.animateClose(after)
}
}
+ private fun calculateMenuStyle(): MenuStyle {
+ val colorScheme = decorThemeUtil.getColorScheme(taskInfo)
+ return MenuStyle(
+ backgroundColor = colorScheme.surfaceBright.toArgb(),
+ textColor = colorScheme.onSurface.toArgb(),
+ windowingButtonColor = ColorStateList(
+ arrayOf(
+ intArrayOf(android.R.attr.state_pressed),
+ intArrayOf(android.R.attr.state_focused),
+ intArrayOf(android.R.attr.state_selected),
+ intArrayOf(),
+ ),
+ intArrayOf(
+ colorScheme.onSurface.toArgb(),
+ colorScheme.onSurface.toArgb(),
+ colorScheme.primary.toArgb(),
+ colorScheme.onSurface.toArgb(),
+ )
+ ),
+ )
+ }
+
+ private data class MenuStyle(
+ @ColorInt val backgroundColor: Int,
+ @ColorInt val textColor: Int,
+ val windowingButtonColor: ColorStateList,
+ )
+
companion object {
private const val TAG = "HandleMenu"
private const val SHOULD_SHOW_MORE_ACTIONS_PILL = false
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 d212f2131ed4..a691f59a2155 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
@@ -139,7 +139,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
private SurfaceControlViewHost mViewHost;
private Configuration mWindowDecorConfig;
TaskDragResizer mTaskDragResizer;
- private boolean mIsCaptionVisible;
+ boolean mIsCaptionVisible;
/** The most recent set of insets applied to this window decoration. */
private WindowDecorationInsets mWindowDecorationInsets;
@@ -508,6 +508,8 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
mTaskDragResizer = taskDragResizer;
}
+ // TODO(b/346441962): Move these three methods closer to implementing or View-level classes to
+ // keep implementation details more encapsulated.
private void setCaptionVisibility(View rootView, boolean visible) {
if (rootView == null) {
return;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt
index 4897f76a20cf..6a354f10049b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt
@@ -30,17 +30,22 @@ import android.view.WindowManager
*/
class AdditionalSystemViewContainer(
private val context: Context,
- layoutId: Int,
taskId: Int,
x: Int,
y: Int,
width: Int,
- height: Int
+ height: Int,
+ layoutId: Int? = null
) : AdditionalViewContainer() {
override val view: View
+ val windowManager: WindowManager? = context.getSystemService(WindowManager::class.java)
init {
- view = LayoutInflater.from(context).inflate(layoutId, null)
+ if (layoutId != null) {
+ view = LayoutInflater.from(context).inflate(layoutId, null)
+ } else {
+ view = View(context)
+ }
val lp = WindowManager.LayoutParams(
width, height, x, y,
WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL,
@@ -51,12 +56,11 @@ class AdditionalSystemViewContainer(
gravity = Gravity.LEFT or Gravity.TOP
setTrustedOverlay()
}
- val wm: WindowManager? = context.getSystemService(WindowManager::class.java)
- wm?.addView(view, lp)
+ windowManager?.addView(view, lp)
}
override fun releaseView() {
- context.getSystemService(WindowManager::class.java)?.removeViewImmediate(view)
+ windowManager?.removeViewImmediate(view)
}
override fun setPosition(t: SurfaceControl.Transaction, x: Float, y: Float) {
@@ -64,6 +68,6 @@ class AdditionalSystemViewContainer(
this.x = x.toInt()
this.y = y.toInt()
}
- view.layoutParams = lp
+ windowManager?.updateViewLayout(view, lp)
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/extension/TaskInfo.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/extension/TaskInfo.kt
index 7ade9876d28a..6f8e00143848 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/extension/TaskInfo.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/extension/TaskInfo.kt
@@ -18,6 +18,8 @@ package com.android.wm.shell.windowdecor.extension
import android.app.TaskInfo
import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
+import android.app.WindowConfiguration.WINDOWING_MODE_PINNED
import android.view.WindowInsetsController.APPEARANCE_LIGHT_CAPTION_BARS
import android.view.WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND
@@ -33,5 +35,14 @@ val TaskInfo.isLightCaptionBarAppearance: Boolean
return (appearance and APPEARANCE_LIGHT_CAPTION_BARS) != 0
}
+/** Whether the task is in fullscreen windowing mode. */
val TaskInfo.isFullscreen: Boolean
get() = windowingMode == WINDOWING_MODE_FULLSCREEN
+
+/** Whether the task is in pinned windowing mode. */
+val TaskInfo.isPinned: Boolean
+ get() = windowingMode == WINDOWING_MODE_PINNED
+
+/** Whether the task is in multi-window windowing mode. */
+val TaskInfo.isMultiWindow: Boolean
+ get() = windowingMode == WINDOWING_MODE_MULTI_WINDOW
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
index 8d822c252288..76dfe37c7cc0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
@@ -20,37 +20,68 @@ import android.app.ActivityManager.RunningTaskInfo
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.content.res.ColorStateList
import android.graphics.Color
+import android.graphics.Point
+import android.view.SurfaceControl
import android.view.View
+import android.view.View.OnClickListener
+import android.view.View.OnHoverListener
import android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
+import android.view.WindowManager
import android.widget.ImageButton
+import com.android.window.flags.Flags
import com.android.wm.shell.R
import com.android.wm.shell.animation.Interpolators
+import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer
/**
* A desktop mode window decoration used when the window is in full "focus" (i.e. fullscreen/split).
* It hosts a simple handle bar from which to initiate a drag motion to enter desktop mode.
*/
internal class AppHandleViewHolder(
- rootView: View,
- onCaptionTouchListener: View.OnTouchListener,
- onCaptionButtonClickListener: View.OnClickListener
+ rootView: View,
+ private val onCaptionTouchListener: View.OnTouchListener,
+ private val onCaptionButtonClickListener: OnClickListener,
+ private val onCaptionHoverListener: OnHoverListener,
) : WindowDecorationViewHolder(rootView) {
companion object {
private const val CAPTION_HANDLE_ANIMATION_DURATION: Long = 100
}
-
+ private lateinit var taskInfo: RunningTaskInfo
+ private val windowManager = context.getSystemService(WindowManager::class.java)
private val captionView: View = rootView.requireViewById(R.id.desktop_mode_caption)
private val captionHandle: ImageButton = rootView.requireViewById(R.id.caption_handle)
+ // An invisible View that takes up the same coordinates as captionHandle but is layered
+ // above the status bar. The purpose of this View is to receive input intended for
+ // captionHandle.
+ private var statusBarInputLayer: AdditionalSystemViewContainer? = null
+
init {
captionView.setOnTouchListener(onCaptionTouchListener)
captionHandle.setOnTouchListener(onCaptionTouchListener)
captionHandle.setOnClickListener(onCaptionButtonClickListener)
+ captionHandle.setOnHoverListener(onCaptionHoverListener)
}
- override fun bindData(taskInfo: RunningTaskInfo) {
+ override fun bindData(
+ taskInfo: RunningTaskInfo,
+ position: Point,
+ width: Int,
+ height: Int,
+ isCaptionVisible: Boolean
+ ) {
captionHandle.imageTintList = ColorStateList.valueOf(getCaptionHandleBarColor(taskInfo))
+ this.taskInfo = taskInfo
+ if (!isCaptionVisible && hasStatusBarInputLayer()) {
+ disposeStatusBarInputLayer()
+ return
+ }
+ if (hasStatusBarInputLayer()) {
+ updateStatusBarInputLayer(position)
+ } else {
+ createStatusBarInputLayer(position, width, height)
+ }
}
override fun onHandleMenuOpened() {
@@ -61,6 +92,45 @@ internal class AppHandleViewHolder(
animateCaptionHandleAlpha(startValue = 0f, endValue = 1f)
}
+ private fun createStatusBarInputLayer(handlePosition: Point,
+ handleWidth: Int,
+ handleHeight: Int) {
+ if (!Flags.enableAdditionalWindowsAboveStatusBar()) return
+ statusBarInputLayer = AdditionalSystemViewContainer(context, taskInfo.taskId,
+ handlePosition.x, handlePosition.y, handleWidth, handleHeight)
+ val view = statusBarInputLayer?.view
+ val lp = view?.layoutParams as WindowManager.LayoutParams
+ lp.title = "Handle Input Layer of task " + taskInfo.taskId
+ lp.setTrustedOverlay()
+ // Make this window a spy window to enable it to pilfer pointers from the system-wide
+ // gesture listener that receives events before window. This is to prevent notification
+ // shade gesture when we swipe down to enter desktop.
+ lp.inputFeatures = WindowManager.LayoutParams.INPUT_FEATURE_SPY
+ view.id = R.id.caption_handle
+ view.setOnClickListener(onCaptionButtonClickListener)
+ view.setOnTouchListener(onCaptionTouchListener)
+ view.setOnHoverListener(onCaptionHoverListener)
+ windowManager.updateViewLayout(view, lp)
+ }
+
+ private fun updateStatusBarInputLayer(globalPosition: Point) {
+ statusBarInputLayer?.setPosition(SurfaceControl.Transaction(), globalPosition.x.toFloat(),
+ globalPosition.y.toFloat()) ?: return
+ }
+
+ private fun hasStatusBarInputLayer(): Boolean {
+ return statusBarInputLayer != null
+ }
+
+ /**
+ * Remove the input layer from [WindowManager]. Should be used when caption handle
+ * is not visible.
+ */
+ fun disposeStatusBarInputLayer() {
+ statusBarInputLayer?.releaseView()
+ statusBarInputLayer = null
+ }
+
private fun getCaptionHandleBarColor(taskInfo: RunningTaskInfo): Int {
return if (shouldUseLightCaptionColors(taskInfo)) {
context.getColor(R.color.desktop_mode_caption_handle_bar_light)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt
index 46127b177bc3..b704d9c001c8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt
@@ -21,6 +21,7 @@ import android.content.res.ColorStateList
import android.content.res.Configuration
import android.graphics.Bitmap
import android.graphics.Color
+import android.graphics.Point
import android.graphics.drawable.LayerDrawable
import android.graphics.drawable.RippleDrawable
import android.graphics.drawable.ShapeDrawable
@@ -136,7 +137,13 @@ internal class AppHeaderViewHolder(
onMaximizeHoverAnimationFinishedListener
}
- override fun bindData(taskInfo: RunningTaskInfo) {
+ override fun bindData(
+ taskInfo: RunningTaskInfo,
+ position: Point,
+ width: Int,
+ height: Int,
+ isCaptionVisible: Boolean
+ ) {
if (Flags.enableThemedAppHeaders()) {
bindDataWithThemedHeaders(taskInfo)
} else {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/WindowDecorationViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/WindowDecorationViewHolder.kt
index 5ae8d252a908..2341b099699f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/WindowDecorationViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/WindowDecorationViewHolder.kt
@@ -17,6 +17,7 @@ package com.android.wm.shell.windowdecor.viewholder
import android.app.ActivityManager.RunningTaskInfo
import android.content.Context
+import android.graphics.Point
import android.view.View
/**
@@ -30,7 +31,13 @@ internal abstract class WindowDecorationViewHolder(rootView: View) {
* A signal to the view holder that new data is available and that the views should be updated to
* reflect it.
*/
- abstract fun bindData(taskInfo: RunningTaskInfo)
+ abstract fun bindData(
+ taskInfo: RunningTaskInfo,
+ position: Point,
+ width: Int,
+ height: Int,
+ isCaptionVisible: Boolean
+ )
/** Callback when the handle menu is opened. */
abstract fun onHandleMenuOpened()
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/SwitchToOverviewFromDesktop.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/SwitchToOverviewFromDesktop.kt
new file mode 100644
index 000000000000..b4cadf4f300b
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/SwitchToOverviewFromDesktop.kt
@@ -0,0 +1,70 @@
+/*
+ * 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.flicker.service.desktopmode.scenarios
+
+import android.app.Instrumentation
+import android.tools.NavBar
+import android.tools.Rotation
+import android.tools.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.window.flags.Flags
+import com.android.wm.shell.flicker.service.common.Utils
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+/**
+* Base test for opening recent apps overview from desktop mode.
+*
+* Navigation mode can be passed as a constructor parameter, by default it is set to gesture navigation.
+*/
+@Ignore("Base Test Class")
+abstract class SwitchToOverviewFromDesktop
+@JvmOverloads
+constructor(val navigationMode: NavBar = NavBar.MODE_GESTURAL) {
+
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation))
+
+ @Rule @JvmField val testSetupRule = Utils.testSetupRule(navigationMode, Rotation.ROTATION_0)
+
+ @Before
+ fun setup() {
+ Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
+ testApp.enterDesktopWithDrag(wmHelper, device)
+ }
+
+ @Test
+ open fun switchToOverview() {
+ tapl.getLaunchedAppState().switchToOverview()
+ }
+
+ @After
+ fun teardown() {
+ testApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt
index db962e717a3b..2406bdeebdf2 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt
+++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt
@@ -48,7 +48,10 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) {
@Before
fun setup() {
- tapl.workspace.switchToOverview().dismissAllTasks()
+ val overview = tapl.workspace.switchToOverview()
+ if (overview.hasTasks()) {
+ overview.dismissAllTasks()
+ }
tapl.setEnableRotation(true)
tapl.setExpectedRotation(rotation.value)
diff --git a/libs/WindowManager/Shell/tests/unittest/Android.bp b/libs/WindowManager/Shell/tests/unittest/Android.bp
index 6b6954289a34..a0408652a29b 100644
--- a/libs/WindowManager/Shell/tests/unittest/Android.bp
+++ b/libs/WindowManager/Shell/tests/unittest/Android.bp
@@ -37,6 +37,7 @@ android_test {
],
static_libs: [
+ "TestParameterInjector",
"WindowManager-Shell",
"junit",
"flag-junit",
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java
index 55b6bd278f20..bba9418db66a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java
@@ -46,12 +46,14 @@ import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.window.TransitionInfo;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.window.flags.Flags;
import com.android.wm.shell.transition.TransitionInfoBuilder;
+import com.google.testing.junit.testparameterinjector.TestParameter;
+import com.google.testing.junit.testparameterinjector.TestParameterInjector;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -59,6 +61,7 @@ import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import java.util.ArrayList;
+import java.util.Arrays;
/**
* Tests for {@link ActivityEmbeddingAnimationRunner}.
@@ -67,7 +70,7 @@ import java.util.ArrayList;
* atest WMShellUnitTests:ActivityEmbeddingAnimationRunnerTests
*/
@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(TestParameterInjector.class)
public class ActivityEmbeddingAnimationRunnerTests extends ActivityEmbeddingAnimationTestBase {
@Rule
@@ -204,15 +207,13 @@ public class ActivityEmbeddingAnimationRunnerTests extends ActivityEmbeddingAnim
// TODO(b/243518738): Rewrite with TestParameter
@EnableFlags(Flags.FLAG_ACTIVITY_EMBEDDING_OVERLAY_PRESENTATION_FLAG)
@Test
- public void testCalculateParentBounds_flagEnabled() {
+ public void testCalculateParentBounds_flagEnabled_emptyParentSize() {
TransitionInfo.Change change;
final TransitionInfo.Change stubChange = createChange(0 /* flags */);
final Rect actualParentBounds = new Rect();
- Rect parentBounds = new Rect(0, 0, 2000, 2000);
- Rect endAbsBounds = new Rect(0, 0, 2000, 2000);
change = prepareChangeForParentBoundsCalculationTest(
new Point(0, 0) /* endRelOffset */,
- endAbsBounds,
+ new Rect(0, 0, 2000, 2000),
new Point() /* endParentSize */
);
@@ -220,69 +221,80 @@ public class ActivityEmbeddingAnimationRunnerTests extends ActivityEmbeddingAnim
assertTrue("Parent bounds must be empty because end parent size is not set.",
actualParentBounds.isEmpty());
+ }
- String testString = "Parent start with (0, 0)";
- change = prepareChangeForParentBoundsCalculationTest(
- new Point(endAbsBounds.left - parentBounds.left,
- endAbsBounds.top - parentBounds.top),
- endAbsBounds, new Point(parentBounds.width(), parentBounds.height()));
-
- calculateParentBounds(change, stubChange, actualParentBounds);
-
- assertEquals(testString + ": Parent bounds must be " + parentBounds, parentBounds,
- actualParentBounds);
-
- testString = "Container not start with (0, 0)";
- parentBounds = new Rect(0, 0, 2000, 2000);
- endAbsBounds = new Rect(1000, 500, 2000, 1500);
- change = prepareChangeForParentBoundsCalculationTest(
- new Point(endAbsBounds.left - parentBounds.left,
- endAbsBounds.top - parentBounds.top),
- endAbsBounds, new Point(parentBounds.width(), parentBounds.height()));
-
- calculateParentBounds(change, stubChange, actualParentBounds);
-
- assertEquals(testString + ": Parent bounds must be " + parentBounds, parentBounds,
- actualParentBounds);
-
- testString = "Parent container on the right";
- parentBounds = new Rect(1000, 0, 2000, 2000);
- endAbsBounds = new Rect(1000, 500, 1500, 1500);
- change = prepareChangeForParentBoundsCalculationTest(
- new Point(endAbsBounds.left - parentBounds.left,
- endAbsBounds.top - parentBounds.top),
- endAbsBounds, new Point(parentBounds.width(), parentBounds.height()));
-
- calculateParentBounds(change, stubChange, actualParentBounds);
-
- assertEquals(testString + ": Parent bounds must be " + parentBounds, parentBounds,
- actualParentBounds);
-
- testString = "Parent container on the bottom";
- parentBounds = new Rect(0, 1000, 2000, 2000);
- endAbsBounds = new Rect(500, 1500, 1500, 2000);
- change = prepareChangeForParentBoundsCalculationTest(
+ @EnableFlags(Flags.FLAG_ACTIVITY_EMBEDDING_OVERLAY_PRESENTATION_FLAG)
+ @Test
+ public void testCalculateParentBounds_flagEnabled(
+ @TestParameter ParentBoundsTestParameters params) {
+ final TransitionInfo.Change stubChange = createChange(0 /*flags*/);
+ final Rect parentBounds = params.getParentBounds();
+ final Rect endAbsBounds = params.getEndAbsBounds();
+ final TransitionInfo.Change change = prepareChangeForParentBoundsCalculationTest(
new Point(endAbsBounds.left - parentBounds.left,
endAbsBounds.top - parentBounds.top),
endAbsBounds, new Point(parentBounds.width(), parentBounds.height()));
+ final Rect actualParentBounds = new Rect();
calculateParentBounds(change, stubChange, actualParentBounds);
- assertEquals(testString + ": Parent bounds must be " + parentBounds, parentBounds,
- actualParentBounds);
-
- testString = "Parent container in the middle";
- parentBounds = new Rect(500, 500, 1500, 1500);
- endAbsBounds = new Rect(1000, 500, 1500, 1000);
- change = prepareChangeForParentBoundsCalculationTest(
- new Point(endAbsBounds.left - parentBounds.left,
- endAbsBounds.top - parentBounds.top),
- endAbsBounds, new Point(parentBounds.width(), parentBounds.height()));
-
- calculateParentBounds(change, stubChange, actualParentBounds);
+ assertEquals(parentBounds, actualParentBounds);
+ }
- assertEquals(testString + ": Parent bounds must be " + parentBounds, parentBounds,
- actualParentBounds);
+ private enum ParentBoundsTestParameters {
+ PARENT_START_WITH_0_0(
+ new int[]{0, 0, 2000, 2000},
+ new int[]{0, 0, 2000, 2000}),
+ CONTAINER_NOT_START_WITH_0_0(
+ new int[] {0, 0, 2000, 2000},
+ new int[] {1000, 500, 1500, 1500}),
+ PARENT_ON_THE_RIGHT(
+ new int[] {1000, 0, 2000, 2000},
+ new int[] {1000, 500, 1500, 1500}),
+ PARENT_ON_THE_BOTTOM(
+ new int[] {0, 1000, 2000, 2000},
+ new int[] {500, 1500, 1500, 2000}),
+ PARENT_IN_THE_MIDDLE(
+ new int[] {500, 500, 1500, 1500},
+ new int[] {1000, 500, 1500, 1000});
+
+ /**
+ * An int array to present {left, top, right, bottom} of the parent {@link Rect bounds}.
+ */
+ @NonNull
+ private final int[] mParentBounds;
+
+ /**
+ * An int array to present {left, top, right, bottom} of the absolute container
+ * {@link Rect bounds} after the transition finishes.
+ */
+ @NonNull
+ private final int[] mEndAbsBounds;
+
+ ParentBoundsTestParameters(
+ @NonNull int[] parentBounds, @NonNull int[] endAbsBounds) {
+ mParentBounds = parentBounds;
+ mEndAbsBounds = endAbsBounds;
+ }
+
+ @NonNull
+ private Rect getParentBounds() {
+ return asRect(mParentBounds);
+ }
+
+ @NonNull
+ private Rect getEndAbsBounds() {
+ return asRect(mEndAbsBounds);
+ }
+
+ @NonNull
+ private static Rect asRect(@NonNull int[] bounds) {
+ if (bounds.length != 4) {
+ throw new IllegalArgumentException("There must be exactly 4 elements in bounds, "
+ + "but found " + bounds.length + ": " + Arrays.toString(bounds));
+ }
+ return new Rect(bounds[0], bounds[1], bounds[2], bounds[3]);
+ }
}
@Test
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 6002c21ccb24..8421365e594d 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
@@ -580,138 +580,6 @@ class DesktopTasksControllerTest : ShellTestCase() {
}
@Test
- @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
- fun moveToDesktop_landscapeDevice_resizable_undefinedOrientation_defaultLandscapeBounds() {
- val task = setUpFullscreenTask()
- setUpLandscapeDisplay()
-
- controller.moveToDesktop(task, transitionSource = UNKNOWN)
- val wct = getLatestEnterDesktopWct()
- assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
- fun moveToDesktop_landscapeDevice_resizable_landscapeOrientation_defaultLandscapeBounds() {
- val task = setUpFullscreenTask(screenOrientation = SCREEN_ORIENTATION_LANDSCAPE)
- setUpLandscapeDisplay()
-
- controller.moveToDesktop(task, transitionSource = UNKNOWN)
- val wct = getLatestEnterDesktopWct()
- assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
- fun moveToDesktop_landscapeDevice_resizable_portraitOrientation_resizablePortraitBounds() {
- val task =
- setUpFullscreenTask(screenOrientation = SCREEN_ORIENTATION_PORTRAIT, shouldLetterbox = true)
- setUpLandscapeDisplay()
-
- controller.moveToDesktop(task, transitionSource = UNKNOWN)
- val wct = getLatestEnterDesktopWct()
- assertThat(findBoundsChange(wct, task)).isEqualTo(RESIZABLE_PORTRAIT_BOUNDS)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
- fun moveToDesktop_landscapeDevice_unResizable_landscapeOrientation_defaultLandscapeBounds() {
- val task =
- setUpFullscreenTask(isResizable = false, screenOrientation = SCREEN_ORIENTATION_LANDSCAPE)
- setUpLandscapeDisplay()
-
- controller.moveToDesktop(task, transitionSource = UNKNOWN)
- val wct = getLatestEnterDesktopWct()
- assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
- fun moveToDesktop_landscapeDevice_unResizable_portraitOrientation_unResizablePortraitBounds() {
- val task =
- setUpFullscreenTask(
- isResizable = false,
- screenOrientation = SCREEN_ORIENTATION_PORTRAIT,
- shouldLetterbox = true)
- setUpLandscapeDisplay()
-
- controller.moveToDesktop(task, transitionSource = UNKNOWN)
- val wct = getLatestEnterDesktopWct()
- assertThat(findBoundsChange(wct, task)).isEqualTo(UNRESIZABLE_PORTRAIT_BOUNDS)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
- fun moveToDesktop_portraitDevice_resizable_undefinedOrientation_defaultPortraitBounds() {
- val task = setUpFullscreenTask(deviceOrientation = ORIENTATION_PORTRAIT)
- setUpPortraitDisplay()
-
- controller.moveToDesktop(task, transitionSource = UNKNOWN)
- val wct = getLatestEnterDesktopWct()
- assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
- fun moveToDesktop_portraitDevice_resizable_portraitOrientation_defaultPortraitBounds() {
- val task =
- setUpFullscreenTask(
- deviceOrientation = ORIENTATION_PORTRAIT,
- screenOrientation = SCREEN_ORIENTATION_PORTRAIT)
- setUpPortraitDisplay()
-
- controller.moveToDesktop(task, transitionSource = UNKNOWN)
- val wct = getLatestEnterDesktopWct()
- assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
- fun moveToDesktop_portraitDevice_resizable_landscapeOrientation_resizableLandscapeBounds() {
- val task =
- setUpFullscreenTask(
- deviceOrientation = ORIENTATION_PORTRAIT,
- screenOrientation = SCREEN_ORIENTATION_LANDSCAPE,
- shouldLetterbox = true)
- setUpPortraitDisplay()
-
- controller.moveToDesktop(task, transitionSource = UNKNOWN)
- val wct = getLatestEnterDesktopWct()
- assertThat(findBoundsChange(wct, task)).isEqualTo(RESIZABLE_LANDSCAPE_BOUNDS)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
- fun moveToDesktop_portraitDevice_unResizable_portraitOrientation_defaultPortraitBounds() {
- val task =
- setUpFullscreenTask(
- isResizable = false,
- deviceOrientation = ORIENTATION_PORTRAIT,
- screenOrientation = SCREEN_ORIENTATION_PORTRAIT)
- setUpPortraitDisplay()
-
- controller.moveToDesktop(task, transitionSource = UNKNOWN)
- val wct = getLatestEnterDesktopWct()
- assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
- fun moveToDesktop_portraitDevice_unResizable_landscapeOrientation_unResizableLandscapeBounds() {
- val task =
- setUpFullscreenTask(
- isResizable = false,
- deviceOrientation = ORIENTATION_PORTRAIT,
- screenOrientation = SCREEN_ORIENTATION_LANDSCAPE,
- shouldLetterbox = true)
- setUpPortraitDisplay()
-
- controller.moveToDesktop(task, transitionSource = UNKNOWN)
- val wct = getLatestEnterDesktopWct()
- assertThat(findBoundsChange(wct, task)).isEqualTo(UNRESIZABLE_LANDSCAPE_BOUNDS)
- }
-
- @Test
fun moveToDesktop_tdaFullscreen_windowingModeSetToFreeform() {
val task = setUpFullscreenTask()
val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlagsTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlagsTest.kt
index b1d62f485a2a..805ebbc6a6da 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlagsTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlagsTest.kt
@@ -16,12 +16,15 @@
package com.android.wm.shell.shared.desktopmode
+import android.os.SystemProperties
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
import android.provider.Settings
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito
+import com.android.modules.utils.testing.ExtendedMockitoRule
import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE
import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY
import com.android.window.flags.Flags.FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION
@@ -37,6 +40,9 @@ import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mockito
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
/**
* Test class for [DesktopModeFlags]
@@ -47,7 +53,14 @@ import org.junit.runner.RunWith
@RunWith(AndroidTestingRunner::class)
class DesktopModeFlagsTest : ShellTestCase() {
- @JvmField @Rule val setFlagsRule = SetFlagsRule()
+ @Rule(order = 1)
+ @JvmField
+ val setFlagsRule = SetFlagsRule()
+
+ @Rule(order = 2)
+ @JvmField
+ val extendedMockitoRule: ExtendedMockitoRule =
+ ExtendedMockitoRule.Builder(this).mockStatic(SystemProperties::class.java).build()
@Before
fun setUp() {
@@ -119,7 +132,7 @@ class DesktopModeFlagsTest : ShellTestCase() {
@Test
@EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
fun isEnabled_unrecognizableOverride_featureFlagOn_returnsTrue() {
- setOverride(-2)
+ setOverride(INVALID_TOGGLE_OVERRIDE_SETTING)
// For overridableFlag, for recognizable overrides, follow flag
assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue()
@@ -129,7 +142,7 @@ class DesktopModeFlagsTest : ShellTestCase() {
@EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
@DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
fun isEnabled_unrecognizableOverride_featureFlagOff_returnsFalse() {
- setOverride(-2)
+ setOverride(INVALID_TOGGLE_OVERRIDE_SETTING)
// For overridableFlag, for recognizable overrides, follow flag
assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse()
@@ -187,102 +200,82 @@ class DesktopModeFlagsTest : ShellTestCase() {
@EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
@DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
fun isEnabled_noSystemProperty_overrideOn_featureFlagOff_returnsTrueAndStoresPropertyOn() {
- System.clearProperty(SYSTEM_PROPERTY_OVERRIDE_KEY)
+ setSystemProperty(INVALID_TOGGLE_OVERRIDE_SETTING)
setOverride(OVERRIDE_ON.setting)
assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue()
// Store System Property if not present
- assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
- .isEqualTo(OVERRIDE_ON.setting.toString())
+ verifySystemPropertySet(OVERRIDE_ON.setting)
}
@Test
@EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
fun isEnabled_noSystemProperty_overrideUnset_featureFlagOn_returnsTrueAndStoresPropertyUnset() {
- System.clearProperty(SYSTEM_PROPERTY_OVERRIDE_KEY)
+ setSystemProperty(INVALID_TOGGLE_OVERRIDE_SETTING)
setOverride(OVERRIDE_UNSET.setting)
assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue()
// Store System Property if not present
- assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
- .isEqualTo(OVERRIDE_UNSET.setting.toString())
+ verifySystemPropertySet(OVERRIDE_UNSET.setting)
}
@Test
@EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
@DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
fun isEnabled_noSystemProperty_overrideUnset_featureFlagOff_returnsFalseAndStoresPropertyUnset() {
- System.clearProperty(SYSTEM_PROPERTY_OVERRIDE_KEY)
+ setSystemProperty(INVALID_TOGGLE_OVERRIDE_SETTING)
setOverride(OVERRIDE_UNSET.setting)
assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse()
// Store System Property if not present
- assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
- .isEqualTo(OVERRIDE_UNSET.setting.toString())
- }
-
- @Test
- @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
- @Suppress("ktlint:standard:max-line-length")
- fun isEnabled_systemPropertyNotInteger_overrideOff_featureFlagOn_returnsFalseAndStoresPropertyOff() {
- System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, "abc")
- setOverride(OVERRIDE_OFF.setting)
-
- assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse()
- // Store System Property if currently invalid
- assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
- .isEqualTo(OVERRIDE_OFF.setting.toString())
+ verifySystemPropertySet(OVERRIDE_UNSET.setting)
}
@Test
@EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
@Suppress("ktlint:standard:max-line-length")
fun isEnabled_systemPropertyInvalidInteger_overrideOff_featureFlagOn_returnsFalseAndStoresPropertyOff() {
- System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, "-2")
+ setSystemProperty(INVALID_TOGGLE_OVERRIDE_SETTING)
setOverride(OVERRIDE_OFF.setting)
assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse()
// Store System Property if currently invalid
- assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
- .isEqualTo(OVERRIDE_OFF.setting.toString())
+ verifySystemPropertySet(OVERRIDE_OFF.setting)
}
@Test
@EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
fun isEnabled_systemPropertyOff_overrideOn_featureFlagOn_returnsFalseAndDoesNotUpdateProperty() {
- System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, OVERRIDE_OFF.setting.toString())
+ setSystemProperty(OVERRIDE_OFF.setting)
setOverride(OVERRIDE_ON.setting)
// Have a consistent override until reboot
assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse()
- assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
- .isEqualTo(OVERRIDE_OFF.setting.toString())
+ verifySystemPropertyNotUpdated()
}
@Test
@EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
@DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
fun isEnabled_systemPropertyOn_overrideOff_featureFlagOff_returnsTrueAndDoesNotUpdateProperty() {
- System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, OVERRIDE_ON.setting.toString())
+ setSystemProperty(OVERRIDE_ON.setting)
setOverride(OVERRIDE_OFF.setting)
// Have a consistent override until reboot
assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue()
- assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
- .isEqualTo(OVERRIDE_ON.setting.toString())
+ verifySystemPropertyNotUpdated()
}
@Test
@EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
@Suppress("ktlint:standard:max-line-length")
fun isEnabled_systemPropertyUnset_overrideOff_featureFlagOn_returnsTrueAndDoesNotUpdateProperty() {
- System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, OVERRIDE_UNSET.setting.toString())
+ setSystemProperty(OVERRIDE_UNSET.setting)
setOverride(OVERRIDE_OFF.setting)
// Have a consistent override until reboot
assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue()
- assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
- .isEqualTo(OVERRIDE_UNSET.setting.toString())
+ verifySystemPropertyNotUpdated()
}
@Test
@@ -441,16 +434,33 @@ class DesktopModeFlagsTest : ShellTestCase() {
}
private fun resetCache() {
- val cachedToggleOverride =
- DesktopModeFlags::class.java.getDeclaredField("cachedToggleOverride")
+ val cachedToggleOverride = DesktopModeFlags::class.java.getDeclaredField("cachedToggleOverride")
cachedToggleOverride.isAccessible = true
cachedToggleOverride.set(null, null)
- // Clear override cache stored in System property
- System.clearProperty(SYSTEM_PROPERTY_OVERRIDE_KEY)
+ setSystemProperty(INVALID_TOGGLE_OVERRIDE_SETTING)
+ }
+
+ private fun setSystemProperty(systemProperty: Int) {
+ ExtendedMockito.doReturn(systemProperty).`when` {
+ SystemProperties.getInt(eq(SYSTEM_PROPERTY_OVERRIDE_KEY), any())
+ }
+ }
+
+ private fun verifySystemPropertySet(systemProperty: Int) {
+ ExtendedMockito.verify {
+ SystemProperties.set(eq(SYSTEM_PROPERTY_OVERRIDE_KEY), eq(systemProperty.toString()))
+ }
+ }
+
+ private fun verifySystemPropertyNotUpdated() {
+ ExtendedMockito.verify(
+ { SystemProperties.set(eq(SYSTEM_PROPERTY_OVERRIDE_KEY), any()) }, Mockito.never())
}
private companion object {
const val SYSTEM_PROPERTY_OVERRIDE_KEY = "sys.wmshell.desktopmode.dev_toggle_override"
+
+ const val INVALID_TOGGLE_OVERRIDE_SETTING = -2
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
index 5b95b1588814..1c5d5e963156 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
@@ -50,7 +50,7 @@ import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
-import android.os.IBinder;
+import android.os.Handler;
import android.window.IWindowContainerToken;
import android.window.WindowContainerToken;
@@ -104,6 +104,7 @@ public class SplitScreenControllerTests extends ShellTestCase {
@Mock SyncTransactionQueue mSyncQueue;
@Mock RootTaskDisplayAreaOrganizer mRootTDAOrganizer;
@Mock ShellExecutor mMainExecutor;
+ @Mock Handler mMainHandler;
@Mock DisplayController mDisplayController;
@Mock DisplayImeController mDisplayImeController;
@Mock DisplayInsetsController mDisplayInsetsController;
@@ -134,7 +135,7 @@ public class SplitScreenControllerTests extends ShellTestCase {
mDisplayInsetsController, mDragAndDropController, mTransitions, mTransactionPool,
mIconProvider, Optional.of(mRecentTasks), mLaunchAdjacentController,
Optional.of(mWindowDecorViewModel), Optional.of(mDesktopTasksController),
- mStageCoordinator, mMultiInstanceHelper, mMainExecutor));
+ mStageCoordinator, mMultiInstanceHelper, mMainExecutor, mMainHandler));
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
index a3009a55198f..29d3fb4cc04e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
@@ -22,6 +22,7 @@ import static org.mockito.Mockito.mock;
import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Rect;
+import android.os.Handler;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
@@ -77,13 +78,13 @@ public class SplitTestUtils {
DisplayController displayController, DisplayImeController imeController,
DisplayInsetsController insetsController, SplitLayout splitLayout,
Transitions transitions, TransactionPool transactionPool,
- ShellExecutor mainExecutor,
+ ShellExecutor mainExecutor, Handler mainHandler,
Optional<RecentTasksController> recentTasks,
LaunchAdjacentController launchAdjacentController,
Optional<WindowDecorViewModel> windowDecorViewModel) {
super(context, displayId, syncQueue, taskOrganizer, mainStage,
sideStage, displayController, imeController, insetsController, splitLayout,
- transitions, transactionPool, mainExecutor, recentTasks,
+ transitions, transactionPool, mainExecutor, mainHandler, recentTasks,
launchAdjacentController, windowDecorViewModel);
// Prepare root task for testing.
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index 34b2eebb15a1..37ef7881bde7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -49,6 +49,7 @@ import static org.mockito.Mockito.verify;
import android.annotation.NonNull;
import android.app.ActivityManager;
+import android.os.Handler;
import android.os.IBinder;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
@@ -107,6 +108,7 @@ public class SplitTransitionTests extends ShellTestCase {
@Mock private IconProvider mIconProvider;
@Mock private WindowDecorViewModel mWindowDecorViewModel;
@Mock private ShellExecutor mMainExecutor;
+ @Mock private Handler mMainHandler;
@Mock private LaunchAdjacentController mLaunchAdjacentController;
@Mock private DefaultMixedHandler mMixedHandler;
@Mock private SplitScreen.SplitInvocationListener mInvocationListener;
@@ -140,7 +142,7 @@ public class SplitTransitionTests extends ShellTestCase {
mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY,
mSyncQueue, mTaskOrganizer, mMainStage, mSideStage, mDisplayController,
mDisplayImeController, mDisplayInsetsController, mSplitLayout, mTransitions,
- mTransactionPool, mMainExecutor, Optional.empty(),
+ mTransactionPool, mMainExecutor, mMainHandler, Optional.empty(),
mLaunchAdjacentController, Optional.empty());
mStageCoordinator.setMixedHandler(mMixedHandler);
mSplitScreenTransitions = mStageCoordinator.getSplitTransitions();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index d18fec2f24ad..eaef704b7d78 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -138,7 +138,8 @@ public class StageCoordinatorTests extends ShellTestCase {
mStageCoordinator = spy(new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
mTaskOrganizer, mMainStage, mSideStage, mDisplayController, mDisplayImeController,
mDisplayInsetsController, mSplitLayout, mTransitions, mTransactionPool,
- mMainExecutor, Optional.empty(), mLaunchAdjacentController, Optional.empty()));
+ mMainExecutor, mMainHandler, Optional.empty(), mLaunchAdjacentController,
+ Optional.empty()));
mDividerLeash = new SurfaceControl.Builder(mSurfaceSession).setName("fakeDivider").build();
when(mSplitLayout.getBounds1()).thenReturn(mBounds1);
@@ -347,8 +348,7 @@ public class StageCoordinatorTests extends ShellTestCase {
assertThat(options.getLaunchRootTask()).isEqualTo(mMainStage.mRootTaskInfo.token);
assertThat(options.getPendingIntentBackgroundActivityStartMode())
- .isEqualTo(ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
- assertThat(options.isPendingIntentBackgroundActivityLaunchAllowedByPermission()).isTrue();
+ .isEqualTo(ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS);
}
@Test
@@ -359,10 +359,11 @@ public class StageCoordinatorTests extends ShellTestCase {
mMainStage.mRootTaskInfo = new TestRunningTaskInfoBuilder().setVisible(true).build();
when(mStageCoordinator.isSplitActive()).thenReturn(true);
when(mStageCoordinator.isSplitScreenVisible()).thenReturn(true);
+ when(mStageCoordinator.willSleepOnFold()).thenReturn(true);
mStageCoordinator.onFoldedStateChanged(true);
- assertEquals(mStageCoordinator.mTopStageAfterFoldDismiss, STAGE_TYPE_MAIN);
+ assertEquals(mStageCoordinator.mLastActiveStage, STAGE_TYPE_MAIN);
mStageCoordinator.onFinishedWakingUp();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
index b1803e97b107..aeae0bebb697 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
@@ -30,6 +30,7 @@ import android.hardware.display.DisplayManager
import android.hardware.display.VirtualDisplay
import android.hardware.input.InputManager
import android.os.Handler
+import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.annotations.RequiresFlagsEnabled
import android.platform.test.flag.junit.CheckFlagsRule
@@ -62,6 +63,7 @@ import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.TestRunningTaskInfoBuilder
+import com.android.wm.shell.TestShellExecutor
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayInsetsController
import com.android.wm.shell.common.DisplayLayout
@@ -71,6 +73,7 @@ import com.android.wm.shell.desktopmode.DesktopTasksController
import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
+import com.android.wm.shell.splitscreen.SplitScreenController
import com.android.wm.shell.sysui.KeyguardChangeListener
import com.android.wm.shell.sysui.ShellCommandHandler
import com.android.wm.shell.sysui.ShellController
@@ -78,8 +81,6 @@ import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel.DesktopModeOnInsetsChangedListener
import com.android.wm.shell.windowdecor.common.OnTaskActionClickListener
-import java.util.Optional
-import java.util.function.Supplier
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Rule
@@ -99,6 +100,8 @@ import org.mockito.kotlin.eq
import org.mockito.kotlin.spy
import org.mockito.kotlin.whenever
import org.mockito.quality.Strictness
+import java.util.Optional
+import java.util.function.Supplier
/**
* Tests of [DesktopModeWindowDecorViewModel]
@@ -122,6 +125,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
@Mock private lateinit var mockMainChoreographer: Choreographer
@Mock private lateinit var mockTaskOrganizer: ShellTaskOrganizer
@Mock private lateinit var mockDisplayController: DisplayController
+ @Mock private lateinit var mockSplitScreenController: SplitScreenController
@Mock private lateinit var mockDisplayLayout: DisplayLayout
@Mock private lateinit var displayInsetsController: DisplayInsetsController
@Mock private lateinit var mockSyncQueue: SyncTransactionQueue
@@ -136,6 +140,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
@Mock private lateinit var mockShellCommandHandler: ShellCommandHandler
@Mock private lateinit var mockWindowManager: IWindowManager
@Mock private lateinit var mockInteractionJankMonitor: InteractionJankMonitor
+ private val bgExecutor = TestShellExecutor()
private val transactionFactory = Supplier<SurfaceControl.Transaction> {
SurfaceControl.Transaction()
@@ -155,6 +160,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
mockShellExecutor,
mockMainHandler,
mockMainChoreographer,
+ bgExecutor,
shellInit,
mockShellCommandHandler,
mockWindowManager,
@@ -171,7 +177,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
mockRootTaskDisplayAreaOrganizer,
windowDecorByTaskIdSpy, mockInteractionJankMonitor
)
-
+ desktopModeWindowDecorViewModel.setSplitScreenController(mockSplitScreenController)
whenever(mockDisplayController.getDisplayLayout(any())).thenReturn(mockDisplayLayout)
whenever(mockDisplayLayout.stableInsets()).thenReturn(STABLE_INSETS)
whenever(mockInputMonitorFactory.create(any(), any())).thenReturn(mockInputMonitor)
@@ -204,10 +210,12 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
verify(mockDesktopModeWindowDecorFactory).create(
mContext,
mockDisplayController,
+ mockSplitScreenController,
mockTaskOrganizer,
task,
taskSurface,
mockMainHandler,
+ bgExecutor,
mockMainChoreographer,
mockSyncQueue,
mockRootTaskDisplayAreaOrganizer
@@ -228,10 +236,12 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
verify(mockDesktopModeWindowDecorFactory, never()).create(
mContext,
mockDisplayController,
+ mockSplitScreenController,
mockTaskOrganizer,
task,
taskSurface,
mockMainHandler,
+ bgExecutor,
mockMainChoreographer,
mockSyncQueue,
mockRootTaskDisplayAreaOrganizer
@@ -243,10 +253,12 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
verify(mockDesktopModeWindowDecorFactory, times(1)).create(
mContext,
mockDisplayController,
+ mockSplitScreenController,
mockTaskOrganizer,
task,
taskSurface,
mockMainHandler,
+ bgExecutor,
mockMainChoreographer,
mockSyncQueue,
mockRootTaskDisplayAreaOrganizer
@@ -254,6 +266,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
}
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
fun testCreateAndDisposeEventReceiver() {
val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM)
setUpMockDecorationForTask(task)
@@ -266,6 +279,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
}
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
fun testEventReceiversOnMultipleDisplays() {
val secondaryDisplay = createVirtualDisplay() ?: return
val secondaryDisplayId = secondaryDisplay.display.displayId
@@ -344,7 +358,8 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
onTaskChanging(task)
verify(mockDesktopModeWindowDecorFactory, never())
- .create(any(), any(), any(), eq(task), any(), any(), any(), any(), any())
+ .create(any(), any(), any(), any(), eq(task), any(), any(), any(), any(), any(),
+ any())
}
@Test
@@ -365,7 +380,8 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
onTaskOpening(task)
verify(mockDesktopModeWindowDecorFactory)
- .create(any(), any(), any(), eq(task), any(), any(), any(), any(), any())
+ .create(any(), any(), any(), any(), eq(task), any(), any(), any(), any(),
+ any(), any())
} finally {
mockitoSession.finishMocking()
}
@@ -382,7 +398,8 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
onTaskOpening(task)
verify(mockDesktopModeWindowDecorFactory, never())
- .create(any(), any(), any(), eq(task), any(), any(), any(), any(), any())
+ .create(any(), any(), any(), any(), eq(task), any(), any(), any(), any(), any(),
+ any())
}
@Test
@@ -399,7 +416,8 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
onTaskOpening(task)
verify(mockDesktopModeWindowDecorFactory, never())
- .create(any(), any(), any(), eq(task), any(), any(), any(), any(), any())
+ .create(any(), any(), any(), any(), eq(task), any(), any(), any(), any(),
+ any(), any())
}
@Test
@@ -496,7 +514,8 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
onTaskOpening(task)
verify(mockDesktopModeWindowDecorFactory, never())
- .create(any(), any(), any(), eq(task), any(), any(), any(), any(), any())
+ .create(any(), any(), any(), any(), eq(task), any(), any(), any(), any(), any(),
+ any())
} finally {
mockitoSession.finishMocking()
}
@@ -520,7 +539,8 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
onTaskOpening(task)
verify(mockDesktopModeWindowDecorFactory)
- .create(any(), any(), any(), eq(task), any(), any(), any(), any(), any())
+ .create(any(), any(), any(), any(), eq(task), any(), any(), any(), any(), any(),
+ any())
} finally {
mockitoSession.finishMocking()
}
@@ -543,7 +563,8 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
onTaskOpening(task)
verify(mockDesktopModeWindowDecorFactory)
- .create(any(), any(), any(), eq(task), any(), any(), any(), any(), any())
+ .create(any(), any(), any(), any(), eq(task), any(), any(), any(), any(), any(),
+ any())
} finally {
mockitoSession.finishMocking()
}
@@ -682,7 +703,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
val decoration = mock(DesktopModeWindowDecoration::class.java)
whenever(
mockDesktopModeWindowDecorFactory.create(
- any(), any(), any(), eq(task), any(), any(), any(), any(), any())
+ any(), any(), any(), any(), eq(task), any(), any(), any(), any(), any(), any())
).thenReturn(decoration)
decoration.mTaskInfo = task
whenever(decoration.isFocused).thenReturn(task.isFocused)
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 d8606093ac5c..412fef30d4fb 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
@@ -82,9 +82,12 @@ import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestRunningTaskInfoBuilder;
+import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
+import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.windowdecor.WindowDecoration.RelayoutParams;
import com.android.wm.shell.windowdecor.common.OnTaskActionClickListener;
@@ -127,6 +130,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
@Mock
private DisplayController mMockDisplayController;
@Mock
+ private SplitScreenController mMockSplitScreenController;
+ @Mock
private ShellTaskOrganizer mMockShellTaskOrganizer;
@Mock
private Choreographer mMockChoreographer;
@@ -165,6 +170,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
private SurfaceControl.Transaction mMockTransaction;
private StaticMockitoSession mMockitoSession;
private TestableContext mTestableContext;
+ private ShellExecutor mBgExecutor = new TestShellExecutor();
/** Set up run before test class. */
@BeforeClass
@@ -392,6 +398,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
}
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
public void relayout_fullscreenTask_appliesTransactionImmediately() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
@@ -418,6 +425,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
}
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
public void relayout_fullscreenTask_doesNotCreateViewHostImmediately() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
@@ -429,6 +437,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
}
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
public void relayout_fullscreenTask_postsViewHostCreation() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
@@ -457,6 +466,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
}
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
public void relayout_removesExistingHandlerCallback() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
@@ -471,6 +481,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
}
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
public void close_removesExistingHandlerCallback() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
@@ -656,8 +667,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
ActivityManager.RunningTaskInfo taskInfo,
MaximizeMenuFactory maximizeMenuFactory) {
final DesktopModeWindowDecoration windowDecor = new DesktopModeWindowDecoration(mContext,
- mMockDisplayController, mMockShellTaskOrganizer, taskInfo, mMockSurfaceControl,
- mMockHandler, mMockChoreographer, mMockSyncQueue, mMockRootTaskDisplayAreaOrganizer,
+ mMockDisplayController, mMockSplitScreenController, mMockShellTaskOrganizer,
+ taskInfo, mMockSurfaceControl, mMockHandler, mBgExecutor, mMockChoreographer,
+ mMockSyncQueue, mMockRootTaskDisplayAreaOrganizer,
SurfaceControl.Builder::new, mMockTransactionSupplier,
WindowContainerTransaction::new, SurfaceControl::new,
mMockSurfaceControlViewHostFactory, maximizeMenuFactory);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
index adda9a688172..e548f8f1eb2a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
@@ -130,7 +130,7 @@ class HandleMenuTest : ShellTestCase() {
@EnableFlags(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
fun testFullscreenMenuUsesSystemViewContainer() {
createTaskInfo(WINDOWING_MODE_FULLSCREEN, SPLIT_POSITION_UNDEFINED)
- val handleMenu = createAndShowHandleMenu()
+ val handleMenu = createAndShowHandleMenu(SPLIT_POSITION_UNDEFINED)
assertTrue(handleMenu.handleMenuViewContainer is AdditionalSystemViewContainer)
// Verify menu is created at coordinates that, when added to WindowManager,
// show at the top-center of display.
@@ -142,7 +142,7 @@ class HandleMenuTest : ShellTestCase() {
@EnableFlags(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
fun testFreeformMenu_usesViewHostViewContainer() {
createTaskInfo(WINDOWING_MODE_FREEFORM, SPLIT_POSITION_UNDEFINED)
- handleMenu = createAndShowHandleMenu()
+ handleMenu = createAndShowHandleMenu(SPLIT_POSITION_UNDEFINED)
assertTrue(handleMenu.handleMenuViewContainer is AdditionalViewHostViewContainer)
// Verify menu is created near top-left of task.
val expected = Point(MENU_START_MARGIN, MENU_TOP_MARGIN)
@@ -153,7 +153,7 @@ class HandleMenuTest : ShellTestCase() {
@EnableFlags(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
fun testSplitLeftMenu_usesSystemViewContainer() {
createTaskInfo(WINDOWING_MODE_MULTI_WINDOW, SPLIT_POSITION_TOP_OR_LEFT)
- handleMenu = createAndShowHandleMenu()
+ handleMenu = createAndShowHandleMenu(SPLIT_POSITION_TOP_OR_LEFT)
assertTrue(handleMenu.handleMenuViewContainer is AdditionalSystemViewContainer)
// Verify menu is created at coordinates that, when added to WindowManager,
// show at the top-center of split left task.
@@ -168,7 +168,7 @@ class HandleMenuTest : ShellTestCase() {
@EnableFlags(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
fun testSplitRightMenu_usesSystemViewContainer() {
createTaskInfo(WINDOWING_MODE_MULTI_WINDOW, SPLIT_POSITION_BOTTOM_OR_RIGHT)
- handleMenu = createAndShowHandleMenu()
+ handleMenu = createAndShowHandleMenu(SPLIT_POSITION_BOTTOM_OR_RIGHT)
assertTrue(handleMenu.handleMenuViewContainer is AdditionalSystemViewContainer)
// Verify menu is created at coordinates that, when added to WindowManager,
// show at the top-center of split right task.
@@ -208,16 +208,30 @@ class HandleMenuTest : ShellTestCase() {
}
}
- private fun createAndShowHandleMenu(): HandleMenu {
+ private fun createAndShowHandleMenu(splitPosition: Int): HandleMenu {
val layoutId = if (mockDesktopWindowDecoration.mTaskInfo.isFreeform) {
R.layout.desktop_mode_app_header
} else {
R.layout.desktop_mode_app_handle
}
+ val captionX = when (mockDesktopWindowDecoration.mTaskInfo.windowingMode) {
+ WINDOWING_MODE_FULLSCREEN -> (DISPLAY_BOUNDS.width() / 2) - (HANDLE_WIDTH / 2)
+ WINDOWING_MODE_FREEFORM -> 0
+ WINDOWING_MODE_MULTI_WINDOW -> {
+ if (splitPosition == SPLIT_POSITION_TOP_OR_LEFT) {
+ (SPLIT_LEFT_BOUNDS.width() / 2) - (HANDLE_WIDTH / 2)
+ } else {
+ (SPLIT_RIGHT_BOUNDS.width() / 2) - (HANDLE_WIDTH / 2)
+ }
+ }
+ else -> error("Invalid windowing mode")
+ }
val handleMenu = HandleMenu(mockDesktopWindowDecoration, layoutId,
onClickListener, onTouchListener, appIcon, appName, displayController,
- splitScreenController, true /* shouldShowWindowingPill */,
- true /* shouldShowBrowserPill */, 50 /* captionHeight */)
+ splitScreenController, shouldShowWindowingPill = true,
+ shouldShowBrowserPill = true, captionWidth = HANDLE_WIDTH, captionHeight = 50,
+ captionX = captionX
+ )
handleMenu.show()
return handleMenu
}
@@ -233,5 +247,6 @@ class HandleMenuTest : ShellTestCase() {
private const val MENU_START_MARGIN = 20
private const val MENU_PILL_ELEVATION = 2
private const val MENU_PILL_SPACING_MARGIN = 4
+ private const val HANDLE_WIDTH = 80
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainerTest.kt
index d3e996b12e1f..3b490550ab61 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainerTest.kt
@@ -68,12 +68,12 @@ class AdditionalSystemViewContainerTest : ShellTestCase() {
fun testReleaseView_ViewRemoved() {
viewContainer = AdditionalSystemViewContainer(
mockContext,
- R.layout.desktop_mode_window_decor_handle_menu,
TASK_ID,
X,
Y,
WIDTH,
- HEIGHT
+ HEIGHT,
+ R.layout.desktop_mode_window_decor_handle_menu
)
verify(mockWindowManager).addView(eq(mockView), any())
viewContainer.releaseView()