summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Sergey Nikolaienkov <sergeynv@google.com> 2020-11-05 13:54:46 +0000
committer Sergey Nikolaienkov <sergeynv@google.com> 2020-11-06 08:17:01 +0000
commitfbc3469d7bdf75e42517f5fcaf7e0140f3998f4e (patch)
treeee1e65486035b982fac9b10ebb3ccbc9d1271b86
parent2d2d6357e8693f05cd1e80152c5ceff6a5ea6479 (diff)
Test PipNotification when there is a MediaSession
Bug: 171520419 Test: atest WMShellFlickerTests:TvPipNotificationTests Change-Id: Ie70f394e572e9adfeee7ecff9ba4a0e686197ca4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/AndroidManifest.xml2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/NotificationListener.kt14
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt24
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipNotificationTests.kt56
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_pip.xml33
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/PipActivity.java97
7 files changed, 211 insertions, 17 deletions
diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidManifest.xml b/libs/WindowManager/Shell/tests/flicker/AndroidManifest.xml
index 58fc90e4d827..9e8330973b40 100644
--- a/libs/WindowManager/Shell/tests/flicker/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/AndroidManifest.xml
@@ -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"/>
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/NotificationListener.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/NotificationListener.kt
index 3957f5a1c6f5..51f7a18f60dd 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/NotificationListener.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/NotificationListener.kt
@@ -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")
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt
index a588888c5902..55796da100c4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt
@@ -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?
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
index edde581824d1..42686e7e87d7 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
@@ -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
@@ -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 " +
@@ -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.
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipNotificationTests.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipNotificationTests.kt
index 8bb78c232732..1d44658d71ad 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipNotificationTests.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipNotificationTests.kt
@@ -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
@@ -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())
@@ -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())
@@ -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>> {
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_pip.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_pip.xml
index e1870d9c523d..0e79d03cc3ee 100644
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_pip.xml
+++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_pip.xml
@@ -18,9 +18,36 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:orientation="vertical"
android:background="@android:color/holo_blue_bright">
- <Button android:layout_width="wrap_content"
+
+ <Button
+ android:id="@+id/enter_pip"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Enter PIP"/>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Media Session"/>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <Button
+ android:id="@+id/media_session_start"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:id="@+id/enter_pip"
- android:text="Enter PIP"/>
+ android:text="Start"/>
+
+ <Button
+ android:id="@+id/media_session_stop"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Stop"/>
+
+ </LinearLayout>
+
</LinearLayout>
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/PipActivity.java b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/PipActivity.java
index 305281691e11..f70603ede0e5 100644
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/PipActivity.java
+++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/PipActivity.java
@@ -16,30 +16,113 @@
package com.android.wm.shell.flicker.testapp;
+import static android.media.MediaMetadata.METADATA_KEY_TITLE;
+import static android.media.session.PlaybackState.ACTION_PLAY_PAUSE;
+import static android.media.session.PlaybackState.ACTION_STOP;
+import static android.media.session.PlaybackState.STATE_PAUSED;
+import static android.media.session.PlaybackState.STATE_PLAYING;
+import static android.media.session.PlaybackState.STATE_STOPPED;
+
import android.app.Activity;
import android.app.PictureInPictureParams;
import android.graphics.Rect;
+import android.media.MediaMetadata;
+import android.media.session.MediaSession;
+import android.media.session.PlaybackState;
import android.os.Bundle;
import android.util.Rational;
+import android.view.Window;
import android.view.WindowManager;
-import android.widget.Button;
public class PipActivity extends Activity {
+ /**
+ * A media session title for when the session is in {@link STATE_PLAYING}.
+ * TvPipNotificationTests check whether the actual notification title matches this string.
+ */
+ private static final String TITLE_STATE_PLAYING = "TestApp media is playing";
+ /**
+ * A media session title for when the session is in {@link STATE_PAUSED}.
+ * TvPipNotificationTests check whether the actual notification title matches this string.
+ */
+ private static final String TITLE_STATE_PAUSED = "TestApp media is paused";
+
+ private MediaSession mMediaSession;
+ private final PlaybackState.Builder mPlaybackStateBuilder = new PlaybackState.Builder()
+ .setActions(ACTION_PLAY_PAUSE | ACTION_STOP)
+ .setState(STATE_STOPPED, 0, 1f);
+ private PlaybackState mPlaybackState = mPlaybackStateBuilder.build();
+ private final MediaMetadata.Builder mMediaMetadataBuilder = new MediaMetadata.Builder();
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- WindowManager.LayoutParams p = getWindow().getAttributes();
- p.layoutInDisplayCutoutMode = WindowManager.LayoutParams
+
+ final Window window = getWindow();
+ final WindowManager.LayoutParams layoutParams = window.getAttributes();
+ layoutParams.layoutInDisplayCutoutMode = WindowManager.LayoutParams
.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
- getWindow().setAttributes(p);
+ window.setAttributes(layoutParams);
+
setContentView(R.layout.activity_pip);
- Button enterPip = (Button) findViewById(R.id.enter_pip);
- PictureInPictureParams params = new PictureInPictureParams.Builder()
+ final PictureInPictureParams pipParams = new PictureInPictureParams.Builder()
.setAspectRatio(new Rational(1, 1))
.setSourceRectHint(new Rect(0, 0, 100, 100))
.build();
+ findViewById(R.id.enter_pip).setOnClickListener(v -> enterPictureInPictureMode(pipParams));
+
+ findViewById(R.id.media_session_start)
+ .setOnClickListener(v -> updateMediaSessionState(STATE_PLAYING));
+ findViewById(R.id.media_session_stop)
+ .setOnClickListener(v -> updateMediaSessionState(STATE_STOPPED));
+
+ mMediaSession = new MediaSession(this, "WMShell_TestApp");
+ mMediaSession.setPlaybackState(mPlaybackStateBuilder.build());
+ mMediaSession.setCallback(new MediaSession.Callback() {
+ @Override
+ public void onPlay() {
+ updateMediaSessionState(STATE_PLAYING);
+ }
+
+ @Override
+ public void onPause() {
+ updateMediaSessionState(STATE_PAUSED);
+ }
+
+ @Override
+ public void onStop() {
+ updateMediaSessionState(STATE_STOPPED);
+ }
+ });
+ }
+
+ private void updateMediaSessionState(int newState) {
+ if (mPlaybackState.getState() == newState) {
+ return;
+ }
+ final String title;
+ switch (newState) {
+ case STATE_PLAYING:
+ title = TITLE_STATE_PLAYING;
+ break;
+ case STATE_PAUSED:
+ title = TITLE_STATE_PAUSED;
+ break;
+ case STATE_STOPPED:
+ title = "";
+ break;
+
+ default:
+ throw new IllegalArgumentException("Unknown state " + newState);
+ }
+
+ mPlaybackStateBuilder.setState(newState, 0, 1f);
+ mPlaybackState = mPlaybackStateBuilder.build();
+
+ mMediaMetadataBuilder.putText(METADATA_KEY_TITLE, title);
- enterPip.setOnClickListener((v) -> enterPictureInPictureMode(params));
+ mMediaSession.setPlaybackState(mPlaybackState);
+ mMediaSession.setMetadata(mMediaMetadataBuilder.build());
+ mMediaSession.setActive(newState != STATE_STOPPED);
}
}