Loading libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt +4 −0 Original line number Diff line number Diff line Loading @@ -31,6 +31,10 @@ val TEST_APP_PIP_ACTIVITY_COMPONENT_NAME: ComponentName = ComponentName.createRe TEST_APP_PACKAGE_NAME, ".PipActivity") const val TEST_APP_PIP_ACTIVITY_LABEL = "PipApp" const val TEST_APP_PIP_ACTIVITY_WINDOW_NAME = "PipActivity" const val TEST_APP_PIP_MENU_ACTION_NO_OP = "No-Op" const val TEST_APP_PIP_MENU_ACTION_ON = "On" const val TEST_APP_PIP_MENU_ACTION_OFF = "Off" const val TEST_APP_PIP_MENU_ACTION_CLEAR = "Clear" // Test App > Ime Activity val TEST_APP_IME_ACTIVITY_COMPONENT_NAME: ComponentName = ComponentName.createRelative( Loading libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt +6 −0 Original line number Diff line number Diff line Loading @@ -70,6 +70,12 @@ class PipAppHelper( startButton.click() } fun checkWithCustomActionsCheckbox() = uiDevice .findObject(By.res(packageName, "with_custom_actions")) ?.takeIf { it.isCheckable } ?.apply { if (!isChecked) click() } ?: error("'With custom actions' checkbox not found") fun pauseMedia() = mediaController?.transportControls?.pause() ?: error("No active media session found") Loading libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt +96 −0 Original line number Diff line number Diff line Loading @@ -20,7 +20,12 @@ import android.graphics.Rect import androidx.test.filters.RequiresDevice import androidx.test.uiautomator.UiObject2 import com.android.wm.shell.flicker.SYSTEM_UI_PACKAGE_NAME import com.android.wm.shell.flicker.TEST_APP_PIP_MENU_ACTION_CLEAR import com.android.wm.shell.flicker.TEST_APP_PIP_MENU_ACTION_NO_OP import com.android.wm.shell.flicker.TEST_APP_PIP_MENU_ACTION_OFF import com.android.wm.shell.flicker.TEST_APP_PIP_MENU_ACTION_ON import com.android.wm.shell.flicker.wait import org.junit.Assert.assertNull import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test Loading Loading @@ -128,6 +133,7 @@ class TvPipMenuTests : TvPipTestBase() { testApp.clickStartMediaSessionButton() enterPip_openMenu_assertShown() assertFullscreenAndCloseButtonsAreShown() // PiP menu should contain the Pause button val pauseButton = uiDevice.findTvPipMenuElementWithDescription(pauseButtonDescription) Loading @@ -137,6 +143,7 @@ class TvPipMenuTests : TvPipTestBase() { // When we pause media, the button should change from Pause to Play pauseButton.click() assertFullscreenAndCloseButtonsAreShown() // PiP menu should contain the Play button now uiDevice.waitForTvPipMenuElementWithDescription(playButtonDescription) ?: fail("\"Play\" button should be shown in Pip menu if there is an active " + Loading @@ -145,10 +152,99 @@ class TvPipMenuTests : TvPipTestBase() { testApp.closePipWindow() } @Test fun pipMenu_withCustomActions() { // Enter PiP with custom actions. testApp.checkWithCustomActionsCheckbox() enterPip_openMenu_assertShown() // PiP menu should contain "No-Op", "Off" and "Clear" buttons... uiDevice.findTvPipMenuElementWithDescription(TEST_APP_PIP_MENU_ACTION_NO_OP) ?: fail("\"No-Op\" button should be shown in Pip menu") val offButton = uiDevice.findTvPipMenuElementWithDescription(TEST_APP_PIP_MENU_ACTION_OFF) ?: fail("\"Off\" button should be shown in Pip menu") uiDevice.findTvPipMenuElementWithDescription(TEST_APP_PIP_MENU_ACTION_CLEAR) ?: fail("\"Clear\" button should be shown in Pip menu") // ... and should also contain the "Full screen" and "Close" buttons. assertFullscreenAndCloseButtonsAreShown() offButton.click() // Invoking the "Off" action should replace it with the "On" action/button and should // remove the "No-Op" action/button. "Clear" action/button should remain in the menu ... uiDevice.waitForTvPipMenuElementWithDescription(TEST_APP_PIP_MENU_ACTION_ON) ?: fail("\"On\" button should be shown in Pip for a corresponding custom action") assertNull("\"No-Op\" button should not be shown in Pip menu", uiDevice.findTvPipMenuElementWithDescription(TEST_APP_PIP_MENU_ACTION_NO_OP)) val clearButton = uiDevice.findTvPipMenuElementWithDescription(TEST_APP_PIP_MENU_ACTION_CLEAR) ?: fail("\"Clear\" button should be shown in Pip menu") // ... as well as the "Full screen" and "Close" buttons. assertFullscreenAndCloseButtonsAreShown() clearButton.click() // Invoking the "Clear" action should remove all the custom actions and their corresponding // buttons, ... uiDevice.waitUntilTvPipMenuElementWithDescriptionIsGone(TEST_APP_PIP_MENU_ACTION_ON)?.also { isGone -> if (!isGone) fail("\"On\" button should not be shown in Pip menu") } assertNull("\"Off\" button should not be shown in Pip menu", uiDevice.findTvPipMenuElementWithDescription(TEST_APP_PIP_MENU_ACTION_OFF)) assertNull("\"Clear\" button should not be shown in Pip menu", uiDevice.findTvPipMenuElementWithDescription(TEST_APP_PIP_MENU_ACTION_CLEAR)) assertNull("\"No-Op\" button should not be shown in Pip menu", uiDevice.findTvPipMenuElementWithDescription(TEST_APP_PIP_MENU_ACTION_NO_OP)) // ... but the menu should still contain the "Full screen" and "Close" buttons. assertFullscreenAndCloseButtonsAreShown() testApp.closePipWindow() } @Test fun pipMenu_customActions_override_mediaControls() { // Start media session before entering PiP with custom actions. testApp.clickStartMediaSessionButton() testApp.checkWithCustomActionsCheckbox() enterPip_openMenu_assertShown() // PiP menu should contain "No-Op", "Off" and "Clear" buttons for the custom actions... uiDevice.findTvPipMenuElementWithDescription(TEST_APP_PIP_MENU_ACTION_NO_OP) ?: fail("\"No-Op\" button should be shown in Pip menu") uiDevice.findTvPipMenuElementWithDescription(TEST_APP_PIP_MENU_ACTION_OFF) ?: fail("\"Off\" button should be shown in Pip menu") val clearButton = uiDevice.findTvPipMenuElementWithDescription(TEST_APP_PIP_MENU_ACTION_CLEAR) ?: fail("\"Clear\" button should be shown in Pip menu") // ... should also contain the "Full screen" and "Close" buttons, ... assertFullscreenAndCloseButtonsAreShown() // ... but should not contain media buttons. assertNull("\"Play\" button should not be shown in menu when there are custom actions", uiDevice.findTvPipMenuElementWithDescription(playButtonDescription)) assertNull("\"Pause\" button should not be shown in menu when there are custom actions", uiDevice.findTvPipMenuElementWithDescription(pauseButtonDescription)) clearButton.click() // Invoking the "Clear" action should remove all the custom actions, which should bring up // media buttons... uiDevice.waitForTvPipMenuElementWithDescription(pauseButtonDescription) ?: fail("\"Pause\" button should be shown in Pip menu if there is an active " + "playing media session.") // ... while the "Full screen" and "Close" buttons should remain in the menu. assertFullscreenAndCloseButtonsAreShown() testApp.closePipWindow() } private fun enterPip_openMenu_assertShown(): UiObject2 { testApp.clickEnterPipButton() // Pressing the Window key should bring up Pip menu uiDevice.pressWindowKey() return uiDevice.waitForTvPipMenu() ?: fail("Pip menu should have been shown") } private fun assertFullscreenAndCloseButtonsAreShown() { uiDevice.findTvPipMenuCloseButton() ?: fail("\"Close PIP\" button should be shown in Pip menu") uiDevice.findTvPipMenuFullscreenButton() ?: fail("\"Full screen\" button should be shown in Pip menu") } } No newline at end of file libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt +3 −0 Original line number Diff line number Diff line Loading @@ -58,6 +58,9 @@ fun UiDevice.waitForTvPipMenuElementWithDescription(desc: String): UiObject2? { ?.findObject(buttonSelector) } fun UiDevice.waitUntilTvPipMenuElementWithDescriptionIsGone(desc: String): Boolean? = wait(Until.gone(By.copy(tvPipMenuSelector).hasDescendant(By.desc(desc))), WAIT_TIME_MS) fun UiObject2.isFullscreen(uiDevice: UiDevice): Boolean = visibleBounds.run { height() == uiDevice.displayHeight && width() == uiDevice.displayWidth } No newline at end of file libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_pip.xml +6 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,12 @@ android:text="Enter PIP" android:onClick="enterPip"/> <CheckBox android:id="@+id/with_custom_actions" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="With custom actions"/> <RadioGroup android:layout_width="match_parent" android:layout_height="wrap_content" Loading Loading
libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt +4 −0 Original line number Diff line number Diff line Loading @@ -31,6 +31,10 @@ val TEST_APP_PIP_ACTIVITY_COMPONENT_NAME: ComponentName = ComponentName.createRe TEST_APP_PACKAGE_NAME, ".PipActivity") const val TEST_APP_PIP_ACTIVITY_LABEL = "PipApp" const val TEST_APP_PIP_ACTIVITY_WINDOW_NAME = "PipActivity" const val TEST_APP_PIP_MENU_ACTION_NO_OP = "No-Op" const val TEST_APP_PIP_MENU_ACTION_ON = "On" const val TEST_APP_PIP_MENU_ACTION_OFF = "Off" const val TEST_APP_PIP_MENU_ACTION_CLEAR = "Clear" // Test App > Ime Activity val TEST_APP_IME_ACTIVITY_COMPONENT_NAME: ComponentName = ComponentName.createRelative( Loading
libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt +6 −0 Original line number Diff line number Diff line Loading @@ -70,6 +70,12 @@ class PipAppHelper( startButton.click() } fun checkWithCustomActionsCheckbox() = uiDevice .findObject(By.res(packageName, "with_custom_actions")) ?.takeIf { it.isCheckable } ?.apply { if (!isChecked) click() } ?: error("'With custom actions' checkbox not found") fun pauseMedia() = mediaController?.transportControls?.pause() ?: error("No active media session found") Loading
libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt +96 −0 Original line number Diff line number Diff line Loading @@ -20,7 +20,12 @@ import android.graphics.Rect import androidx.test.filters.RequiresDevice import androidx.test.uiautomator.UiObject2 import com.android.wm.shell.flicker.SYSTEM_UI_PACKAGE_NAME import com.android.wm.shell.flicker.TEST_APP_PIP_MENU_ACTION_CLEAR import com.android.wm.shell.flicker.TEST_APP_PIP_MENU_ACTION_NO_OP import com.android.wm.shell.flicker.TEST_APP_PIP_MENU_ACTION_OFF import com.android.wm.shell.flicker.TEST_APP_PIP_MENU_ACTION_ON import com.android.wm.shell.flicker.wait import org.junit.Assert.assertNull import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test Loading Loading @@ -128,6 +133,7 @@ class TvPipMenuTests : TvPipTestBase() { testApp.clickStartMediaSessionButton() enterPip_openMenu_assertShown() assertFullscreenAndCloseButtonsAreShown() // PiP menu should contain the Pause button val pauseButton = uiDevice.findTvPipMenuElementWithDescription(pauseButtonDescription) Loading @@ -137,6 +143,7 @@ class TvPipMenuTests : TvPipTestBase() { // When we pause media, the button should change from Pause to Play pauseButton.click() assertFullscreenAndCloseButtonsAreShown() // PiP menu should contain the Play button now uiDevice.waitForTvPipMenuElementWithDescription(playButtonDescription) ?: fail("\"Play\" button should be shown in Pip menu if there is an active " + Loading @@ -145,10 +152,99 @@ class TvPipMenuTests : TvPipTestBase() { testApp.closePipWindow() } @Test fun pipMenu_withCustomActions() { // Enter PiP with custom actions. testApp.checkWithCustomActionsCheckbox() enterPip_openMenu_assertShown() // PiP menu should contain "No-Op", "Off" and "Clear" buttons... uiDevice.findTvPipMenuElementWithDescription(TEST_APP_PIP_MENU_ACTION_NO_OP) ?: fail("\"No-Op\" button should be shown in Pip menu") val offButton = uiDevice.findTvPipMenuElementWithDescription(TEST_APP_PIP_MENU_ACTION_OFF) ?: fail("\"Off\" button should be shown in Pip menu") uiDevice.findTvPipMenuElementWithDescription(TEST_APP_PIP_MENU_ACTION_CLEAR) ?: fail("\"Clear\" button should be shown in Pip menu") // ... and should also contain the "Full screen" and "Close" buttons. assertFullscreenAndCloseButtonsAreShown() offButton.click() // Invoking the "Off" action should replace it with the "On" action/button and should // remove the "No-Op" action/button. "Clear" action/button should remain in the menu ... uiDevice.waitForTvPipMenuElementWithDescription(TEST_APP_PIP_MENU_ACTION_ON) ?: fail("\"On\" button should be shown in Pip for a corresponding custom action") assertNull("\"No-Op\" button should not be shown in Pip menu", uiDevice.findTvPipMenuElementWithDescription(TEST_APP_PIP_MENU_ACTION_NO_OP)) val clearButton = uiDevice.findTvPipMenuElementWithDescription(TEST_APP_PIP_MENU_ACTION_CLEAR) ?: fail("\"Clear\" button should be shown in Pip menu") // ... as well as the "Full screen" and "Close" buttons. assertFullscreenAndCloseButtonsAreShown() clearButton.click() // Invoking the "Clear" action should remove all the custom actions and their corresponding // buttons, ... uiDevice.waitUntilTvPipMenuElementWithDescriptionIsGone(TEST_APP_PIP_MENU_ACTION_ON)?.also { isGone -> if (!isGone) fail("\"On\" button should not be shown in Pip menu") } assertNull("\"Off\" button should not be shown in Pip menu", uiDevice.findTvPipMenuElementWithDescription(TEST_APP_PIP_MENU_ACTION_OFF)) assertNull("\"Clear\" button should not be shown in Pip menu", uiDevice.findTvPipMenuElementWithDescription(TEST_APP_PIP_MENU_ACTION_CLEAR)) assertNull("\"No-Op\" button should not be shown in Pip menu", uiDevice.findTvPipMenuElementWithDescription(TEST_APP_PIP_MENU_ACTION_NO_OP)) // ... but the menu should still contain the "Full screen" and "Close" buttons. assertFullscreenAndCloseButtonsAreShown() testApp.closePipWindow() } @Test fun pipMenu_customActions_override_mediaControls() { // Start media session before entering PiP with custom actions. testApp.clickStartMediaSessionButton() testApp.checkWithCustomActionsCheckbox() enterPip_openMenu_assertShown() // PiP menu should contain "No-Op", "Off" and "Clear" buttons for the custom actions... uiDevice.findTvPipMenuElementWithDescription(TEST_APP_PIP_MENU_ACTION_NO_OP) ?: fail("\"No-Op\" button should be shown in Pip menu") uiDevice.findTvPipMenuElementWithDescription(TEST_APP_PIP_MENU_ACTION_OFF) ?: fail("\"Off\" button should be shown in Pip menu") val clearButton = uiDevice.findTvPipMenuElementWithDescription(TEST_APP_PIP_MENU_ACTION_CLEAR) ?: fail("\"Clear\" button should be shown in Pip menu") // ... should also contain the "Full screen" and "Close" buttons, ... assertFullscreenAndCloseButtonsAreShown() // ... but should not contain media buttons. assertNull("\"Play\" button should not be shown in menu when there are custom actions", uiDevice.findTvPipMenuElementWithDescription(playButtonDescription)) assertNull("\"Pause\" button should not be shown in menu when there are custom actions", uiDevice.findTvPipMenuElementWithDescription(pauseButtonDescription)) clearButton.click() // Invoking the "Clear" action should remove all the custom actions, which should bring up // media buttons... uiDevice.waitForTvPipMenuElementWithDescription(pauseButtonDescription) ?: fail("\"Pause\" button should be shown in Pip menu if there is an active " + "playing media session.") // ... while the "Full screen" and "Close" buttons should remain in the menu. assertFullscreenAndCloseButtonsAreShown() testApp.closePipWindow() } private fun enterPip_openMenu_assertShown(): UiObject2 { testApp.clickEnterPipButton() // Pressing the Window key should bring up Pip menu uiDevice.pressWindowKey() return uiDevice.waitForTvPipMenu() ?: fail("Pip menu should have been shown") } private fun assertFullscreenAndCloseButtonsAreShown() { uiDevice.findTvPipMenuCloseButton() ?: fail("\"Close PIP\" button should be shown in Pip menu") uiDevice.findTvPipMenuFullscreenButton() ?: fail("\"Full screen\" button should be shown in Pip menu") } } No newline at end of file
libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt +3 −0 Original line number Diff line number Diff line Loading @@ -58,6 +58,9 @@ fun UiDevice.waitForTvPipMenuElementWithDescription(desc: String): UiObject2? { ?.findObject(buttonSelector) } fun UiDevice.waitUntilTvPipMenuElementWithDescriptionIsGone(desc: String): Boolean? = wait(Until.gone(By.copy(tvPipMenuSelector).hasDescendant(By.desc(desc))), WAIT_TIME_MS) fun UiObject2.isFullscreen(uiDevice: UiDevice): Boolean = visibleBounds.run { height() == uiDevice.displayHeight && width() == uiDevice.displayWidth } No newline at end of file
libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_pip.xml +6 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,12 @@ android:text="Enter PIP" android:onClick="enterPip"/> <CheckBox android:id="@+id/with_custom_actions" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="With custom actions"/> <RadioGroup android:layout_width="match_parent" android:layout_height="wrap_content" Loading