Add flicker test for snap resizing using keyboard shortcuts.
Bug: 375400066
Flag: com.android.window.flags.enable_task_resizing_keyboard_shortcuts
Test: atest SnapResizeAppWindowLeftWithKeyboard
Test: atest SnapResizeAppWindowRightWithKeyboard
Change-Id: I899963a7680ac5b9a75309438d8bc818f69613db
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt
index f4f60d7..9972247 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt
@@ -262,6 +262,40 @@
).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
)
+ val SNAP_RESIZE_LEFT_WITH_KEYBOARD =
+ FlickerConfigEntry(
+ scenarioId = ScenarioId("SNAP_RESIZE_LEFT_WITH_KEYBOARD"),
+ extractor =
+ TaggedScenarioExtractorBuilder()
+ .setTargetTag(CujType.CUJ_DESKTOP_MODE_SNAP_RESIZE)
+ .setTransitionMatcher(
+ TaggedCujTransitionMatcher(associatedTransitionRequired = false)
+ )
+ .build(),
+ assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS + listOf(
+ AppWindowCoversLeftHalfScreenAtEnd(
+ DESKTOP_MODE_APP, SNAP_WINDOW_MAX_DIFF_THRESHOLD_RATIO
+ )
+ ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
+ )
+
+ val SNAP_RESIZE_RIGHT_WITH_KEYBOARD =
+ FlickerConfigEntry(
+ scenarioId = ScenarioId("SNAP_RESIZE_RIGHT_WITH_KEYBOARD"),
+ extractor =
+ TaggedScenarioExtractorBuilder()
+ .setTargetTag(CujType.CUJ_DESKTOP_MODE_SNAP_RESIZE)
+ .setTransitionMatcher(
+ TaggedCujTransitionMatcher(associatedTransitionRequired = false)
+ )
+ .build(),
+ assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS + listOf(
+ AppWindowCoversRightHalfScreenAtEnd(
+ DESKTOP_MODE_APP, SNAP_WINDOW_MAX_DIFF_THRESHOLD_RATIO
+ )
+ ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
+ )
+
val SNAP_RESIZE_LEFT_WITH_DRAG =
FlickerConfigEntry(
scenarioId = ScenarioId("SNAP_RESIZE_LEFT_WITH_DRAG"),
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowLeftWithKeyboard.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowLeftWithKeyboard.kt
new file mode 100644
index 0000000..b0b78a2
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowLeftWithKeyboard.kt
@@ -0,0 +1,48 @@
+/*
+ * 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
+
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.SNAP_RESIZE_LEFT_WITH_KEYBOARD
+import com.android.wm.shell.scenarios.SnapResizeAppWindowWithKeyboardShortcuts
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Snap resize app window using keyboard shortcut META + [.
+ *
+ * Assert that the app window fills the left half the display after being snap resized.
+ */
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class SnapResizeAppWindowLeftWithKeyboard : SnapResizeAppWindowWithKeyboardShortcuts(toLeft = true) {
+ @ExpectedScenarios(["SNAP_RESIZE_LEFT_WITH_KEYBOARD"])
+ @Test
+ override fun snapResizeAppWindowWithKeyboardShortcuts() =
+ super.snapResizeAppWindowWithKeyboardShortcuts()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(SNAP_RESIZE_LEFT_WITH_KEYBOARD)
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowRightWithKeyboard.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowRightWithKeyboard.kt
new file mode 100644
index 0000000..b9b9188
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowRightWithKeyboard.kt
@@ -0,0 +1,50 @@
+/*
+ * 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
+
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.SNAP_RESIZE_RIGHT_WITH_KEYBOARD
+import com.android.wm.shell.scenarios.SnapResizeAppWindowWithKeyboardShortcuts
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Snap resize app window using keyboard shortcut META + ].
+ *
+ * Assert that the app window fills the right half the display after being snap resized.
+ */
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class SnapResizeAppWindowRightWithKeyboard : SnapResizeAppWindowWithKeyboardShortcuts(
+ toLeft = false
+) {
+ @ExpectedScenarios(["SNAP_RESIZE_RIGHT_WITH_KEYBOARD"])
+ @Test
+ override fun snapResizeAppWindowWithKeyboardShortcuts() =
+ super.snapResizeAppWindowWithKeyboardShortcuts()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(SNAP_RESIZE_RIGHT_WITH_KEYBOARD)
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithKeyboardShortcuts.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithKeyboardShortcuts.kt
new file mode 100644
index 0000000..0680644
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithKeyboardShortcuts.kt
@@ -0,0 +1,80 @@
+/*
+ * 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.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.KeyEventHelper
+import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.window.flags.Flags
+import com.android.wm.shell.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
+
+@Ignore("Test Base Class")
+abstract class SnapResizeAppWindowWithKeyboardShortcuts(
+ private val toLeft: Boolean = true,
+ isResizable: Boolean = true
+) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val keyEventHelper = KeyEventHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val testApp = if (isResizable) {
+ DesktopModeAppHelper(SimpleAppHelper(instrumentation))
+ } else {
+ DesktopModeAppHelper(NonResizeableAppHelper(instrumentation))
+ }
+
+ @Rule
+ @JvmField
+ val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, Rotation.ROTATION_90)
+
+ @Before
+ fun setup() {
+ Assume.assumeTrue(Flags.enableDesktopWindowingMode() &&
+ Flags.enableTaskResizingKeyboardShortcuts() && tapl.isTablet)
+ testApp.enterDesktopMode(wmHelper, device)
+ }
+
+ @Test
+ open fun snapResizeAppWindowWithKeyboardShortcuts() {
+ testApp.snapResizeWithKeyboard(
+ wmHelper,
+ instrumentation.context,
+ keyEventHelper,
+ toLeft,
+ )
+ }
+
+ @After
+ fun teardown() {
+ testApp.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 c1c5dc6..2e7b207 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
@@ -28,6 +28,9 @@
import android.tools.helpers.SYSTEMUI_PACKAGE
import android.tools.traces.parsers.WindowManagerStateHelper
import android.tools.traces.wm.WindowingMode
+import android.view.KeyEvent.KEYCODE_LEFT_BRACKET
+import android.view.KeyEvent.KEYCODE_RIGHT_BRACKET
+import android.view.KeyEvent.META_META_ON
import android.view.WindowInsets
import android.view.WindowManager
import android.window.DesktopModeFlags
@@ -213,6 +216,26 @@
?: error("Unable to find object with resource id $buttonResId")
snapResizeButton.click()
+ waitAndVerifySnapResize(wmHelper, context, toLeft)
+ }
+
+ fun snapResizeWithKeyboard(
+ wmHelper: WindowManagerStateHelper,
+ context: Context,
+ keyEventHelper: KeyEventHelper,
+ toLeft: Boolean,
+ ) {
+ val bracketKey = if (toLeft) KEYCODE_LEFT_BRACKET else KEYCODE_RIGHT_BRACKET
+ keyEventHelper.actionDown(bracketKey, META_META_ON)
+ keyEventHelper.actionUp(bracketKey, META_META_ON)
+ waitAndVerifySnapResize(wmHelper, context, toLeft)
+ }
+
+ private fun waitAndVerifySnapResize(
+ wmHelper: WindowManagerStateHelper,
+ context: Context,
+ toLeft: Boolean
+ ) {
val displayRect = getDisplayRect(wmHelper)
val insets = getWindowInsets(
context, WindowInsets.Type.statusBars() or WindowInsets.Type.navigationBars()
diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/KeyEventHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/KeyEventHelper.kt
new file mode 100644
index 0000000..ebd8cc3
--- /dev/null
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/KeyEventHelper.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.server.wm.flicker.helpers
+
+import android.app.Instrumentation
+import android.os.SystemClock
+import android.view.KeyEvent.ACTION_DOWN
+import android.view.KeyEvent.ACTION_UP
+import android.view.KeyEvent
+
+/**
+ * Helper class for injecting a custom key event. This is used for instrumenting keyboard shortcut
+ * actions.
+ */
+class KeyEventHelper(
+ private val instr: Instrumentation,
+) {
+
+ fun actionDown(keyCode: Int, metaState: Int = 0, time: Long = SystemClock.uptimeMillis()) {
+ injectKeyEvent(ACTION_DOWN, keyCode, metaState, downTime = time, eventTime = time)
+ }
+
+ fun actionUp(keyCode: Int, metaState: Int = 0, time: Long = SystemClock.uptimeMillis()) {
+ injectKeyEvent(ACTION_UP, keyCode, metaState, downTime = time, eventTime = time)
+ }
+
+ private fun injectKeyEvent(
+ action: Int,
+ keyCode: Int,
+ metaState: Int = 0,
+ downTime: Long = SystemClock.uptimeMillis(),
+ eventTime: Long = SystemClock.uptimeMillis()
+ ): KeyEvent {
+ val event = KeyEvent(downTime, eventTime, action, keyCode, /* repeat= */ 0, metaState)
+ injectKeyEvent(event)
+ return event
+ }
+
+ private fun injectKeyEvent(event: KeyEvent) {
+ instr.uiAutomation.injectInputEvent(event, true)
+ }
+}
\ No newline at end of file