Merge "Add placeholder when media control title is blank" into rvc-dev

This commit is contained in:
Beth Thibodeau
2023-06-16 00:15:31 +00:00
committed by Android (Google) Code Review
3 changed files with 105 additions and 3 deletions

View File

@@ -2797,6 +2797,8 @@
<string name="controls_media_resume">Resume</string> <string name="controls_media_resume">Resume</string>
<!-- Label for button to go to media control settings screen [CHAR_LIMIT=30] --> <!-- Label for button to go to media control settings screen [CHAR_LIMIT=30] -->
<string name="controls_media_settings_button">Settings</string> <string name="controls_media_settings_button">Settings</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>
<!-- Error message indicating that a control timed out while waiting for an update [CHAR_LIMIT=30] --> <!-- Error message indicating that a control timed out while waiting for an update [CHAR_LIMIT=30] -->
<string name="controls_error_timeout">Inactive, check app</string> <string name="controls_error_timeout">Inactive, check app</string>

View File

@@ -364,12 +364,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, app)
}
// Artist name // Artist name
var artist: CharSequence? = metadata.getString(MediaMetadata.METADATA_KEY_ARTIST) var artist: CharSequence? = metadata.getString(MediaMetadata.METADATA_KEY_ARTIST)

View File

@@ -10,11 +10,13 @@ import android.service.notification.StatusBarNotification
import android.testing.AndroidTestingRunner import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dump.DumpManager import com.android.systemui.dump.DumpManager
import com.android.systemui.statusbar.SbnBuilder import com.android.systemui.statusbar.SbnBuilder
import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
@@ -23,6 +25,8 @@ import org.junit.Before
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
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.mock
@@ -33,9 +37,11 @@ import org.mockito.Mockito.`when` as whenever
private const val KEY = "KEY" private const val KEY = "KEY"
private const val KEY_2 = "KEY_2" private const val KEY_2 = "KEY_2"
private const val PACKAGE_NAME = "com.android.systemui" private const val PACKAGE_NAME = "com.android.systemui"
private const val APP_NAME = "SystemUI" private const val APP_NAME = "com.android.systemui.tests"
private const val SESSION_ARTIST = "artist" private const val SESSION_ARTIST = "artist"
private const val SESSION_TITLE = "title" 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 const val USER_ID = 0
private fun <T> anyObject(): T { private fun <T> anyObject(): T {
@@ -61,6 +67,7 @@ class MediaDataManagerTest : SysuiTestCase() {
@JvmField @Rule val mockito = MockitoJUnit.rule() @JvmField @Rule val mockito = MockitoJUnit.rule()
lateinit var mediaDataManager: MediaDataManager lateinit var mediaDataManager: MediaDataManager
lateinit var mediaNotification: StatusBarNotification lateinit var mediaNotification: StatusBarNotification
@Captor lateinit var mediaDataCaptor: ArgumentCaptor<MediaData>
@Before @Before
fun setup() { fun setup() {
@@ -143,6 +150,95 @@ class MediaDataManagerTest : SysuiTestCase() {
verify(listener).onMediaDataRemoved(eq(KEY)) verify(listener).onMediaDataRemoved(eq(KEY))
} }
@Test
fun testOnNotificationAdded_emptyTitle_hasPlaceholder() {
// When the manager has a notification with an empty title
val listener = mock(MediaDataManager.Listener::class.java)
mediaDataManager.addListener(listener)
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)
)
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 listener = mock(MediaDataManager.Listener::class.java)
mediaDataManager.addListener(listener)
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)
)
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 listener = mock(MediaDataManager.Listener::class.java)
mediaDataManager.addListener(listener)
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)
)
assertThat(mediaDataCaptor.value.song).isEqualTo(SESSION_TITLE)
}
@Test @Test
fun testOnNotificationRemoved_withResumption() { fun testOnNotificationRemoved_withResumption() {
// GIVEN that the manager has a notification with a resume action // GIVEN that the manager has a notification with a resume action