Loading libs/WindowManager/Shell/tests/flicker/AndroidManifest.xml +2 −0 Original line number Diff line number Diff line Loading @@ -34,6 +34,8 @@ <uses-permission android:name="android.permission.READ_LOGS"/> <!-- Force-stop test apps --> <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES"/> <!-- Control test app's media session --> <uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL"/> <application> <uses-library android:name="android.test.runner"/> Loading libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/NotificationListener.kt +12 −2 Original line number Diff line number Diff line Loading @@ -74,11 +74,21 @@ class NotificationListener : NotificationListenerService() { return wait { instance == null } } fun findNotification( predicate: (StatusBarNotification) -> Boolean ): StatusBarNotification? { instance?.run { return notifications.values.firstOrNull(predicate) } ?: throw IllegalStateException("NotificationListenerService is not connected") } fun waitForNotificationToAppear( predicate: (StatusBarNotification) -> Boolean ): StatusBarNotification? { return instance?.let { waitForResult(extractor = { it.notifications.values.first(predicate) }).second instance?.let { return waitForResult(extractor = { it.notifications.values.firstOrNull(predicate) }).second } ?: throw IllegalStateException("NotificationListenerService is not connected") } Loading libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt +1 −1 Original line number Diff line number Diff line Loading @@ -42,7 +42,7 @@ abstract class BaseAppHelper( ) { protected val uiDevice: UiDevice = UiDevice.getInstance(instrumentation) private val context: Context protected val context: Context get() = mInstrumentation.context private val activityManager: ActivityManager? Loading libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt +24 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,8 @@ package com.android.wm.shell.flicker.helpers import android.app.Instrumentation import android.media.session.MediaController import android.media.session.MediaSessionManager import android.os.SystemClock import android.view.KeyEvent.KEYCODE_WINDOW import androidx.test.uiautomator.By Loading @@ -35,6 +37,15 @@ class PipAppHelper( TEST_APP_PIP_ACTIVITY_LABEL, TEST_APP_PIP_ACTIVITY_COMPONENT_NAME ) { private val mediaSessionManager: MediaSessionManager get() = context.getSystemService(MediaSessionManager::class.java) ?: error("Could not get MediaSessionManager") private val mediaController: MediaController? get() = mediaSessionManager.getActiveSessions(null).firstOrNull { it.packageName == packageName } fun clickEnterPipButton() { val enterPipButton = uiDevice.findObject(By.res(packageName, "enter_pip")) assertNotNull("Pip button not found, this usually happens when the device " + Loading @@ -50,6 +61,19 @@ class PipAppHelper( } } fun clickStartMediaSessionButton() { val startButton = uiDevice.findObject(By.res(packageName, "media_session_start")) assertNotNull("Start button not found, this usually happens when the device " + "was left in an unknown state (e.g. in split screen)", startButton) startButton.click() } fun pauseMedia() = mediaController?.transportControls?.pause() ?: error("No active media session found") fun stopMedia() = mediaController?.transportControls?.stop() ?: error("No active media session found") fun closePipWindow() { // TODO(b/172321238): remove this check once and simply call closePipWindow once the TV // logic is integrated there. Loading libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipNotificationTests.kt +52 −4 Original line number Diff line number Diff line Loading @@ -22,12 +22,14 @@ import android.os.Bundle import android.service.notification.StatusBarNotification import android.view.Surface import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.NotificationListener.Companion.findNotification import com.android.wm.shell.flicker.NotificationListener.Companion.startNotificationListener import com.android.wm.shell.flicker.NotificationListener.Companion.stopNotificationListener import com.android.wm.shell.flicker.NotificationListener.Companion.waitForNotificationToAppear import com.android.wm.shell.flicker.NotificationListener.Companion.waitForNotificationToDisappear import org.junit.After import org.junit.Assert.assertNotNull import org.junit.Assert.assertNull import org.junit.Assert.assertTrue import org.junit.Before import org.junit.FixMethodOrder Loading Loading @@ -83,10 +85,10 @@ class TvPipNotificationTests(rotationName: String, rotation: Int) val notification: StatusBarNotification = waitForNotificationToAppear { it.isPipNotificationWithTitle(testApp.label) } ?: throw AssertionError("Pip notification should have been posted") } ?: fail("Pip notification should have been posted") notification.deleteIntent?.send() ?: throw AssertionError("Pip notification should contain `delete_intent`") ?: fail("Pip notification should contain `delete_intent`") assertTrue("Pip should have closed by sending the `delete_intent`", testApp.waitUntilClosed()) Loading @@ -101,10 +103,10 @@ class TvPipNotificationTests(rotationName: String, rotation: Int) val notification: StatusBarNotification = waitForNotificationToAppear { it.isPipNotificationWithTitle(testApp.label) } ?: throw AssertionError("Pip notification should have been posted") } ?: fail("Pip notification should have been posted") notification.contentIntent?.send() ?: throw AssertionError("Pip notification should contain `content_intent`") ?: fail("Pip notification should contain `content_intent`") assertTrue("Pip menu should have been shown after sending `content_intent`", uiDevice.waitForTvPipMenu()) Loading @@ -113,7 +115,53 @@ class TvPipNotificationTests(rotationName: String, rotation: Int) testApp.closePipWindow() } @Test fun pipNotification_mediaSessionTitle_isDisplayed() { testApp.launchViaIntent() // Start media session and to PiP testApp.clickStartMediaSessionButton() testApp.clickEnterPipButton() // Wait for the correct notification to show up... waitForNotificationToAppear { it.isPipNotificationWithTitle(TITLE_MEDIA_SESSION_PLAYING) } ?: fail("Pip notification with media session title should have been posted") // ... and make sure "regular" PiP notification is now shown assertNull("Regular notification should not have been posted", findNotification { it.isPipNotificationWithTitle(testApp.label) }) // Pause the media session. When paused the application updates the title for the media // session. This change should be reflected in the notification. testApp.pauseMedia() // Wait for the "paused" notification to show up... waitForNotificationToAppear { it.isPipNotificationWithTitle(TITLE_MEDIA_SESSION_PAUSED) } ?: fail("Pip notification with media session title should have been posted") // ... and make sure "playing" PiP notification is gone assertNull("Regular notification should not have been posted", findNotification { it.isPipNotificationWithTitle(TITLE_MEDIA_SESSION_PLAYING) }) // Now stop the media session, which should revert the title to the "default" one. testApp.stopMedia() // Wait for the "regular" notification to show up... waitForNotificationToAppear { it.isPipNotificationWithTitle(testApp.label) } ?: fail("Pip notification with media session title should have been posted") // ... and make sure previous ("paused") notification is gone assertNull("Regular notification should not have been posted", findNotification { it.isPipNotificationWithTitle(TITLE_MEDIA_SESSION_PAUSED) }) testApp.closePipWindow() } private fun fail(message: String): Nothing = throw AssertionError(message) companion object { private const val TITLE_MEDIA_SESSION_PLAYING = "TestApp media is playing" private const val TITLE_MEDIA_SESSION_PAUSED = "TestApp media is paused" @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<Array<Any>> { Loading Loading
libs/WindowManager/Shell/tests/flicker/AndroidManifest.xml +2 −0 Original line number Diff line number Diff line Loading @@ -34,6 +34,8 @@ <uses-permission android:name="android.permission.READ_LOGS"/> <!-- Force-stop test apps --> <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES"/> <!-- Control test app's media session --> <uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL"/> <application> <uses-library android:name="android.test.runner"/> Loading
libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/NotificationListener.kt +12 −2 Original line number Diff line number Diff line Loading @@ -74,11 +74,21 @@ class NotificationListener : NotificationListenerService() { return wait { instance == null } } fun findNotification( predicate: (StatusBarNotification) -> Boolean ): StatusBarNotification? { instance?.run { return notifications.values.firstOrNull(predicate) } ?: throw IllegalStateException("NotificationListenerService is not connected") } fun waitForNotificationToAppear( predicate: (StatusBarNotification) -> Boolean ): StatusBarNotification? { return instance?.let { waitForResult(extractor = { it.notifications.values.first(predicate) }).second instance?.let { return waitForResult(extractor = { it.notifications.values.firstOrNull(predicate) }).second } ?: throw IllegalStateException("NotificationListenerService is not connected") } Loading
libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt +1 −1 Original line number Diff line number Diff line Loading @@ -42,7 +42,7 @@ abstract class BaseAppHelper( ) { protected val uiDevice: UiDevice = UiDevice.getInstance(instrumentation) private val context: Context protected val context: Context get() = mInstrumentation.context private val activityManager: ActivityManager? Loading
libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt +24 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,8 @@ package com.android.wm.shell.flicker.helpers import android.app.Instrumentation import android.media.session.MediaController import android.media.session.MediaSessionManager import android.os.SystemClock import android.view.KeyEvent.KEYCODE_WINDOW import androidx.test.uiautomator.By Loading @@ -35,6 +37,15 @@ class PipAppHelper( TEST_APP_PIP_ACTIVITY_LABEL, TEST_APP_PIP_ACTIVITY_COMPONENT_NAME ) { private val mediaSessionManager: MediaSessionManager get() = context.getSystemService(MediaSessionManager::class.java) ?: error("Could not get MediaSessionManager") private val mediaController: MediaController? get() = mediaSessionManager.getActiveSessions(null).firstOrNull { it.packageName == packageName } fun clickEnterPipButton() { val enterPipButton = uiDevice.findObject(By.res(packageName, "enter_pip")) assertNotNull("Pip button not found, this usually happens when the device " + Loading @@ -50,6 +61,19 @@ class PipAppHelper( } } fun clickStartMediaSessionButton() { val startButton = uiDevice.findObject(By.res(packageName, "media_session_start")) assertNotNull("Start button not found, this usually happens when the device " + "was left in an unknown state (e.g. in split screen)", startButton) startButton.click() } fun pauseMedia() = mediaController?.transportControls?.pause() ?: error("No active media session found") fun stopMedia() = mediaController?.transportControls?.stop() ?: error("No active media session found") fun closePipWindow() { // TODO(b/172321238): remove this check once and simply call closePipWindow once the TV // logic is integrated there. Loading
libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipNotificationTests.kt +52 −4 Original line number Diff line number Diff line Loading @@ -22,12 +22,14 @@ import android.os.Bundle import android.service.notification.StatusBarNotification import android.view.Surface import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.NotificationListener.Companion.findNotification import com.android.wm.shell.flicker.NotificationListener.Companion.startNotificationListener import com.android.wm.shell.flicker.NotificationListener.Companion.stopNotificationListener import com.android.wm.shell.flicker.NotificationListener.Companion.waitForNotificationToAppear import com.android.wm.shell.flicker.NotificationListener.Companion.waitForNotificationToDisappear import org.junit.After import org.junit.Assert.assertNotNull import org.junit.Assert.assertNull import org.junit.Assert.assertTrue import org.junit.Before import org.junit.FixMethodOrder Loading Loading @@ -83,10 +85,10 @@ class TvPipNotificationTests(rotationName: String, rotation: Int) val notification: StatusBarNotification = waitForNotificationToAppear { it.isPipNotificationWithTitle(testApp.label) } ?: throw AssertionError("Pip notification should have been posted") } ?: fail("Pip notification should have been posted") notification.deleteIntent?.send() ?: throw AssertionError("Pip notification should contain `delete_intent`") ?: fail("Pip notification should contain `delete_intent`") assertTrue("Pip should have closed by sending the `delete_intent`", testApp.waitUntilClosed()) Loading @@ -101,10 +103,10 @@ class TvPipNotificationTests(rotationName: String, rotation: Int) val notification: StatusBarNotification = waitForNotificationToAppear { it.isPipNotificationWithTitle(testApp.label) } ?: throw AssertionError("Pip notification should have been posted") } ?: fail("Pip notification should have been posted") notification.contentIntent?.send() ?: throw AssertionError("Pip notification should contain `content_intent`") ?: fail("Pip notification should contain `content_intent`") assertTrue("Pip menu should have been shown after sending `content_intent`", uiDevice.waitForTvPipMenu()) Loading @@ -113,7 +115,53 @@ class TvPipNotificationTests(rotationName: String, rotation: Int) testApp.closePipWindow() } @Test fun pipNotification_mediaSessionTitle_isDisplayed() { testApp.launchViaIntent() // Start media session and to PiP testApp.clickStartMediaSessionButton() testApp.clickEnterPipButton() // Wait for the correct notification to show up... waitForNotificationToAppear { it.isPipNotificationWithTitle(TITLE_MEDIA_SESSION_PLAYING) } ?: fail("Pip notification with media session title should have been posted") // ... and make sure "regular" PiP notification is now shown assertNull("Regular notification should not have been posted", findNotification { it.isPipNotificationWithTitle(testApp.label) }) // Pause the media session. When paused the application updates the title for the media // session. This change should be reflected in the notification. testApp.pauseMedia() // Wait for the "paused" notification to show up... waitForNotificationToAppear { it.isPipNotificationWithTitle(TITLE_MEDIA_SESSION_PAUSED) } ?: fail("Pip notification with media session title should have been posted") // ... and make sure "playing" PiP notification is gone assertNull("Regular notification should not have been posted", findNotification { it.isPipNotificationWithTitle(TITLE_MEDIA_SESSION_PLAYING) }) // Now stop the media session, which should revert the title to the "default" one. testApp.stopMedia() // Wait for the "regular" notification to show up... waitForNotificationToAppear { it.isPipNotificationWithTitle(testApp.label) } ?: fail("Pip notification with media session title should have been posted") // ... and make sure previous ("paused") notification is gone assertNull("Regular notification should not have been posted", findNotification { it.isPipNotificationWithTitle(TITLE_MEDIA_SESSION_PAUSED) }) testApp.closePipWindow() } private fun fail(message: String): Nothing = throw AssertionError(message) companion object { private const val TITLE_MEDIA_SESSION_PLAYING = "TestApp media is playing" private const val TITLE_MEDIA_SESSION_PAUSED = "TestApp media is paused" @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<Array<Any>> { Loading