From c54367124cd59746c260197bb9dfe830484c97b2 Mon Sep 17 00:00:00 2001 From: Selim Cinek Date: Mon, 27 Apr 2020 15:15:44 -0700 Subject: [PATCH] Work back in media uri loading that was changed in the refactor Rebuilt ag/11180565 and fixed a few cases while doing so. This also includes the fixes from ag/11270945 Test: play music from spotify Bug: 154137987 Change-Id: Ib912df716d754f451b68b289920a2e138977b9bd --- .../systemui/media/MediaControlPanel.java | 60 ++++++-- .../com/android/systemui/media/MediaData.kt | 7 +- .../systemui/media/MediaDataManager.kt | 137 +++++++++++++----- .../notification/row/HybridGroupManager.java | 4 +- 4 files changed, 158 insertions(+), 50 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java index 673fafa0b1a3a..6621ca13583c0 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java @@ -26,10 +26,13 @@ import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.ColorStateList; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; +import android.graphics.drawable.Icon; import android.graphics.drawable.RippleDrawable; -import android.media.MediaMetadata; import android.media.session.MediaController; import android.media.session.MediaController.PlaybackInfo; import android.media.session.MediaSession; @@ -52,7 +55,10 @@ import androidx.constraintlayout.motion.widget.KeyAttributes; import androidx.constraintlayout.motion.widget.KeyFrames; import androidx.constraintlayout.motion.widget.MotionLayout; import androidx.constraintlayout.widget.ConstraintSet; +import androidx.core.graphics.drawable.RoundedBitmapDrawable; +import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory; +import com.android.settingslib.Utils; import com.android.settingslib.media.LocalMediaManager; import com.android.settingslib.media.MediaDevice; import com.android.settingslib.media.MediaOutputSliceConstants; @@ -104,6 +110,8 @@ public class MediaControlPanel { private boolean mIsRegistered = false; private final List mKeyFrames; private String mKey; + private int mAlbumArtSize; + private int mAlbumArtRadius; public static final String MEDIA_PREFERENCES = "media_control_prefs"; public static final String MEDIA_PREFERENCE_KEY = "browser_components"; @@ -112,13 +120,6 @@ public class MediaControlPanel { private boolean mIsRemotePlayback; private QSMediaBrowser mQSMediaBrowser; - // URI fields to try loading album art from - private static final String[] ART_URIS = { - MediaMetadata.METADATA_KEY_ALBUM_ART_URI, - MediaMetadata.METADATA_KEY_ART_URI, - MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI - }; - private final MediaController.Callback mSessionCallback = new MediaController.Callback() { @Override public void onSessionDestroyed() { @@ -210,6 +211,13 @@ public class MediaControlPanel { SeekBar bar = getView().findViewById(R.id.media_progress_bar); bar.setOnSeekBarChangeListener(mSeekBarViewModel.getSeekBarListener()); bar.setOnTouchListener(mSeekBarViewModel.getSeekBarTouchListener()); + loadDimens(); + } + + private void loadDimens() { + mAlbumArtRadius = mContext.getResources().getDimensionPixelSize( + Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius)); + mAlbumArtSize = mContext.getResources().getDimensionPixelSize(R.dimen.qs_media_album_size); } /** @@ -296,7 +304,10 @@ public class MediaControlPanel { } ImageView albumView = mMediaNotifView.findViewById(R.id.album_art); - albumView.setImageDrawable(data.getArtwork()); + // TODO: migrate this to a view with rounded corners instead of baking the rounding + // into the bitmap + Drawable artwork = createRoundedBitmap(data.getArtwork()); + albumView.setImageDrawable(artwork); // App icon ImageView appIcon = mMediaNotifView.requireViewById(R.id.icon); @@ -400,6 +411,37 @@ public class MediaControlPanel { makeActive(); } + private Drawable createRoundedBitmap(Icon icon) { + if (icon == null) { + return null; + } + // Let's scale down the View, such that the content always nicely fills the view. + // ThumbnailUtils actually scales it down such that it may not be filled for odd aspect + // ratios + Drawable drawable = icon.loadDrawable(mContext); + float aspectRatio = drawable.getIntrinsicHeight() / (float) drawable.getIntrinsicWidth(); + Rect bounds; + if (aspectRatio > 1.0f) { + bounds = new Rect(0, 0, mAlbumArtSize, (int) (mAlbumArtSize * aspectRatio)); + } else { + bounds = new Rect(0, 0, (int) (mAlbumArtSize / aspectRatio), mAlbumArtSize); + } + if (bounds.width() > mAlbumArtSize || bounds.height() > mAlbumArtSize) { + float offsetX = (bounds.width() - mAlbumArtSize) / 2.0f; + float offsetY = (bounds.height() - mAlbumArtSize) / 2.0f; + bounds.offset((int) -offsetX,(int) -offsetY); + } + drawable.setBounds(bounds); + Bitmap scaled = Bitmap.createBitmap(mAlbumArtSize, mAlbumArtSize, + Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(scaled); + drawable.draw(canvas); + RoundedBitmapDrawable artwork = RoundedBitmapDrawableFactory.create( + mContext.getResources(), scaled); + artwork.setCornerRadius(mAlbumArtRadius); + return artwork; + } + /** * Updates the keyframe visibility such that only views that are not visible actually go * through a transition and fade in. diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt index 8165362ef677c..6a2646170e854 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt @@ -18,6 +18,7 @@ package com.android.systemui.media import android.app.PendingIntent import android.graphics.drawable.Drawable +import android.graphics.drawable.Icon import android.media.session.MediaSession /** State of a media view. */ @@ -27,9 +28,9 @@ data class MediaData( val backgroundColor: Int, val app: String?, val appIcon: Drawable?, - val artist: String?, - val song: String?, - val artwork: Drawable?, + val artist: CharSequence?, + val song: CharSequence?, + val artwork: Icon?, val actions: List, val actionsToShowInCompact: List, val packageName: String?, diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt index 227e8e7b3986f..e7d0f7ec1a375 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt @@ -17,29 +17,45 @@ package com.android.systemui.media import android.app.Notification +import android.content.ContentResolver import android.content.Context import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.ImageDecoder +import android.graphics.Rect import android.graphics.drawable.Drawable +import android.graphics.drawable.Icon import android.media.MediaMetadata import android.media.session.MediaSession -import android.media.session.PlaybackState +import android.net.Uri import android.provider.Settings import android.service.notification.StatusBarNotification -import androidx.core.graphics.drawable.RoundedBitmapDrawable -import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory +import android.text.TextUtils +import android.util.Log import com.android.internal.util.ContrastColorUtil -import com.android.settingslib.Utils -import com.android.systemui.R import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.statusbar.NotificationMediaManager import com.android.systemui.statusbar.notification.MediaNotificationProcessor +import com.android.systemui.statusbar.notification.row.HybridGroupManager +import java.io.IOException import java.util.* import java.util.concurrent.Executor import javax.inject.Inject import javax.inject.Singleton import kotlin.collections.LinkedHashMap +// URI fields to try loading album art from +private val ART_URIS = arrayOf( + MediaMetadata.METADATA_KEY_ALBUM_ART_URI, + MediaMetadata.METADATA_KEY_ART_URI, + MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI +) + +private const val TAG = "MediaDataManager" + +private val LOADING = MediaData(false, 0, 0, null, null, null, null, null, + emptyList(), emptyList(), null, null, null) + /** * A class that facilitates management and loading of Media Data, ready for binding. */ @@ -52,20 +68,8 @@ class MediaDataManager @Inject constructor( ) { private val listeners: MutableSet = mutableSetOf() - private var albumArtSize: Int = 0 - private var albumArtRadius: Int = 0 private val mediaEntries: LinkedHashMap = LinkedHashMap() - init { - loadDimens() - } - - private fun loadDimens() { - albumArtRadius = context.resources.getDimensionPixelSize( - Utils.getThemeAttr(context, android.R.attr.dialogCornerRadius)) - albumArtSize = context.resources.getDimensionPixelSize(R.dimen.qs_media_album_size) - } - fun onNotificationAdded(key: String, sbn: StatusBarNotification) { if (isMediaNotification(sbn)) { if (!mediaEntries.containsKey(key)) { @@ -111,9 +115,31 @@ class MediaDataManager @Inject constructor( if (artworkBitmap == null) { artworkBitmap = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART) } - // TODO: load media data from uri - if (artworkBitmap != null) { + if (artworkBitmap == null) { + artworkBitmap = loadBitmapFromUri(metadata) + } + val artWorkIcon = if (artworkBitmap == null) { + notif.getLargeIcon() + } else { + Icon.createWithBitmap(artworkBitmap) + } + if (artWorkIcon != null) { // If we have art, get colors from that + if (artworkBitmap == null) { + if (artWorkIcon.type == Icon.TYPE_BITMAP + || artWorkIcon.type == Icon.TYPE_ADAPTIVE_BITMAP) { + artworkBitmap = artWorkIcon.bitmap + } else { + val drawable: Drawable = artWorkIcon.loadDrawable(context) + artworkBitmap = Bitmap.createBitmap( + drawable.intrinsicWidth, + drawable.intrinsicHeight, + Bitmap.Config.ARGB_8888) + val canvas = Canvas(artworkBitmap) + drawable.setBounds(0, 0, drawable.intrinsicWidth, drawable.intrinsicHeight) + drawable.draw(canvas) + } + } val p = MediaNotificationProcessor.generateArtworkPaletteBuilder(artworkBitmap) .generate() val swatch = MediaNotificationProcessor.findBackgroundSwatch(p) @@ -126,16 +152,6 @@ class MediaDataManager @Inject constructor( isDark) fgColor = ContrastColorUtil.ensureTextContrast(fgColor, bgColor, isDark) - // Album art - var artwork: RoundedBitmapDrawable? = null - if (artworkBitmap != null) { - val original = artworkBitmap.copy(Bitmap.Config.ARGB_8888, true) - val scaled = Bitmap.createScaledBitmap(original, albumArtSize, albumArtSize, - false) - artwork = RoundedBitmapDrawableFactory.create(context.resources, scaled) - artwork.cornerRadius = albumArtRadius.toFloat() - } - // App name val builder = Notification.Builder.recoverBuilder(context, notif) val app = builder.loadHeaderAppName() @@ -144,10 +160,19 @@ class MediaDataManager @Inject constructor( val smallIconDrawable: Drawable = sbn.notification.smallIcon.loadDrawable(context) // Song name - val song: String = metadata.getString(MediaMetadata.METADATA_KEY_TITLE) + var song: CharSequence? = metadata.getString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE) + if (song == null) { + song = metadata.getString(MediaMetadata.METADATA_KEY_TITLE) + } + if (song == null) { + song = HybridGroupManager.resolveTitle(notif) + } // Artist name - val artist: String = metadata.getString(MediaMetadata.METADATA_KEY_ARTIST) + var artist: CharSequence? = metadata.getString(MediaMetadata.METADATA_KEY_ARTIST) + if (artist == null) { + artist = HybridGroupManager.resolveText(notif) + } // Control buttons val actionIcons: MutableList = ArrayList() @@ -167,12 +192,55 @@ class MediaDataManager @Inject constructor( foregroundExcecutor.execute { onMediaDataLoaded(key, MediaData(true, fgColor, bgColor, app, smallIconDrawable, artist, - song, artwork, actionIcons, actionsToShowCollapsed, sbn.packageName, token, + song, artWorkIcon, actionIcons, actionsToShowCollapsed, sbn.packageName, token, notif.contentIntent)) } } + /** + * Load a bitmap from the various Art metadata URIs + */ + private fun loadBitmapFromUri(metadata: MediaMetadata): Bitmap? { + for (uri in ART_URIS) { + val uriString = metadata.getString(uri) + if (!TextUtils.isEmpty(uriString)) { + val albumArt = loadBitmapFromUri(Uri.parse(uriString)) + if (albumArt != null) { + Log.d(TAG, "loaded art from $uri") + break + } + } + } + return null + } + + /** + * Load a bitmap from a URI + * @param uri the uri to load + * @return bitmap, or null if couldn't be loaded + */ + private fun loadBitmapFromUri(uri: Uri): Bitmap? { + // ImageDecoder requires a scheme of the following types + if (uri.getScheme() == null) { + return null; + } + + if (!uri.getScheme().equals(ContentResolver.SCHEME_CONTENT) + && !uri.getScheme().equals(ContentResolver.SCHEME_ANDROID_RESOURCE) + && !uri.getScheme().equals(ContentResolver.SCHEME_FILE)) { + return null; + } + + val source = ImageDecoder.createSource(context.getContentResolver(), uri) + return try { + ImageDecoder.decodeBitmap(source) + } catch (e: IOException) { + e.printStackTrace() + null + } + } + fun onMediaDataLoaded(key: String, data: MediaData) { if (mediaEntries.containsKey(key)) { // Otherwise this was removed already @@ -236,6 +304,3 @@ class MediaDataManager @Inject constructor( fun onMediaDataRemoved(key: String) {} } } - -private val LOADING = MediaData(false, 0, 0, null, null, null, null, null, - emptyList(), emptyList(), null, null, null) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java index 0ccebc130b1d9..56f8e087d64dc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java @@ -109,7 +109,7 @@ public class HybridGroupManager { } @Nullable - private CharSequence resolveText(Notification notification) { + public static CharSequence resolveText(Notification notification) { CharSequence contentText = notification.extras.getCharSequence(Notification.EXTRA_TEXT); if (contentText == null) { contentText = notification.extras.getCharSequence(Notification.EXTRA_BIG_TEXT); @@ -118,7 +118,7 @@ public class HybridGroupManager { } @Nullable - private CharSequence resolveTitle(Notification notification) { + public static CharSequence resolveTitle(Notification notification) { CharSequence titleText = notification.extras.getCharSequence(Notification.EXTRA_TITLE); if (titleText == null) { titleText = notification.extras.getCharSequence(Notification.EXTRA_TITLE_BIG);