Merge "Improve album art handling" into rvc-dev am: 9f4b4c23e7
Change-Id: Ib8d240892f3fa3269e575695518b1e44cc8b3973
This commit is contained in:
@@ -26,14 +26,18 @@ import android.content.pm.PackageManager;
|
|||||||
import android.content.pm.ResolveInfo;
|
import android.content.pm.ResolveInfo;
|
||||||
import android.content.res.ColorStateList;
|
import android.content.res.ColorStateList;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.ImageDecoder;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.graphics.drawable.GradientDrawable;
|
import android.graphics.drawable.GradientDrawable;
|
||||||
|
import android.graphics.drawable.Icon;
|
||||||
import android.graphics.drawable.RippleDrawable;
|
import android.graphics.drawable.RippleDrawable;
|
||||||
import android.media.MediaDescription;
|
import android.media.MediaDescription;
|
||||||
import android.media.MediaMetadata;
|
import android.media.MediaMetadata;
|
||||||
|
import android.media.ThumbnailUtils;
|
||||||
import android.media.session.MediaController;
|
import android.media.session.MediaController;
|
||||||
import android.media.session.MediaSession;
|
import android.media.session.MediaSession;
|
||||||
import android.media.session.PlaybackState;
|
import android.media.session.PlaybackState;
|
||||||
|
import android.net.Uri;
|
||||||
import android.service.media.MediaBrowserService;
|
import android.service.media.MediaBrowserService;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
@@ -59,6 +63,7 @@ import com.android.systemui.plugins.ActivityStarter;
|
|||||||
import com.android.systemui.qs.QSMediaBrowser;
|
import com.android.systemui.qs.QSMediaBrowser;
|
||||||
import com.android.systemui.util.Assert;
|
import com.android.systemui.util.Assert;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
@@ -99,6 +104,13 @@ public class MediaControlPanel {
|
|||||||
com.android.internal.R.id.action4
|
com.android.internal.R.id.action4
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 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() {
|
private final MediaController.Callback mSessionCallback = new MediaController.Callback() {
|
||||||
@Override
|
@Override
|
||||||
public void onSessionDestroyed() {
|
public void onSessionDestroyed() {
|
||||||
@@ -205,14 +217,16 @@ public class MediaControlPanel {
|
|||||||
* Update the media panel view for the given media session
|
* Update the media panel view for the given media session
|
||||||
* @param token
|
* @param token
|
||||||
* @param iconDrawable
|
* @param iconDrawable
|
||||||
|
* @param largeIcon
|
||||||
* @param iconColor
|
* @param iconColor
|
||||||
* @param bgColor
|
* @param bgColor
|
||||||
* @param contentIntent
|
* @param contentIntent
|
||||||
* @param appNameString
|
* @param appNameString
|
||||||
* @param key
|
* @param key
|
||||||
*/
|
*/
|
||||||
public void setMediaSession(MediaSession.Token token, Drawable iconDrawable, int iconColor,
|
public void setMediaSession(MediaSession.Token token, Drawable iconDrawable, Icon largeIcon,
|
||||||
int bgColor, PendingIntent contentIntent, String appNameString, String key) {
|
int iconColor, int bgColor, PendingIntent contentIntent, String appNameString,
|
||||||
|
String key) {
|
||||||
// Ensure that component names are updated if token has changed
|
// Ensure that component names are updated if token has changed
|
||||||
if (mToken == null || !mToken.equals(token)) {
|
if (mToken == null || !mToken.equals(token)) {
|
||||||
mToken = token;
|
mToken = token;
|
||||||
@@ -303,7 +317,7 @@ public class MediaControlPanel {
|
|||||||
ImageView albumView = mMediaNotifView.findViewById(R.id.album_art);
|
ImageView albumView = mMediaNotifView.findViewById(R.id.album_art);
|
||||||
if (albumView != null) {
|
if (albumView != null) {
|
||||||
// Resize art in a background thread
|
// Resize art in a background thread
|
||||||
mBackgroundExecutor.execute(() -> processAlbumArt(mediaMetadata, albumView));
|
mBackgroundExecutor.execute(() -> processAlbumArt(mediaMetadata, largeIcon, albumView));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Song name
|
// Song name
|
||||||
@@ -396,30 +410,82 @@ public class MediaControlPanel {
|
|||||||
* @param albumView view to hold the album art
|
* @param albumView view to hold the album art
|
||||||
*/
|
*/
|
||||||
protected void processAlbumArt(MediaDescription description, ImageView albumView) {
|
protected void processAlbumArt(MediaDescription description, ImageView albumView) {
|
||||||
Bitmap albumArt = description.getIconBitmap();
|
Bitmap albumArt = null;
|
||||||
//TODO check other fields (b/151054111, b/152067055)
|
|
||||||
|
// First try loading from URI
|
||||||
|
albumArt = loadBitmapFromUri(description.getIconUri());
|
||||||
|
|
||||||
|
// Then check bitmap
|
||||||
|
if (albumArt == null) {
|
||||||
|
albumArt = description.getIconBitmap();
|
||||||
|
}
|
||||||
|
|
||||||
processAlbumArtInternal(albumArt, albumView);
|
processAlbumArtInternal(albumArt, albumView);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process album art for layout
|
* Process album art for layout
|
||||||
* @param metadata media metadata
|
* @param metadata media metadata
|
||||||
|
* @param largeIcon from notification, checked as a fallback if metadata does not have art
|
||||||
* @param albumView view to hold the album art
|
* @param albumView view to hold the album art
|
||||||
*/
|
*/
|
||||||
private void processAlbumArt(MediaMetadata metadata, ImageView albumView) {
|
private void processAlbumArt(MediaMetadata metadata, Icon largeIcon, ImageView albumView) {
|
||||||
Bitmap albumArt = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
|
Bitmap albumArt = null;
|
||||||
//TODO check other fields (b/151054111, b/152067055)
|
|
||||||
|
// First look in URI fields
|
||||||
|
for (String field : ART_URIS) {
|
||||||
|
String uriString = metadata.getString(field);
|
||||||
|
if (uriString != null) {
|
||||||
|
albumArt = loadBitmapFromUri(Uri.parse(uriString));
|
||||||
|
if (albumArt != null) {
|
||||||
|
Log.d(TAG, "loaded art from " + field);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then check bitmap field
|
||||||
|
if (albumArt == null) {
|
||||||
|
albumArt = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally try the notification's largeIcon
|
||||||
|
if (albumArt == null && largeIcon != null) {
|
||||||
|
albumArt = largeIcon.getBitmap();
|
||||||
|
}
|
||||||
|
|
||||||
processAlbumArtInternal(albumArt, albumView);
|
processAlbumArtInternal(albumArt, albumView);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processAlbumArtInternal(Bitmap albumArt, ImageView albumView) {
|
/**
|
||||||
float radius = mContext.getResources().getDimension(R.dimen.qs_media_corner_radius);
|
* Load a bitmap from a URI
|
||||||
|
* @param uri
|
||||||
|
* @return bitmap, or null if couldn't be loaded
|
||||||
|
*/
|
||||||
|
private Bitmap loadBitmapFromUri(Uri uri) {
|
||||||
|
ImageDecoder.Source source = ImageDecoder.createSource(mContext.getContentResolver(), uri);
|
||||||
|
try {
|
||||||
|
return ImageDecoder.decodeBitmap(source);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resize and crop the image if provided and update the control view
|
||||||
|
* @param albumArt Bitmap of art to display, or null to hide view
|
||||||
|
* @param albumView View that will hold the art
|
||||||
|
*/
|
||||||
|
private void processAlbumArtInternal(@Nullable Bitmap albumArt, ImageView albumView) {
|
||||||
|
// Resize
|
||||||
RoundedBitmapDrawable roundedDrawable = null;
|
RoundedBitmapDrawable roundedDrawable = null;
|
||||||
if (albumArt != null) {
|
if (albumArt != null) {
|
||||||
|
float radius = mContext.getResources().getDimension(R.dimen.qs_media_corner_radius);
|
||||||
Bitmap original = albumArt.copy(Bitmap.Config.ARGB_8888, true);
|
Bitmap original = albumArt.copy(Bitmap.Config.ARGB_8888, true);
|
||||||
int albumSize = (int) mContext.getResources().getDimension(
|
int albumSize = (int) mContext.getResources().getDimension(
|
||||||
R.dimen.qs_media_album_size);
|
R.dimen.qs_media_album_size);
|
||||||
Bitmap scaled = Bitmap.createScaledBitmap(original, albumSize, albumSize, false);
|
Bitmap scaled = ThumbnailUtils.extractThumbnail(original, albumSize, albumSize);
|
||||||
roundedDrawable = RoundedBitmapDrawableFactory.create(mContext.getResources(), scaled);
|
roundedDrawable = RoundedBitmapDrawableFactory.create(mContext.getResources(), scaled);
|
||||||
roundedDrawable.setCornerRadius(radius);
|
roundedDrawable.setCornerRadius(radius);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import android.content.Context;
|
|||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.res.ColorStateList;
|
import android.content.res.ColorStateList;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.graphics.drawable.Icon;
|
||||||
import android.media.MediaDescription;
|
import android.media.MediaDescription;
|
||||||
import android.media.session.MediaController;
|
import android.media.session.MediaController;
|
||||||
import android.media.session.MediaSession;
|
import android.media.session.MediaSession;
|
||||||
@@ -115,8 +116,8 @@ public class QSMediaPlayer extends MediaControlPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set what we can normally
|
// Set what we can normally
|
||||||
super.setMediaSession(token, icon, iconColor, bgColor, contentIntent, appName.toString(),
|
super.setMediaSession(token, icon, null, iconColor, bgColor, contentIntent,
|
||||||
null);
|
appName.toString(), null);
|
||||||
|
|
||||||
// Then add info from MediaDescription
|
// Then add info from MediaDescription
|
||||||
ImageView albumView = mMediaNotifView.findViewById(R.id.album_art);
|
ImageView albumView = mMediaNotifView.findViewById(R.id.album_art);
|
||||||
@@ -149,6 +150,7 @@ public class QSMediaPlayer extends MediaControlPanel {
|
|||||||
* Update media panel view for the given media session
|
* Update media panel view for the given media session
|
||||||
* @param token token for this media session
|
* @param token token for this media session
|
||||||
* @param icon app notification icon
|
* @param icon app notification icon
|
||||||
|
* @param largeIcon notification's largeIcon, used as a fallback for album art
|
||||||
* @param iconColor foreground color (for text, icons)
|
* @param iconColor foreground color (for text, icons)
|
||||||
* @param bgColor background color
|
* @param bgColor background color
|
||||||
* @param actionsContainer a LinearLayout containing the media action buttons
|
* @param actionsContainer a LinearLayout containing the media action buttons
|
||||||
@@ -156,11 +158,12 @@ public class QSMediaPlayer extends MediaControlPanel {
|
|||||||
* @param appName Application title
|
* @param appName Application title
|
||||||
* @param key original notification's key
|
* @param key original notification's key
|
||||||
*/
|
*/
|
||||||
public void setMediaSession(MediaSession.Token token, Drawable icon, int iconColor,
|
public void setMediaSession(MediaSession.Token token, Drawable icon, Icon largeIcon,
|
||||||
int bgColor, View actionsContainer, PendingIntent contentIntent, String appName,
|
int iconColor, int bgColor, View actionsContainer, PendingIntent contentIntent,
|
||||||
String key) {
|
String appName, String key) {
|
||||||
|
|
||||||
super.setMediaSession(token, icon, iconColor, bgColor, contentIntent, appName, key);
|
super.setMediaSession(token, icon, largeIcon, iconColor, bgColor, contentIntent, appName,
|
||||||
|
key);
|
||||||
|
|
||||||
// Media controls
|
// Media controls
|
||||||
if (actionsContainer != null) {
|
if (actionsContainer != null) {
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import android.content.res.Configuration;
|
|||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.graphics.drawable.Icon;
|
||||||
import android.media.MediaDescription;
|
import android.media.MediaDescription;
|
||||||
import android.media.session.MediaSession;
|
import android.media.session.MediaSession;
|
||||||
import android.metrics.LogMaker;
|
import android.metrics.LogMaker;
|
||||||
@@ -225,14 +226,16 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
|
|||||||
* Add or update a player for the associated media session
|
* Add or update a player for the associated media session
|
||||||
* @param token
|
* @param token
|
||||||
* @param icon
|
* @param icon
|
||||||
|
* @param largeIcon
|
||||||
* @param iconColor
|
* @param iconColor
|
||||||
* @param bgColor
|
* @param bgColor
|
||||||
* @param actionsContainer
|
* @param actionsContainer
|
||||||
* @param notif
|
* @param notif
|
||||||
* @param key
|
* @param key
|
||||||
*/
|
*/
|
||||||
public void addMediaSession(MediaSession.Token token, Drawable icon, int iconColor, int bgColor,
|
public void addMediaSession(MediaSession.Token token, Drawable icon, Icon largeIcon,
|
||||||
View actionsContainer, StatusBarNotification notif, String key) {
|
int iconColor, int bgColor, View actionsContainer, StatusBarNotification notif,
|
||||||
|
String key) {
|
||||||
if (!useQsMediaPlayer(mContext)) {
|
if (!useQsMediaPlayer(mContext)) {
|
||||||
// Shouldn't happen, but just in case
|
// Shouldn't happen, but just in case
|
||||||
Log.e(TAG, "Tried to add media session without player!");
|
Log.e(TAG, "Tried to add media session without player!");
|
||||||
@@ -296,7 +299,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
|
|||||||
Log.d(TAG, "setting player session");
|
Log.d(TAG, "setting player session");
|
||||||
String appName = Notification.Builder.recoverBuilder(getContext(), notif.getNotification())
|
String appName = Notification.Builder.recoverBuilder(getContext(), notif.getNotification())
|
||||||
.loadHeaderAppName();
|
.loadHeaderAppName();
|
||||||
player.setMediaSession(token, icon, iconColor, bgColor, actionsContainer,
|
player.setMediaSession(token, icon, largeIcon, iconColor, bgColor, actionsContainer,
|
||||||
notif.getNotification().contentIntent, appName, key);
|
notif.getNotification().contentIntent, appName, key);
|
||||||
|
|
||||||
if (mMediaPlayers.size() > 0) {
|
if (mMediaPlayers.size() > 0) {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package com.android.systemui.qs;
|
|||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.graphics.drawable.Icon;
|
||||||
import android.media.session.MediaController;
|
import android.media.session.MediaController;
|
||||||
import android.media.session.MediaSession;
|
import android.media.session.MediaSession;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -58,6 +59,7 @@ public class QuickQSMediaPlayer extends MediaControlPanel {
|
|||||||
* Update media panel view for the given media session
|
* Update media panel view for the given media session
|
||||||
* @param token token for this media session
|
* @param token token for this media session
|
||||||
* @param icon app notification icon
|
* @param icon app notification icon
|
||||||
|
* @param largeIcon notification's largeIcon, used as a fallback for album art
|
||||||
* @param iconColor foreground color (for text, icons)
|
* @param iconColor foreground color (for text, icons)
|
||||||
* @param bgColor background color
|
* @param bgColor background color
|
||||||
* @param actionsContainer a LinearLayout containing the media action buttons
|
* @param actionsContainer a LinearLayout containing the media action buttons
|
||||||
@@ -66,8 +68,9 @@ public class QuickQSMediaPlayer extends MediaControlPanel {
|
|||||||
* @param contentIntent Intent to send when user taps on the view
|
* @param contentIntent Intent to send when user taps on the view
|
||||||
* @param key original notification's key
|
* @param key original notification's key
|
||||||
*/
|
*/
|
||||||
public void setMediaSession(MediaSession.Token token, Drawable icon, int iconColor, int bgColor,
|
public void setMediaSession(MediaSession.Token token, Drawable icon, Icon largeIcon,
|
||||||
View actionsContainer, int[] actionsToShow, PendingIntent contentIntent, String key) {
|
int iconColor, int bgColor, View actionsContainer, int[] actionsToShow,
|
||||||
|
PendingIntent contentIntent, String key) {
|
||||||
// Only update if this is a different session and currently playing
|
// Only update if this is a different session and currently playing
|
||||||
String oldPackage = "";
|
String oldPackage = "";
|
||||||
if (getController() != null) {
|
if (getController() != null) {
|
||||||
@@ -82,7 +85,7 @@ public class QuickQSMediaPlayer extends MediaControlPanel {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
super.setMediaSession(token, icon, iconColor, bgColor, contentIntent, null, key);
|
super.setMediaSession(token, icon, largeIcon, iconColor, bgColor, contentIntent, null, key);
|
||||||
|
|
||||||
LinearLayout parentActionsLayout = (LinearLayout) actionsContainer;
|
LinearLayout parentActionsLayout = (LinearLayout) actionsContainer;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|||||||
@@ -191,6 +191,7 @@ public class NotificationMediaTemplateViewWrapper extends NotificationTemplateVi
|
|||||||
Drawable iconDrawable = notif.getSmallIcon().loadDrawable(mContext);
|
Drawable iconDrawable = notif.getSmallIcon().loadDrawable(mContext);
|
||||||
panel.getMediaPlayer().setMediaSession(token,
|
panel.getMediaPlayer().setMediaSession(token,
|
||||||
iconDrawable,
|
iconDrawable,
|
||||||
|
notif.getLargeIcon(),
|
||||||
tintColor,
|
tintColor,
|
||||||
mBackgroundColor,
|
mBackgroundColor,
|
||||||
mActions,
|
mActions,
|
||||||
@@ -201,6 +202,7 @@ public class NotificationMediaTemplateViewWrapper extends NotificationTemplateVi
|
|||||||
com.android.systemui.R.id.quick_settings_panel);
|
com.android.systemui.R.id.quick_settings_panel);
|
||||||
bigPanel.addMediaSession(token,
|
bigPanel.addMediaSession(token,
|
||||||
iconDrawable,
|
iconDrawable,
|
||||||
|
notif.getLargeIcon(),
|
||||||
tintColor,
|
tintColor,
|
||||||
mBackgroundColor,
|
mBackgroundColor,
|
||||||
mActions,
|
mActions,
|
||||||
|
|||||||
Reference in New Issue
Block a user