Skip to content

Commit cf7f93c

Browse files
Beth ThibodeauAndroid Build Coastguard Worker
authored andcommitted
[DO NOT MERGE] Add placeholder when media control title is blank
When an app posts a media control with no available title, show a placeholder string with the app name instead Bug: 274775190 Test: atest MediaDataManagerTest (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:965e514614f374dcffe2a638b7ecee3d51531340) Merged-In: Ie406c180af48653595e8e222a15b4dda27de2e0e Change-Id: Ie406c180af48653595e8e222a15b4dda27de2e0e
1 parent ce2f8f4 commit cf7f93c

3 files changed

Lines changed: 121 additions & 11 deletions

File tree

packages/SystemUI/res/values/strings.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2382,6 +2382,8 @@
23822382
<string name="controls_media_smartspace_rec_item_no_artist_description">Play <xliff:g id="song_name" example="Daily mix">%1$s</xliff:g> from <xliff:g id="app_label" example="Spotify">%2$s</xliff:g></string>
23832383
<!-- Header title for Smartspace recommendation card within media controls. [CHAR_LIMIT=30] -->
23842384
<string name="controls_media_smartspace_rec_header">For You</string>
2385+
<!-- Placeholder title to inform user that an app has posted media controls [CHAR_LIMIT=NONE] -->
2386+
<string name="controls_media_empty_title"><xliff:g id="app_name" example="Foo Music App">%1$s</xliff:g> is running</string>
23852387

23862388
<!--- ****** Media tap-to-transfer ****** -->
23872389
<!-- Text for a button to undo the media transfer. [CHAR LIMIT=20] -->

packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -786,12 +786,16 @@ class MediaDataManager(
786786

787787
// Song name
788788
var song: CharSequence? = metadata?.getString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE)
789-
if (song == null) {
789+
if (song.isNullOrBlank()) {
790790
song = metadata?.getString(MediaMetadata.METADATA_KEY_TITLE)
791791
}
792-
if (song == null) {
792+
if (song.isNullOrBlank()) {
793793
song = HybridGroupManager.resolveTitle(notif)
794794
}
795+
if (song.isNullOrBlank()) {
796+
// For apps that don't include a title, add a placeholder
797+
song = context.getString(R.string.controls_media_empty_title, appName)
798+
}
795799

796800
// Explicit Indicator
797801
var isExplicit = false

packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt

Lines changed: 113 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import android.app.smartspace.SmartspaceConfig
2525
import android.app.smartspace.SmartspaceManager
2626
import android.app.smartspace.SmartspaceTarget
2727
import android.content.Intent
28+
import android.content.pm.PackageManager
2829
import android.graphics.Bitmap
2930
import android.graphics.drawable.Icon
3031
import android.media.MediaDescription
@@ -76,6 +77,7 @@ import org.mockito.ArgumentMatchers.anyInt
7677
import org.mockito.Captor
7778
import org.mockito.Mock
7879
import org.mockito.Mockito
80+
import org.mockito.Mockito.mock
7981
import org.mockito.Mockito.never
8082
import org.mockito.Mockito.reset
8183
import org.mockito.Mockito.verify
@@ -516,6 +518,107 @@ class MediaDataManagerTest : SysuiTestCase() {
516518
verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
517519
}
518520

521+
@Test
522+
fun testOnNotificationAdded_emptyTitle_hasPlaceholder() {
523+
// When the manager has a notification with an empty title
524+
val mockPackageManager = mock(PackageManager::class.java)
525+
context.setMockPackageManager(mockPackageManager)
526+
whenever(mockPackageManager.getApplicationLabel(any())).thenReturn(APP_NAME)
527+
whenever(controller.metadata)
528+
.thenReturn(
529+
metadataBuilder
530+
.putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_EMPTY_TITLE)
531+
.build()
532+
)
533+
mediaDataManager.onNotificationAdded(KEY, mediaNotification)
534+
535+
// Then a media control is created with a placeholder title string
536+
assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
537+
assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
538+
verify(listener)
539+
.onMediaDataLoaded(
540+
eq(KEY),
541+
eq(null),
542+
capture(mediaDataCaptor),
543+
eq(true),
544+
eq(0),
545+
eq(false)
546+
)
547+
val placeholderTitle = context.getString(R.string.controls_media_empty_title, APP_NAME)
548+
assertThat(mediaDataCaptor.value.song).isEqualTo(placeholderTitle)
549+
}
550+
551+
@Test
552+
fun testOnNotificationAdded_blankTitle_hasPlaceholder() {
553+
// GIVEN that the manager has a notification with a blank title
554+
val mockPackageManager = mock(PackageManager::class.java)
555+
context.setMockPackageManager(mockPackageManager)
556+
whenever(mockPackageManager.getApplicationLabel(any())).thenReturn(APP_NAME)
557+
whenever(controller.metadata)
558+
.thenReturn(
559+
metadataBuilder
560+
.putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_BLANK_TITLE)
561+
.build()
562+
)
563+
mediaDataManager.onNotificationAdded(KEY, mediaNotification)
564+
565+
// Then a media control is created with a placeholder title string
566+
assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
567+
assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
568+
verify(listener)
569+
.onMediaDataLoaded(
570+
eq(KEY),
571+
eq(null),
572+
capture(mediaDataCaptor),
573+
eq(true),
574+
eq(0),
575+
eq(false)
576+
)
577+
val placeholderTitle = context.getString(R.string.controls_media_empty_title, APP_NAME)
578+
assertThat(mediaDataCaptor.value.song).isEqualTo(placeholderTitle)
579+
}
580+
581+
@Test
582+
fun testOnNotificationAdded_emptyMetadata_usesNotificationTitle() {
583+
// When the app sets the metadata title fields to empty strings, but does include a
584+
// non-blank notification title
585+
val mockPackageManager = mock(PackageManager::class.java)
586+
context.setMockPackageManager(mockPackageManager)
587+
whenever(mockPackageManager.getApplicationLabel(any())).thenReturn(APP_NAME)
588+
whenever(controller.metadata)
589+
.thenReturn(
590+
metadataBuilder
591+
.putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_EMPTY_TITLE)
592+
.putString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE, SESSION_EMPTY_TITLE)
593+
.build()
594+
)
595+
mediaNotification =
596+
SbnBuilder().run {
597+
setPkg(PACKAGE_NAME)
598+
modifyNotification(context).also {
599+
it.setSmallIcon(android.R.drawable.ic_media_pause)
600+
it.setContentTitle(SESSION_TITLE)
601+
it.setStyle(MediaStyle().apply { setMediaSession(session.sessionToken) })
602+
}
603+
build()
604+
}
605+
mediaDataManager.onNotificationAdded(KEY, mediaNotification)
606+
607+
// Then the media control is added using the notification's title
608+
assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
609+
assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
610+
verify(listener)
611+
.onMediaDataLoaded(
612+
eq(KEY),
613+
eq(null),
614+
capture(mediaDataCaptor),
615+
eq(true),
616+
eq(0),
617+
eq(false)
618+
)
619+
assertThat(mediaDataCaptor.value.song).isEqualTo(SESSION_TITLE)
620+
}
621+
519622
@Test
520623
fun testOnNotificationRemoved_emptyTitle_notConverted() {
521624
// GIVEN that the manager has a notification with a resume action and empty title.
@@ -529,8 +632,11 @@ class MediaDataManagerTest : SysuiTestCase() {
529632
val data = mediaDataCaptor.value
530633
val instanceId = data.instanceId
531634
assertThat(data.resumption).isFalse()
532-
mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {}))
533-
635+
mediaDataManager.onMediaDataLoaded(
636+
KEY,
637+
null,
638+
data.copy(song = SESSION_EMPTY_TITLE, resumeAction = Runnable {})
639+
)
534640
// WHEN the notification is removed
535641
reset(listener)
536642
mediaDataManager.onNotificationRemoved(KEY)
@@ -554,17 +660,15 @@ class MediaDataManagerTest : SysuiTestCase() {
554660
@Test
555661
fun testOnNotificationRemoved_blankTitle_notConverted() {
556662
// GIVEN that the manager has a notification with a resume action and blank title.
557-
whenever(controller.metadata)
558-
.thenReturn(
559-
metadataBuilder
560-
.putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_BLANK_TITLE)
561-
.build()
562-
)
563663
addNotificationAndLoad()
564664
val data = mediaDataCaptor.value
565665
val instanceId = data.instanceId
566666
assertThat(data.resumption).isFalse()
567-
mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {}))
667+
mediaDataManager.onMediaDataLoaded(
668+
KEY,
669+
null,
670+
data.copy(song = SESSION_BLANK_TITLE, resumeAction = Runnable {})
671+
)
568672

569673
// WHEN the notification is removed
570674
reset(listener)

0 commit comments

Comments
 (0)