Add display focus e2e platform test scenarios
Bug: 371094633
Flag: com.android.window.flags.enable_display_focus_in_shell_transitions
Test: atest OpenAndFocusTest
Test: atest ClickAndFocusTest
Test: atest DragAndKeepFocusTest
Test: atest MoveToNextDisplayAndFocusTest
Test: atest CloseThenMoveFocusTest
Change-Id: I2d67bb68697f6ad6a9c5c89320d28ae1578f0457
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/fundamentals/focus/ClickAndFocusTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/fundamentals/focus/ClickAndFocusTest.kt
new file mode 100644
index 0000000..4697cde
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/fundamentals/focus/ClickAndFocusTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2025 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.functional.fundamentals.focus
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.ClickAndFocus
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [ClickAndFocus]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class ClickAndFocusTest : ClickAndFocus()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/fundamentals/focus/CloseThenMoveFocusTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/fundamentals/focus/CloseThenMoveFocusTest.kt
new file mode 100644
index 0000000..0bec61e
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/fundamentals/focus/CloseThenMoveFocusTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2025 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.functional.fundamentals.focus
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.CloseThenMoveFocus
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [CloseThenMoveFocus]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class CloseThenMoveFocusTest : CloseThenMoveFocus()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/fundamentals/focus/DragAndKeepFocusTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/fundamentals/focus/DragAndKeepFocusTest.kt
new file mode 100644
index 0000000..dcc1023
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/fundamentals/focus/DragAndKeepFocusTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2025 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.functional.fundamentals.focus
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.DragAndKeepFocus
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [DragAndKeppFocus]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class DragAndKeepFocusTest : DragAndKeepFocus()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/fundamentals/focus/MoveToNextDisplayAndFocusTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/fundamentals/focus/MoveToNextDisplayAndFocusTest.kt
new file mode 100644
index 0000000..017b72d
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/fundamentals/focus/MoveToNextDisplayAndFocusTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2025 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.functional.fundamentals.focus
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.MoveToNextDisplayAndFocus
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [MoveToNextDisplayAndFocus]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class MoveToNextDisplayAndFocusTest : MoveToNextDisplayAndFocus()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/fundamentals/focus/OpenAndFocusTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/fundamentals/focus/OpenAndFocusTest.kt
new file mode 100644
index 0000000..f2e5bb4
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/fundamentals/focus/OpenAndFocusTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2025 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.functional.fundamentals.focus
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.OpenAndFocus
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [OpenAndFocus]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class OpenAndFocusTest : OpenAndFocus()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ClickAndFocus.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ClickAndFocus.kt
new file mode 100644
index 0000000..3610273
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ClickAndFocus.kt
@@ -0,0 +1,84 @@
+/* Copyright (C) 2025 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.scenarios
+
+import android.platform.test.annotations.EnableFlags
+import android.tools.PlatformConsts.DEFAULT_DISPLAY
+import android.tools.traces.parsers.WindowManagerStateHelper
+import android.view.KeyEvent.KEYCODE_MINUS
+import android.view.KeyEvent.META_META_ON
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import androidx.test.uiautomator.UiDevice
+import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
+import com.android.server.wm.flicker.helpers.KeyEventHelper
+import com.android.server.wm.flicker.helpers.MailAppHelper
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.window.flags.Flags
+import com.android.wm.shell.shared.desktopmode.DesktopState
+import org.junit.After import org.junit.Assume import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+import platform.test.desktop.SimulatedConnectedDisplayTestRule
+
+
+/**
+ * Base scenario test to test if the display if focused after click.
+ */
+@Ignore("Test Base Class")
+@EnableFlags(
+ Flags.FLAG_ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS,
+)
+abstract class ClickAndFocus() {
+ private val wmHelper = WindowManagerStateHelper(getInstrumentation())
+
+ private val device = UiDevice.getInstance(getInstrumentation())
+ private val testAppInMainDisplay = DesktopModeAppHelper(SimpleAppHelper(getInstrumentation()))
+ private val testAppInExternalDisplay =
+ DesktopModeAppHelper(MailAppHelper(getInstrumentation()))
+ private val keyEventHelper = KeyEventHelper(getInstrumentation())
+
+ @get:Rule(order = 0) val connectedDisplayRule = SimulatedConnectedDisplayTestRule()
+
+ @Before
+ fun setup() {
+ connectedDisplayRule.setupTestDisplay()
+ Assume.assumeTrue(
+ DesktopState.fromContext(getInstrumentation().context)
+ .isDesktopModeSupportedOnDisplay(DEFAULT_DISPLAY)
+ )
+ // TODO(b/426420246): Use launchViaIntentOnDisplay
+ testAppInExternalDisplay.launchViaIntent(wmHelper)
+ testAppInExternalDisplay.moveToNextDisplayViaKeyboard(
+ wmHelper,
+ connectedDisplayRule.addedDisplays.first()
+ )
+ testAppInMainDisplay.launchViaIntent(wmHelper)
+ }
+
+ @Test
+ open fun clickAndFocus() {
+ testAppInExternalDisplay.clickCaption(wmHelper, device)
+ // Send minimize via keyboard and observe window to check display focus.
+ keyEventHelper.press(KEYCODE_MINUS, META_META_ON)
+ }
+
+ @After
+ fun teardown() {
+ testAppInMainDisplay.exit(wmHelper)
+ testAppInExternalDisplay.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/CloseThenMoveFocus.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/CloseThenMoveFocus.kt
new file mode 100644
index 0000000..f7123e2
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/CloseThenMoveFocus.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2025 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.scenarios
+
+import android.platform.test.annotations.EnableFlags
+import android.tools.PlatformConsts.DEFAULT_DISPLAY
+import android.tools.traces.parsers.WindowManagerStateHelper
+import android.view.KeyEvent.KEYCODE_MINUS
+import android.view.KeyEvent.META_META_ON
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import androidx.test.uiautomator.UiDevice
+import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
+import com.android.server.wm.flicker.helpers.KeyEventHelper
+import com.android.server.wm.flicker.helpers.MailAppHelper
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.window.flags.Flags
+import com.android.wm.shell.shared.desktopmode.DesktopState
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+import platform.test.desktop.SimulatedConnectedDisplayTestRule
+
+
+/**
+ * Base scenario test to test if the remaining window in other display is focused after the focused
+ * window is closed.
+ */
+@Ignore("Test Base Class")
+@EnableFlags(
+ Flags.FLAG_ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS,
+)
+abstract class CloseThenMoveFocus() {
+ private val wmHelper = WindowManagerStateHelper(getInstrumentation())
+
+ private val device = UiDevice.getInstance(getInstrumentation())
+ private val testAppInMainDisplay = DesktopModeAppHelper(SimpleAppHelper(getInstrumentation()))
+ private val testAppInExternalDisplay =
+ DesktopModeAppHelper(MailAppHelper(getInstrumentation()))
+ private val keyEventHelper = KeyEventHelper(getInstrumentation())
+
+ @get:Rule(order = 0) val connectedDisplayRule = SimulatedConnectedDisplayTestRule()
+
+ @Before
+ fun setup() {
+ connectedDisplayRule.setupTestDisplay()
+ Assume.assumeTrue(
+ DesktopState.fromContext(getInstrumentation().context)
+ .isDesktopModeSupportedOnDisplay(DEFAULT_DISPLAY)
+ )
+ testAppInMainDisplay.enterDesktopMode(wmHelper, device)
+ // TODO(b/426420246): Use launchViaIntentOnDisplay
+ testAppInExternalDisplay.launchViaIntent(wmHelper)
+ testAppInExternalDisplay.moveToNextDisplayViaKeyboard(
+ wmHelper,
+ connectedDisplayRule.addedDisplays.first()
+ )
+ testAppInExternalDisplay.enterDesktopMode(wmHelper, device)
+ }
+
+ @Test
+ open fun closeThenMoveFocus() {
+ testAppInExternalDisplay.clickCaption(
+ wmHelper,
+ device,
+ connectedDisplayRule.addedDisplays.first()
+ )
+ testAppInExternalDisplay.closeDesktopApp(wmHelper, device)
+ // Send minimize via keyboard and observe window to check display focus.
+ keyEventHelper.press(KEYCODE_MINUS, META_META_ON)
+ }
+
+ @After
+ fun teardown() {
+ testAppInMainDisplay.exit(wmHelper)
+ testAppInExternalDisplay.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAndKeepFocus.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAndKeepFocus.kt
new file mode 100644
index 0000000..e5d0a92
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAndKeepFocus.kt
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2025 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.scenarios
+
+import android.graphics.Point
+import android.hardware.display.DisplayManager
+import android.platform.test.annotations.EnableFlags
+import android.tools.PlatformConsts.DEFAULT_DISPLAY
+import android.tools.traces.parsers.WindowManagerStateHelper
+import android.view.Display.DEFAULT_DISPLAY
+import android.view.DisplayInfo
+import android.view.KeyEvent.KEYCODE_MINUS
+import android.view.KeyEvent.META_META_ON
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import androidx.test.uiautomator.UiDevice
+import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
+import com.android.server.wm.flicker.helpers.KeyEventHelper
+import com.android.server.wm.flicker.helpers.MailAppHelper
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.window.flags.Flags
+import com.android.wm.shell.shared.desktopmode.DesktopState
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+import platform.test.desktop.DesktopMouseTestRule
+import platform.test.desktop.SimulatedConnectedDisplayTestRule
+
+
+/**
+ * Base scenario test to test if the window dragged to other display still keeps the focus.
+ */
+@Ignore("Test Base Class")
+@EnableFlags(
+ Flags.FLAG_ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS,
+)
+abstract class DragAndKeepFocus() {
+ private val wmHelper = WindowManagerStateHelper(getInstrumentation())
+
+ private val device = UiDevice.getInstance(getInstrumentation())
+ private val testAppInMainDisplay = DesktopModeAppHelper(SimpleAppHelper(getInstrumentation()))
+ private val testAppInExternalDisplay =
+ DesktopModeAppHelper(MailAppHelper(getInstrumentation()))
+ private val displayManager =
+ getInstrumentation().targetContext.getSystemService(DisplayManager::class.java)
+ private val keyEventHelper = KeyEventHelper(getInstrumentation())
+
+ @get:Rule(order = 0) val connectedDisplayRule = SimulatedConnectedDisplayTestRule()
+ @get:Rule(order = 1) val desktopMouseRule = DesktopMouseTestRule()
+
+ @Before
+ fun setup() {
+ connectedDisplayRule.setupTestDisplay()
+ Assume.assumeTrue(
+ DesktopState.fromContext(getInstrumentation().context)
+ .isDesktopModeSupportedOnDisplay(DEFAULT_DISPLAY)
+ )
+ testAppInMainDisplay.launchViaIntent(wmHelper)
+ testAppInExternalDisplay.launchViaIntent(wmHelper)
+ }
+
+ @Test
+ open fun dragAndKeepFocus() {
+ val captionBounds =
+ checkNotNull(
+ testAppInExternalDisplay.getCaptionForTheApp(wmHelper, device)?.visibleBounds
+ )
+ val dragCoords = Point(captionBounds.centerX(), captionBounds.centerY())
+
+ // Move cursor to designated drag point
+ desktopMouseRule.move(DEFAULT_DISPLAY, dragCoords.x, dragCoords.y)
+
+ // Start drag and move
+ desktopMouseRule.startDrag()
+ val displayInfo = DisplayInfo().also {
+ displayManager.getDisplay(
+ connectedDisplayRule.addedDisplays.first()
+ ).getDisplayInfo(it)
+ }
+ desktopMouseRule.move(
+ connectedDisplayRule.addedDisplays.first(),
+ displayInfo.appWidth / 2,
+ displayInfo.appHeight / 2,
+ )
+ desktopMouseRule.stopDrag()
+ wmHelper.StateSyncBuilder()
+ .withAppTransitionIdle(connectedDisplayRule.addedDisplays.first())
+ .waitForAndVerify()
+
+ // Send minimize via keyboard and observe window to check display focus.
+ keyEventHelper.press(KEYCODE_MINUS, META_META_ON)
+ }
+
+ @After
+ fun teardown() {
+ testAppInMainDisplay.exit(wmHelper)
+ testAppInExternalDisplay.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MoveToNextDisplayAndFocus.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MoveToNextDisplayAndFocus.kt
new file mode 100644
index 0000000..40cec79
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MoveToNextDisplayAndFocus.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2025 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.scenarios
+
+import android.platform.test.annotations.EnableFlags
+import android.tools.PlatformConsts.DEFAULT_DISPLAY
+import android.tools.traces.parsers.WindowManagerStateHelper
+import android.view.KeyEvent.KEYCODE_MINUS
+import android.view.KeyEvent.META_META_ON
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import androidx.test.uiautomator.UiDevice
+import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
+import com.android.server.wm.flicker.helpers.KeyEventHelper
+import com.android.server.wm.flicker.helpers.MailAppHelper
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.window.flags.Flags
+import com.android.wm.shell.shared.desktopmode.DesktopState
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+import platform.test.desktop.SimulatedConnectedDisplayTestRule
+
+
+/**
+ * Base scenario test to test if the window moved to next display via keyboard keeps the focus.
+ */
+@Ignore("Test Base Class")
+@EnableFlags(
+ Flags.FLAG_ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS,
+)
+abstract class MoveToNextDisplayAndFocus() {
+ private val wmHelper = WindowManagerStateHelper(getInstrumentation())
+
+ private val device = UiDevice.getInstance(getInstrumentation())
+ private val testAppInMainDisplay = DesktopModeAppHelper(SimpleAppHelper(getInstrumentation()))
+ private val testAppInExternalDisplay =
+ DesktopModeAppHelper(MailAppHelper(getInstrumentation()))
+ private val keyEventHelper = KeyEventHelper(getInstrumentation())
+
+ @get:Rule(order = 0) val connectedDisplayRule = SimulatedConnectedDisplayTestRule()
+
+ @Before
+ fun setup() {
+ connectedDisplayRule.setupTestDisplay()
+ Assume.assumeTrue(
+ DesktopState.fromContext(getInstrumentation().context)
+ .isDesktopModeSupportedOnDisplay(DEFAULT_DISPLAY)
+ )
+ testAppInMainDisplay.launchViaIntent(wmHelper)
+ testAppInExternalDisplay.launchViaIntent(wmHelper)
+ }
+
+ @Test
+ open fun moveToNextDisplayAndFocus() {
+ testAppInExternalDisplay.moveToNextDisplayViaKeyboard(
+ wmHelper,
+ connectedDisplayRule.addedDisplays.first()
+ )
+
+ // Send minimize via keyboard and observe window to check display focus.
+ keyEventHelper.press(KEYCODE_MINUS, META_META_ON)
+ }
+
+ @After
+ fun teardown() {
+ testAppInMainDisplay.exit(wmHelper)
+ testAppInExternalDisplay.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenAndFocus.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenAndFocus.kt
new file mode 100644
index 0000000..42062ee
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenAndFocus.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2025 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.scenarios
+
+import android.platform.test.annotations.EnableFlags
+import android.tools.PlatformConsts.DEFAULT_DISPLAY
+import android.tools.traces.parsers.WindowManagerStateHelper
+import android.view.KeyEvent.KEYCODE_MINUS
+import android.view.KeyEvent.META_META_ON
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import androidx.test.uiautomator.UiDevice
+import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
+import com.android.server.wm.flicker.helpers.KeyEventHelper
+import com.android.server.wm.flicker.helpers.MailAppHelper
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.window.flags.Flags
+import com.android.wm.shell.shared.desktopmode.DesktopState
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+import platform.test.desktop.SimulatedConnectedDisplayTestRule
+
+
+/**
+ * Base scenario test to test if newly opened window gets focus.
+ */
+@Ignore("Test Base Class")
+@EnableFlags(
+ Flags.FLAG_ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS,
+)
+abstract class OpenAndFocus() {
+ private val wmHelper = WindowManagerStateHelper(getInstrumentation())
+
+ private val device = UiDevice.getInstance(getInstrumentation())
+ private val testAppInMainDisplay = DesktopModeAppHelper(SimpleAppHelper(getInstrumentation()))
+ private val testAppInExternalDisplay =
+ DesktopModeAppHelper(MailAppHelper(getInstrumentation()))
+ private val keyEventHelper = KeyEventHelper(getInstrumentation())
+
+ @get:Rule(order = 0) val connectedDisplayRule = SimulatedConnectedDisplayTestRule()
+
+ @Before
+ fun setup() {
+ connectedDisplayRule.setupTestDisplay()
+ Assume.assumeTrue(
+ DesktopState.fromContext(getInstrumentation().context)
+ .isDesktopModeSupportedOnDisplay(DEFAULT_DISPLAY)
+ )
+ testAppInMainDisplay.launchViaIntent(wmHelper)
+ }
+
+ @Test
+ open fun openAndFocus() {
+ // TODO(b/426420246): Use launchViaIntentOnDisplay
+ testAppInExternalDisplay.launchViaIntent(wmHelper)
+ testAppInExternalDisplay.moveToNextDisplayViaKeyboard(
+ wmHelper,
+ connectedDisplayRule.addedDisplays.first()
+ )
+
+ // Send minimize via keyboard and observe window to check display focus.
+ keyEventHelper.press(KEYCODE_MINUS, META_META_ON)
+ }
+
+ @After
+ fun teardown() {
+ testAppInMainDisplay.exit(wmHelper)
+ testAppInExternalDisplay.exit(wmHelper)
+ }
+}
diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt
index 09d87a9..995326e 100644
--- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt
@@ -32,6 +32,7 @@
import android.tools.helpers.SYSTEMUI_PACKAGE
import android.tools.traces.parsers.WindowManagerStateHelper
import android.tools.traces.wm.WindowingMode
+import android.view.Display.DEFAULT_DISPLAY
import android.view.KeyEvent.KEYCODE_D
import android.view.KeyEvent.KEYCODE_DPAD_DOWN
import android.view.KeyEvent.KEYCODE_DPAD_UP
@@ -596,7 +597,7 @@
wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
}
- fun exitDesktopModeToFullScreenWithAppHeader(wmHelper:WindowManagerStateHelper) {
+ fun exitDesktopModeToFullScreenWithAppHeader(wmHelper: WindowManagerStateHelper) {
val openMenuButton = getDesktopAppViewByRes(OPEN_MENU_BUTTON)
openMenuButton?.click()
wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
@@ -611,7 +612,7 @@
wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
}
- fun exitDesktopModeToSplitScreenWithAppHeader(wmHelper:WindowManagerStateHelper) {
+ fun exitDesktopModeToSplitScreenWithAppHeader(wmHelper: WindowManagerStateHelper) {
val openMenuButton = getDesktopAppViewByRes(OPEN_MENU_BUTTON)
openMenuButton?.click()
wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
@@ -676,6 +677,22 @@
}
}
+ fun clickCaption(
+ wmHelper: WindowManagerStateHelper,
+ device: UiDevice,
+ displayId: Int = DEFAULT_DISPLAY
+ ) {
+ val caption = checkNotNull(getCaptionForTheApp(wmHelper, device)) {
+ "Unable to find caption"
+ }
+ caption.click()
+ wmHelper
+ .StateSyncBuilder()
+ .withAppTransitionIdle(displayId)
+ .withTopVisibleApp(innerHelper)
+ .waitForAndVerify()
+ }
+
private fun getDesktopAppViewByRes(viewResId: String): UiObject2 =
DeviceHelpers.waitForObj(By.res(SYSTEMUI_PACKAGE, viewResId), TIMEOUT)