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
This commit is contained in:
Selim Cinek
2020-04-27 15:15:44 -07:00
parent f0f7495c92
commit c54367124c
4 changed files with 158 additions and 50 deletions

View File

@@ -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<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 @@ 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.

View File

@@ -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<MediaAction>,
val actionsToShowInCompact: List<Int>,
val packageName: String?,

View File

@@ -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<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 @@ 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<MediaAction> = 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)

View File

@@ -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);