[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 Change-Id: Ie406c180af48653595e8e222a15b4dda27de2e0e
This commit is contained in:
@@ -2382,6 +2382,8 @@
|
|||||||
<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>
|
<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>
|
||||||
<!-- Header title for Smartspace recommendation card within media controls. [CHAR_LIMIT=30] -->
|
<!-- Header title for Smartspace recommendation card within media controls. [CHAR_LIMIT=30] -->
|
||||||
<string name="controls_media_smartspace_rec_header">For You</string>
|
<string name="controls_media_smartspace_rec_header">For You</string>
|
||||||
|
<!-- Placeholder title to inform user that an app has posted media controls [CHAR_LIMIT=NONE] -->
|
||||||
|
<string name="controls_media_empty_title"><xliff:g id="app_name" example="Foo Music App">%1$s</xliff:g> is running</string>
|
||||||
|
|
||||||
<!--- ****** Media tap-to-transfer ****** -->
|
<!--- ****** Media tap-to-transfer ****** -->
|
||||||
<!-- Text for a button to undo the media transfer. [CHAR LIMIT=20] -->
|
<!-- Text for a button to undo the media transfer. [CHAR LIMIT=20] -->
|
||||||
|
|||||||
@@ -786,12 +786,16 @@ class MediaDataManager(
|
|||||||
|
|
||||||
// Song name
|
// Song name
|
||||||
var song: CharSequence? = metadata?.getString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE)
|
var song: CharSequence? = metadata?.getString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE)
|
||||||
if (song == null) {
|
if (song.isNullOrBlank()) {
|
||||||
song = metadata?.getString(MediaMetadata.METADATA_KEY_TITLE)
|
song = metadata?.getString(MediaMetadata.METADATA_KEY_TITLE)
|
||||||
}
|
}
|
||||||
if (song == null) {
|
if (song.isNullOrBlank()) {
|
||||||
song = HybridGroupManager.resolveTitle(notif)
|
song = HybridGroupManager.resolveTitle(notif)
|
||||||
}
|
}
|
||||||
|
if (song.isNullOrBlank()) {
|
||||||
|
// For apps that don't include a title, add a placeholder
|
||||||
|
song = context.getString(R.string.controls_media_empty_title, appName)
|
||||||
|
}
|
||||||
|
|
||||||
// Explicit Indicator
|
// Explicit Indicator
|
||||||
var isExplicit = false
|
var isExplicit = false
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import android.app.smartspace.SmartspaceConfig
|
|||||||
import android.app.smartspace.SmartspaceManager
|
import android.app.smartspace.SmartspaceManager
|
||||||
import android.app.smartspace.SmartspaceTarget
|
import android.app.smartspace.SmartspaceTarget
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageManager
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.drawable.Icon
|
import android.graphics.drawable.Icon
|
||||||
import android.media.MediaDescription
|
import android.media.MediaDescription
|
||||||
@@ -76,6 +77,7 @@ import org.mockito.ArgumentMatchers.anyInt
|
|||||||
import org.mockito.Captor
|
import org.mockito.Captor
|
||||||
import org.mockito.Mock
|
import org.mockito.Mock
|
||||||
import org.mockito.Mockito
|
import org.mockito.Mockito
|
||||||
|
import org.mockito.Mockito.mock
|
||||||
import org.mockito.Mockito.never
|
import org.mockito.Mockito.never
|
||||||
import org.mockito.Mockito.reset
|
import org.mockito.Mockito.reset
|
||||||
import org.mockito.Mockito.verify
|
import org.mockito.Mockito.verify
|
||||||
@@ -516,6 +518,107 @@ class MediaDataManagerTest : SysuiTestCase() {
|
|||||||
verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
|
verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testOnNotificationAdded_emptyTitle_hasPlaceholder() {
|
||||||
|
// When the manager has a notification with an empty title
|
||||||
|
val mockPackageManager = mock(PackageManager::class.java)
|
||||||
|
context.setMockPackageManager(mockPackageManager)
|
||||||
|
whenever(mockPackageManager.getApplicationLabel(any())).thenReturn(APP_NAME)
|
||||||
|
whenever(controller.metadata)
|
||||||
|
.thenReturn(
|
||||||
|
metadataBuilder
|
||||||
|
.putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_EMPTY_TITLE)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
mediaDataManager.onNotificationAdded(KEY, mediaNotification)
|
||||||
|
|
||||||
|
// Then a media control is created with a placeholder title string
|
||||||
|
assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
|
||||||
|
assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
|
||||||
|
verify(listener)
|
||||||
|
.onMediaDataLoaded(
|
||||||
|
eq(KEY),
|
||||||
|
eq(null),
|
||||||
|
capture(mediaDataCaptor),
|
||||||
|
eq(true),
|
||||||
|
eq(0),
|
||||||
|
eq(false)
|
||||||
|
)
|
||||||
|
val placeholderTitle = context.getString(R.string.controls_media_empty_title, APP_NAME)
|
||||||
|
assertThat(mediaDataCaptor.value.song).isEqualTo(placeholderTitle)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testOnNotificationAdded_blankTitle_hasPlaceholder() {
|
||||||
|
// GIVEN that the manager has a notification with a blank title
|
||||||
|
val mockPackageManager = mock(PackageManager::class.java)
|
||||||
|
context.setMockPackageManager(mockPackageManager)
|
||||||
|
whenever(mockPackageManager.getApplicationLabel(any())).thenReturn(APP_NAME)
|
||||||
|
whenever(controller.metadata)
|
||||||
|
.thenReturn(
|
||||||
|
metadataBuilder
|
||||||
|
.putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_BLANK_TITLE)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
mediaDataManager.onNotificationAdded(KEY, mediaNotification)
|
||||||
|
|
||||||
|
// Then a media control is created with a placeholder title string
|
||||||
|
assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
|
||||||
|
assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
|
||||||
|
verify(listener)
|
||||||
|
.onMediaDataLoaded(
|
||||||
|
eq(KEY),
|
||||||
|
eq(null),
|
||||||
|
capture(mediaDataCaptor),
|
||||||
|
eq(true),
|
||||||
|
eq(0),
|
||||||
|
eq(false)
|
||||||
|
)
|
||||||
|
val placeholderTitle = context.getString(R.string.controls_media_empty_title, APP_NAME)
|
||||||
|
assertThat(mediaDataCaptor.value.song).isEqualTo(placeholderTitle)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testOnNotificationAdded_emptyMetadata_usesNotificationTitle() {
|
||||||
|
// When the app sets the metadata title fields to empty strings, but does include a
|
||||||
|
// non-blank notification title
|
||||||
|
val mockPackageManager = mock(PackageManager::class.java)
|
||||||
|
context.setMockPackageManager(mockPackageManager)
|
||||||
|
whenever(mockPackageManager.getApplicationLabel(any())).thenReturn(APP_NAME)
|
||||||
|
whenever(controller.metadata)
|
||||||
|
.thenReturn(
|
||||||
|
metadataBuilder
|
||||||
|
.putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_EMPTY_TITLE)
|
||||||
|
.putString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE, SESSION_EMPTY_TITLE)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
mediaNotification =
|
||||||
|
SbnBuilder().run {
|
||||||
|
setPkg(PACKAGE_NAME)
|
||||||
|
modifyNotification(context).also {
|
||||||
|
it.setSmallIcon(android.R.drawable.ic_media_pause)
|
||||||
|
it.setContentTitle(SESSION_TITLE)
|
||||||
|
it.setStyle(MediaStyle().apply { setMediaSession(session.sessionToken) })
|
||||||
|
}
|
||||||
|
build()
|
||||||
|
}
|
||||||
|
mediaDataManager.onNotificationAdded(KEY, mediaNotification)
|
||||||
|
|
||||||
|
// Then the media control is added using the notification's title
|
||||||
|
assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
|
||||||
|
assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
|
||||||
|
verify(listener)
|
||||||
|
.onMediaDataLoaded(
|
||||||
|
eq(KEY),
|
||||||
|
eq(null),
|
||||||
|
capture(mediaDataCaptor),
|
||||||
|
eq(true),
|
||||||
|
eq(0),
|
||||||
|
eq(false)
|
||||||
|
)
|
||||||
|
assertThat(mediaDataCaptor.value.song).isEqualTo(SESSION_TITLE)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testOnNotificationRemoved_emptyTitle_notConverted() {
|
fun testOnNotificationRemoved_emptyTitle_notConverted() {
|
||||||
// GIVEN that the manager has a notification with a resume action and empty title.
|
// GIVEN that the manager has a notification with a resume action and empty title.
|
||||||
@@ -529,8 +632,11 @@ class MediaDataManagerTest : SysuiTestCase() {
|
|||||||
val data = mediaDataCaptor.value
|
val data = mediaDataCaptor.value
|
||||||
val instanceId = data.instanceId
|
val instanceId = data.instanceId
|
||||||
assertThat(data.resumption).isFalse()
|
assertThat(data.resumption).isFalse()
|
||||||
mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {}))
|
mediaDataManager.onMediaDataLoaded(
|
||||||
|
KEY,
|
||||||
|
null,
|
||||||
|
data.copy(song = SESSION_EMPTY_TITLE, resumeAction = Runnable {})
|
||||||
|
)
|
||||||
// WHEN the notification is removed
|
// WHEN the notification is removed
|
||||||
reset(listener)
|
reset(listener)
|
||||||
mediaDataManager.onNotificationRemoved(KEY)
|
mediaDataManager.onNotificationRemoved(KEY)
|
||||||
@@ -554,17 +660,15 @@ class MediaDataManagerTest : SysuiTestCase() {
|
|||||||
@Test
|
@Test
|
||||||
fun testOnNotificationRemoved_blankTitle_notConverted() {
|
fun testOnNotificationRemoved_blankTitle_notConverted() {
|
||||||
// GIVEN that the manager has a notification with a resume action and blank title.
|
// 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()
|
addNotificationAndLoad()
|
||||||
val data = mediaDataCaptor.value
|
val data = mediaDataCaptor.value
|
||||||
val instanceId = data.instanceId
|
val instanceId = data.instanceId
|
||||||
assertThat(data.resumption).isFalse()
|
assertThat(data.resumption).isFalse()
|
||||||
mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {}))
|
mediaDataManager.onMediaDataLoaded(
|
||||||
|
KEY,
|
||||||
|
null,
|
||||||
|
data.copy(song = SESSION_BLANK_TITLE, resumeAction = Runnable {})
|
||||||
|
)
|
||||||
|
|
||||||
// WHEN the notification is removed
|
// WHEN the notification is removed
|
||||||
reset(listener)
|
reset(listener)
|
||||||
|
|||||||
Reference in New Issue
Block a user