Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 965e5146 authored by Beth Thibodeau's avatar Beth Thibodeau
Browse files

[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
parent b43c742d
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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>
    <!-- Header title for Smartspace recommendation card within media controls. [CHAR_LIMIT=30] -->
    <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 ****** -->
    <!-- Text for a button to undo the media transfer. [CHAR LIMIT=20] -->
+6 −2
Original line number Diff line number Diff line
@@ -786,12 +786,16 @@ class MediaDataManager(

        // Song name
        var song: CharSequence? = metadata?.getString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE)
        if (song == null) {
        if (song.isNullOrBlank()) {
            song = metadata?.getString(MediaMetadata.METADATA_KEY_TITLE)
        }
        if (song == null) {
        if (song.isNullOrBlank()) {
            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
        var isExplicit = false
+113 −9
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import android.app.smartspace.SmartspaceConfig
import android.app.smartspace.SmartspaceManager
import android.app.smartspace.SmartspaceTarget
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.drawable.Icon
import android.media.MediaDescription
@@ -76,6 +77,7 @@ import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
@@ -516,6 +518,107 @@ class MediaDataManagerTest : SysuiTestCase() {
        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
    fun testOnNotificationRemoved_emptyTitle_notConverted() {
        // 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 instanceId = data.instanceId
        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
        reset(listener)
        mediaDataManager.onNotificationRemoved(KEY)
@@ -554,17 +660,15 @@ class MediaDataManagerTest : SysuiTestCase() {
    @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 {}))
        mediaDataManager.onMediaDataLoaded(
            KEY,
            null,
            data.copy(song = SESSION_BLANK_TITLE, resumeAction = Runnable {})
        )

        // WHEN the notification is removed
        reset(listener)