From e79303394c4fecad8022b6be0122d16940435380 Mon Sep 17 00:00:00 2001 From: Hugh Chen Date: Thu, 15 Oct 2020 11:31:49 +0800 Subject: [PATCH] Fix output switcher didn't show album art MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before this CL, output switcher will show application icon when the meta data is null. In this CL, output switcher will align the behavior of the media object. If the meta data doesn’t have an album art, then create it based on notification icon. Bug: 162998388 Test: atest MediaOutputControllerTest Change-Id: I761d83af8e08a744e4cd5c55ff2c445125885730 --- .../media/dialog/MediaOutputController.java | 35 ++++------ .../media/dialog/MediaOutputDialogFactory.kt | 6 +- .../dialog/MediaOutputBaseDialogTest.java | 6 +- .../dialog/MediaOutputControllerTest.java | 68 ++++++++++++++++++- .../media/dialog/MediaOutputDialogTest.java | 6 +- .../dialog/MediaOutputGroupDialogTest.java | 6 +- 6 files changed, 98 insertions(+), 29 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java index b9fa872521253..80928d6da9788 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java @@ -18,10 +18,7 @@ package com.android.systemui.media.dialog; import android.content.Context; import android.content.Intent; -import android.content.pm.PackageManager; import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.media.MediaMetadata; import android.media.MediaRoute2Info; @@ -49,6 +46,8 @@ import com.android.settingslib.media.MediaOutputSliceConstants; import com.android.settingslib.utils.ThreadUtils; import com.android.systemui.R; import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.phone.ShadeController; import java.util.ArrayList; @@ -61,7 +60,7 @@ import javax.inject.Inject; /** * Controller for media output dialog */ -public class MediaOutputController implements LocalMediaManager.DeviceCallback{ +public class MediaOutputController implements LocalMediaManager.DeviceCallback { private static final String TAG = "MediaOutputController"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @@ -73,6 +72,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback{ private final ActivityStarter mActivityStarter; private final List mGroupMediaDevices = new CopyOnWriteArrayList<>(); private final boolean mAboveStatusbar; + private final NotificationEntryManager mNotificationEntryManager; @VisibleForTesting final List mMediaDevices = new CopyOnWriteArrayList<>(); @@ -85,13 +85,15 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback{ @Inject public MediaOutputController(@NonNull Context context, String packageName, boolean aboveStatusbar, MediaSessionManager mediaSessionManager, LocalBluetoothManager - lbm, ShadeController shadeController, ActivityStarter starter) { + lbm, ShadeController shadeController, ActivityStarter starter, + NotificationEntryManager notificationEntryManager) { mContext = context; mPackageName = packageName; mMediaSessionManager = mediaSessionManager; mShadeController = shadeController; mActivityStarter = starter; mAboveStatusbar = aboveStatusbar; + mNotificationEntryManager = notificationEntryManager; InfoMediaManager imm = new InfoMediaManager(mContext, packageName, null, lbm); mLocalMediaManager = new LocalMediaManager(mContext, lbm, imm, packageName); } @@ -197,7 +199,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback{ if (DEBUG) { Log.d(TAG, "Media meta data does not contain icon information"); } - return getPackageIcon(); + return getNotificationIcon(); } IconCompat getDeviceIconCompat(MediaDevice device) { @@ -213,24 +215,15 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback{ return BluetoothUtils.createIconWithDrawable(drawable); } - private IconCompat getPackageIcon() { + IconCompat getNotificationIcon() { if (TextUtils.isEmpty(mPackageName)) { return null; } - try { - final Drawable drawable = mContext.getPackageManager().getApplicationIcon(mPackageName); - if (drawable instanceof BitmapDrawable) { - return IconCompat.createWithBitmap(((BitmapDrawable) drawable).getBitmap()); - } - final Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), - drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); - final Canvas canvas = new Canvas(bitmap); - drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); - drawable.draw(canvas); - return IconCompat.createWithBitmap(bitmap); - } catch (PackageManager.NameNotFoundException e) { - if (DEBUG) { - Log.e(TAG, "Package is not found. Unable to get package icon."); + for (NotificationEntry entry + : mNotificationEntryManager.getActiveNotificationsForCurrentUser()) { + if (entry.getSbn().getNotification().hasMediaSession() + && TextUtils.equals(entry.getSbn().getPackageName(), mPackageName)) { + return IconCompat.createFromIcon(entry.getSbn().getNotification().getLargeIcon()); } } return null; diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt index e912b7d1b08fc..7d1a7ced7472d 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt @@ -20,6 +20,7 @@ import android.content.Context import android.media.session.MediaSessionManager import com.android.settingslib.bluetooth.LocalBluetoothManager import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.statusbar.notification.NotificationEntryManager import com.android.systemui.statusbar.phone.ShadeController import javax.inject.Inject @@ -31,7 +32,8 @@ class MediaOutputDialogFactory @Inject constructor( private val mediaSessionManager: MediaSessionManager, private val lbm: LocalBluetoothManager?, private val shadeController: ShadeController, - private val starter: ActivityStarter + private val starter: ActivityStarter, + private val notificationEntryManager: NotificationEntryManager ) { companion object { var mediaOutputDialog: MediaOutputDialog? = null @@ -41,7 +43,7 @@ class MediaOutputDialogFactory @Inject constructor( fun create(packageName: String, aboveStatusBar: Boolean) { mediaOutputDialog?.dismiss() mediaOutputDialog = MediaOutputController(context, packageName, aboveStatusBar, - mediaSessionManager, lbm, shadeController, starter).run { + mediaSessionManager, lbm, shadeController, starter, notificationEntryManager).run { MediaOutputDialog(context, aboveStatusBar, this) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java index 4639dcbb1685c..c897d8a53e206 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java @@ -39,6 +39,7 @@ import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.phone.ShadeController; import org.junit.Before; @@ -58,6 +59,8 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase { private LocalBluetoothManager mLocalBluetoothManager = mock(LocalBluetoothManager.class); private ShadeController mShadeController = mock(ShadeController.class); private ActivityStarter mStarter = mock(ActivityStarter.class); + private NotificationEntryManager mNotificationEntryManager = + mock(NotificationEntryManager.class); private MediaOutputBaseDialogImpl mMediaOutputBaseDialogImpl; private MediaOutputController mMediaOutputController; @@ -69,7 +72,8 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase { @Before public void setUp() { mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false, - mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter); + mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter, + mNotificationEntryManager); mMediaOutputBaseDialogImpl = new MediaOutputBaseDialogImpl(mContext, mMediaOutputController); mMediaOutputBaseDialogImpl.onCreate(new Bundle()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java index 0576667484df1..6ceac131ed4b3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java @@ -27,15 +27,19 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.app.Notification; import android.content.Context; +import android.graphics.drawable.Icon; import android.media.MediaDescription; import android.media.MediaMetadata; import android.media.RoutingSessionInfo; import android.media.session.MediaController; import android.media.session.MediaSessionManager; +import android.service.notification.StatusBarNotification; import android.testing.AndroidTestingRunner; import android.text.TextUtils; +import androidx.core.graphics.drawable.IconCompat; import androidx.test.filters.SmallTest; import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; @@ -45,6 +49,8 @@ import com.android.settingslib.media.MediaDevice; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.phone.ShadeController; import org.junit.Before; @@ -81,6 +87,8 @@ public class MediaOutputControllerTest extends SysuiTestCase { private RoutingSessionInfo mRemoteSessionInfo = mock(RoutingSessionInfo.class); private ShadeController mShadeController = mock(ShadeController.class); private ActivityStarter mStarter = mock(ActivityStarter.class); + private NotificationEntryManager mNotificationEntryManager = + mock(NotificationEntryManager.class); private Context mSpyContext; private MediaOutputController mMediaOutputController; @@ -100,8 +108,10 @@ public class MediaOutputControllerTest extends SysuiTestCase { MediaSessionManager.class); when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn( mCachedBluetoothDeviceManager); + mMediaOutputController = new MediaOutputController(mSpyContext, TEST_PACKAGE_NAME, false, - mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter); + mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter, + mNotificationEntryManager); mLocalMediaManager = spy(mMediaOutputController.mLocalMediaManager); mMediaOutputController.mLocalMediaManager = mLocalMediaManager; MediaDescription.Builder builder = new MediaDescription.Builder(); @@ -144,7 +154,8 @@ public class MediaOutputControllerTest extends SysuiTestCase { @Test public void start_withoutPackageName_verifyMediaControllerInit() { mMediaOutputController = new MediaOutputController(mSpyContext, null, false, - mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter); + mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter, + mNotificationEntryManager); mMediaOutputController.start(mCb); @@ -164,7 +175,9 @@ public class MediaOutputControllerTest extends SysuiTestCase { @Test public void stop_withoutPackageName_verifyMediaControllerDeinit() { mMediaOutputController = new MediaOutputController(mSpyContext, null, false, - mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter); + mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter, + mNotificationEntryManager); + mMediaOutputController.start(mCb); mMediaOutputController.stop(); @@ -423,4 +436,53 @@ public class MediaOutputControllerTest extends SysuiTestCase { } assertThat(newDevices.get(4).getId()).isEqualTo(TEST_DEVICE_5_ID); } + + @Test + public void getNotificationLargeIcon_withoutPackageName_returnsNull() { + mMediaOutputController = new MediaOutputController(mSpyContext, null, false, + mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter, + mNotificationEntryManager); + + assertThat(mMediaOutputController.getNotificationIcon()).isNull(); + } + + @Test + public void getNotificationLargeIcon_withPackageNameAndMediaSession_returnsIconCompat() { + final List entryList = new ArrayList<>(); + final NotificationEntry entry = mock(NotificationEntry.class); + final StatusBarNotification sbn = mock(StatusBarNotification.class); + final Notification notification = mock(Notification.class); + final Icon icon = mock(Icon.class); + entryList.add(entry); + + when(mNotificationEntryManager.getActiveNotificationsForCurrentUser()) + .thenReturn(entryList); + when(entry.getSbn()).thenReturn(sbn); + when(sbn.getNotification()).thenReturn(notification); + when(sbn.getPackageName()).thenReturn(TEST_PACKAGE_NAME); + when(notification.hasMediaSession()).thenReturn(true); + when(notification.getLargeIcon()).thenReturn(icon); + + assertThat(mMediaOutputController.getNotificationIcon() instanceof IconCompat).isTrue(); + } + + @Test + public void getNotificationLargeIcon_withPackageNameAndNoMediaSession_returnsNull() { + final List entryList = new ArrayList<>(); + final NotificationEntry entry = mock(NotificationEntry.class); + final StatusBarNotification sbn = mock(StatusBarNotification.class); + final Notification notification = mock(Notification.class); + final Icon icon = mock(Icon.class); + entryList.add(entry); + + when(mNotificationEntryManager.getActiveNotificationsForCurrentUser()) + .thenReturn(entryList); + when(entry.getSbn()).thenReturn(sbn); + when(sbn.getNotification()).thenReturn(notification); + when(sbn.getPackageName()).thenReturn(TEST_PACKAGE_NAME); + when(notification.hasMediaSession()).thenReturn(false); + when(notification.getLargeIcon()).thenReturn(icon); + + assertThat(mMediaOutputController.getNotificationIcon()).isNull(); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java index 8648d918d0065..c1e7db1f9b5e6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java @@ -34,6 +34,7 @@ import com.android.settingslib.media.LocalMediaManager; import com.android.settingslib.media.MediaDevice; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.phone.ShadeController; import org.junit.After; @@ -58,6 +59,8 @@ public class MediaOutputDialogTest extends SysuiTestCase { private ActivityStarter mStarter = mock(ActivityStarter.class); private LocalMediaManager mLocalMediaManager = mock(LocalMediaManager.class); private MediaDevice mMediaDevice = mock(MediaDevice.class); + private NotificationEntryManager mNotificationEntryManager = + mock(NotificationEntryManager.class); private MediaOutputDialog mMediaOutputDialog; private MediaOutputController mMediaOutputController; @@ -66,7 +69,8 @@ public class MediaOutputDialogTest extends SysuiTestCase { @Before public void setUp() { mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false, - mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter); + mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter, + mNotificationEntryManager); mMediaOutputController.mLocalMediaManager = mLocalMediaManager; mMediaOutputDialog = new MediaOutputDialog(mContext, false, mMediaOutputController); diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java index 10f2bd5e46999..5813350276718 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java @@ -34,6 +34,7 @@ import com.android.settingslib.media.MediaDevice; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.phone.ShadeController; import org.junit.After; @@ -59,6 +60,8 @@ public class MediaOutputGroupDialogTest extends SysuiTestCase { private LocalMediaManager mLocalMediaManager = mock(LocalMediaManager.class); private MediaDevice mMediaDevice = mock(MediaDevice.class); private MediaDevice mMediaDevice1 = mock(MediaDevice.class); + private NotificationEntryManager mNotificationEntryManager = + mock(NotificationEntryManager.class); private MediaOutputGroupDialog mMediaOutputGroupDialog; private MediaOutputController mMediaOutputController; @@ -67,7 +70,8 @@ public class MediaOutputGroupDialogTest extends SysuiTestCase { @Before public void setUp() { mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false, - mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter); + mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter, + mNotificationEntryManager); mMediaOutputController.mLocalMediaManager = mLocalMediaManager; mMediaOutputGroupDialog = new MediaOutputGroupDialog(mContext, false, mMediaOutputController);