diff options
2 files changed, 158 insertions, 24 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt index 9d6230b429f9..af60e0e2df76 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt @@ -666,7 +666,7 @@ class MediaDataManager( appIntent: PendingIntent, packageName: String ) { - if (TextUtils.isEmpty(desc.title)) { + if (desc.title.isNullOrBlank()) { Log.e(TAG, "Description incomplete") // Delete the placeholder entry mediaEntries.remove(packageName) @@ -1408,6 +1408,13 @@ class MediaDataManager( /** Set the given [MediaData] as a resume state player and notify listeners */ private fun convertToResumePlayer(key: String, data: MediaData) { if (DEBUG) Log.d(TAG, "Converting $key to resume") + // Resumption controls must have a title. + if (data.song.isNullOrBlank()) { + Log.e(TAG, "Description incomplete") + notifyMediaDataRemoved(key) + logger.logMediaRemoved(data.appUid, data.packageName, data.instanceId) + return + } // Move to resume key (aka package name) if that key doesn't already exist. val resumeAction = data.resumeAction?.let { getResumeMediaAction(it) } val actions = resumeAction?.let { listOf(resumeAction) } ?: emptyList() diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt index a07a714ebc77..8c54da1c3153 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt @@ -93,6 +93,8 @@ private const val SYSTEM_PACKAGE_NAME = "com.android.systemui" private const val APP_NAME = "SystemUI" private const val SESSION_ARTIST = "artist" private const val SESSION_TITLE = "title" +private const val SESSION_BLANK_TITLE = " " +private const val SESSION_EMPTY_TITLE = "" private const val USER_ID = 0 private val DISMISS_INTENT = Intent().apply { action = "dismiss" } @@ -214,6 +216,7 @@ class MediaDataManagerTest : SysuiTestCase() { whenever(mediaControllerFactory.create(eq(session.sessionToken))).thenReturn(controller) whenever(controller.transportControls).thenReturn(transportControls) whenever(controller.playbackInfo).thenReturn(playbackInfo) + whenever(controller.metadata).thenReturn(metadataBuilder.build()) whenever(playbackInfo.playbackType) .thenReturn(MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL) @@ -318,18 +321,15 @@ class MediaDataManagerTest : SysuiTestCase() { @Test fun testLoadMetadata_withExplicitIndicator() { - val metadata = - MediaMetadata.Builder().run { - putString(MediaMetadata.METADATA_KEY_ARTIST, SESSION_ARTIST) - putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_TITLE) - putLong( - MediaConstants.METADATA_KEY_IS_EXPLICIT, - MediaConstants.METADATA_VALUE_ATTRIBUTE_PRESENT - ) - build() - } - whenever(mediaControllerFactory.create(anyObject())).thenReturn(controller) - whenever(controller.metadata).thenReturn(metadata) + whenever(controller.metadata) + .thenReturn( + metadataBuilder + .putLong( + MediaConstants.METADATA_KEY_IS_EXPLICIT, + MediaConstants.METADATA_VALUE_ATTRIBUTE_PRESENT + ) + .build() + ) mediaDataManager.addListener(listener) mediaDataManager.onNotificationAdded(KEY, mediaNotification) @@ -350,9 +350,6 @@ class MediaDataManagerTest : SysuiTestCase() { @Test fun testOnMetaDataLoaded_withoutExplicitIndicator() { - whenever(mediaControllerFactory.create(anyObject())).thenReturn(controller) - whenever(controller.metadata).thenReturn(metadataBuilder.build()) - mediaDataManager.addListener(listener) mediaDataManager.onNotificationAdded(KEY, mediaNotification) @@ -385,7 +382,6 @@ class MediaDataManagerTest : SysuiTestCase() { @Test fun testOnMetaDataLoaded_conservesActiveFlag() { whenever(mediaControllerFactory.create(anyObject())).thenReturn(controller) - whenever(controller.metadata).thenReturn(metadataBuilder.build()) mediaDataManager.addListener(listener) mediaDataManager.onNotificationAdded(KEY, mediaNotification) assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) @@ -530,9 +526,78 @@ class MediaDataManagerTest : SysuiTestCase() { } @Test + fun testOnNotificationRemoved_emptyTitle_notConverted() { + // GIVEN that the manager has a notification with a resume action and empty title. + whenever(controller.metadata) + .thenReturn( + metadataBuilder + .putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_EMPTY_TITLE) + .build() + ) + addNotificationAndLoad() + val data = mediaDataCaptor.value + val instanceId = data.instanceId + assertThat(data.resumption).isFalse() + mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {})) + + // WHEN the notification is removed + reset(listener) + mediaDataManager.onNotificationRemoved(KEY) + + // THEN active media is not converted to resume. + verify(listener, never()) + .onMediaDataLoaded( + eq(PACKAGE_NAME), + eq(KEY), + capture(mediaDataCaptor), + eq(true), + eq(0), + eq(false) + ) + verify(logger, never()) + .logActiveConvertedToResume(anyInt(), eq(PACKAGE_NAME), eq(instanceId)) + verify(logger, never()).logResumeMediaAdded(anyInt(), eq(PACKAGE_NAME), any()) + verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(instanceId)) + } + + @Test + fun testOnNotificationRemoved_blankTitle_notConverted() { + // GIVEN that the manager has a notification with a resume action and blank title. + whenever(controller.metadata) + .thenReturn( + metadataBuilder + .putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_BLANK_TITLE) + .build() + ) + addNotificationAndLoad() + val data = mediaDataCaptor.value + val instanceId = data.instanceId + assertThat(data.resumption).isFalse() + mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {})) + + // WHEN the notification is removed + reset(listener) + mediaDataManager.onNotificationRemoved(KEY) + + // THEN active media is not converted to resume. + verify(listener, never()) + .onMediaDataLoaded( + eq(PACKAGE_NAME), + eq(KEY), + capture(mediaDataCaptor), + eq(true), + eq(0), + eq(false) + ) + verify(logger, never()) + .logActiveConvertedToResume(anyInt(), eq(PACKAGE_NAME), eq(instanceId)) + verify(logger, never()).logResumeMediaAdded(anyInt(), eq(PACKAGE_NAME), any()) + verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(instanceId)) + } + + @Test fun testOnNotificationRemoved_withResumption() { // GIVEN that the manager has a notification with a resume action - whenever(controller.metadata).thenReturn(metadataBuilder.build()) addNotificationAndLoad() val data = mediaDataCaptor.value assertThat(data.resumption).isFalse() @@ -557,7 +622,6 @@ class MediaDataManagerTest : SysuiTestCase() { @Test fun testOnNotificationRemoved_twoWithResumption() { // GIVEN that the manager has two notifications with resume actions - whenever(controller.metadata).thenReturn(metadataBuilder.build()) mediaDataManager.onNotificationAdded(KEY, mediaNotification) mediaDataManager.onNotificationAdded(KEY_2, mediaNotification) assertThat(backgroundExecutor.runAllReady()).isEqualTo(2) @@ -623,7 +687,6 @@ class MediaDataManagerTest : SysuiTestCase() { @Test fun testOnNotificationRemoved_withResumption_butNotLocal() { // GIVEN that the manager has a notification with a resume action, but is not local - whenever(controller.metadata).thenReturn(metadataBuilder.build()) whenever(playbackInfo.playbackType) .thenReturn(MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE) addNotificationAndLoad() @@ -660,7 +723,6 @@ class MediaDataManagerTest : SysuiTestCase() { } // And an active, resumable notification - whenever(controller.metadata).thenReturn(metadataBuilder.build()) addNotificationAndLoad() val data = mediaDataCaptor.value assertThat(data.resumption).isFalse() @@ -845,6 +907,74 @@ class MediaDataManagerTest : SysuiTestCase() { } @Test + fun testAddResumptionControls_hasEmptyTitle() { + whenever(mediaFlags.isResumeProgressEnabled()).thenReturn(true) + + // WHEN resumption controls are added that have empty title + val desc = + MediaDescription.Builder().run { + setTitle(SESSION_EMPTY_TITLE) + build() + } + mediaDataManager.addResumptionControls( + USER_ID, + desc, + Runnable {}, + session.sessionToken, + APP_NAME, + pendingIntent, + PACKAGE_NAME + ) + + // Resumption controls are not added. + assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) + assertThat(foregroundExecutor.runAllReady()).isEqualTo(0) + verify(listener, never()) + .onMediaDataLoaded( + eq(PACKAGE_NAME), + eq(null), + capture(mediaDataCaptor), + eq(true), + eq(0), + eq(false) + ) + } + + @Test + fun testAddResumptionControls_hasBlankTitle() { + whenever(mediaFlags.isResumeProgressEnabled()).thenReturn(true) + + // WHEN resumption controls are added that have a blank title + val desc = + MediaDescription.Builder().run { + setTitle(SESSION_BLANK_TITLE) + build() + } + mediaDataManager.addResumptionControls( + USER_ID, + desc, + Runnable {}, + session.sessionToken, + APP_NAME, + pendingIntent, + PACKAGE_NAME + ) + + // Resumption controls are not added. + assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) + assertThat(foregroundExecutor.runAllReady()).isEqualTo(0) + verify(listener, never()) + .onMediaDataLoaded( + eq(PACKAGE_NAME), + eq(null), + capture(mediaDataCaptor), + eq(true), + eq(0), + eq(false) + ) + } + + @Test fun testResumptionDisabled_dismissesResumeControls() { // WHEN there are resume controls and resumption is switched off val desc = @@ -1213,7 +1343,6 @@ class MediaDataManagerTest : SysuiTestCase() { @Test fun testOnActiveMediaConverted_doesNotUpdateLastActiveTime() { // GIVEN that the manager has a notification with a resume action - whenever(controller.metadata).thenReturn(metadataBuilder.build()) addNotificationAndLoad() val data = mediaDataCaptor.value val instanceId = data.instanceId @@ -1513,7 +1642,6 @@ class MediaDataManagerTest : SysuiTestCase() { val instanceId = mediaDataCaptor.value.instanceId // Location is updated to local cast - whenever(controller.metadata).thenReturn(metadataBuilder.build()) whenever(playbackInfo.playbackType) .thenReturn(MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE) addNotificationAndLoad() @@ -1589,7 +1717,6 @@ class MediaDataManagerTest : SysuiTestCase() { @Test fun testPlaybackStateChange_keyHasNullToken_doesNothing() { // When we get an update that sets the data's token to null - whenever(controller.metadata).thenReturn(metadataBuilder.build()) addNotificationAndLoad() val data = mediaDataCaptor.value assertThat(data.resumption).isFalse() |