diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-08-18 16:25:58 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-08-18 16:25:58 +0000 |
commit | 1d80ff70038f722a38f24bc53a02aae7cbb02d12 (patch) | |
tree | b91325686bc0dcf12d71b90aec042846ae550027 | |
parent | d8d24bc98b4332e21c1c94c1d4ac094bf9b349ee (diff) | |
parent | f6e96d43481f5032b6c0855600a66df77beccad5 (diff) | |
download | base-1d80ff70038f722a38f24bc53a02aae7cbb02d12.tar.gz |
Merge cherrypicks of ['googleplex-android-review.googlesource.com/24432330', 'googleplex-android-review.googlesource.com/24470825'] into rvc-platform-release.android-platform-11.0.0_r35
Change-Id: I1b1058dccb18618795c14c01477170f2e3b3b8c4
4 files changed, 117 insertions, 12 deletions
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 174f5c737d14..c18fa48b45fa 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -2830,6 +2830,8 @@ <string name="controls_media_resume">Resume</string> <!-- Label for button to go to media control settings screen [CHAR_LIMIT=30] --> <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] --> <string name="controls_error_timeout">Inactive, check app</string> diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt index 2d7aafc497d8..a758449f8c88 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt @@ -445,13 +445,17 @@ class MediaDataManager( val smallIconDrawable: Drawable = sbn.notification.smallIcon.loadDrawable(context) // Song name - var song: CharSequence? = metadata?.getString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE) - if (song == null) { - song = metadata?.getString(MediaMetadata.METADATA_KEY_TITLE) + var song: CharSequence? = metadata.getString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE) + 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, app) + } // Artist name var artist: CharSequence? = metadata?.getString(MediaMetadata.METADATA_KEY_ARTIST) diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java index 586399c6dfd5..dba92f65de4d 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java @@ -53,6 +53,7 @@ import android.graphics.Color; import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; import android.net.Uri; import android.os.Bundle; import android.os.Handler; @@ -560,13 +561,19 @@ public class PipMenuActivity extends Activity { final RemoteAction action = mActions.get(i); final ImageButton actionView = (ImageButton) mActionsGroup.getChildAt(i); - // TODO: Check if the action drawable has changed before we reload it - action.getIcon().loadDrawableAsync(this, d -> { - if (d != null) { - d.setTint(Color.WHITE); - actionView.setImageDrawable(d); - } - }, mHandler); + final int iconType = action.getIcon().getType(); + if (iconType == Icon.TYPE_URI || iconType == Icon.TYPE_URI_ADAPTIVE_BITMAP) { + // Disallow loading icon from content URI + actionView.setImageDrawable(null); + } else { + // TODO: Check if the action drawable has changed before we reload it + action.getIcon().loadDrawableAsync(this, d -> { + if (d != null) { + d.setTint(Color.WHITE); + actionView.setImageDrawable(d); + } + }, mHandler); + } actionView.setContentDescription(action.getContentDescription()); if (action.isEnabled()) { actionView.setOnClickListener(v -> { diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt index 2f99b2a2a449..780e64af3b4d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt @@ -11,6 +11,7 @@ import android.service.notification.StatusBarNotification import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import androidx.test.filters.SmallTest +import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.dump.DumpManager @@ -39,9 +40,11 @@ import org.mockito.Mockito.`when` as whenever private const val KEY = "KEY" private const val KEY_2 = "KEY_2" 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_TITLE = "title" +private const val SESSION_BLANK_TITLE = " " +private const val SESSION_EMPTY_TITLE = "" private const val USER_ID = 0 private fun <T> anyObject(): T { @@ -169,6 +172,95 @@ class MediaDataManagerTest : SysuiTestCase() { } @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 fun testOnNotificationRemoved_withResumption() { // GIVEN that the manager has a notification with a resume action whenever(controller.metadata).thenReturn(metadataBuilder.build()) |