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
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index 673fafa..6621ca1 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.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.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 @@
private boolean mIsRegistered = false;
private final List<KeyFrames> 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 @@
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 @@
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 @@
}
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 @@
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 8165362..6a26461 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 @@
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 @@
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<MediaAction>,
val actionsToShowInCompact: List<Int>,
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 227e8e7..e7d0f7e 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 @@
) {
private val listeners: MutableSet<Listener> = mutableSetOf()
- private var albumArtSize: Int = 0
- private var albumArtRadius: Int = 0
private val mediaEntries: LinkedHashMap<String, MediaData> = 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 @@
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 @@
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 @@
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<MediaAction> = ArrayList()
@@ -167,12 +192,55 @@
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 @@
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 0ccebc13..56f8e08 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 @@
}
@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 @@
}
@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);