diff --git a/packages/SystemUI/res/layout/qs_media_panel_options.xml b/packages/SystemUI/res/layout/qs_media_panel_options.xml
new file mode 100644
index 0000000000000..0669357910702
--- /dev/null
+++ b/packages/SystemUI/res/layout/qs_media_panel_options.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index f7b92b564dac1..1a05ec1fd080e 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1179,4 +1179,5 @@
150dp
350dp
8dp
+ 10dp
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
index 1a4c327b44054..f7e4c794836e7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
@@ -18,8 +18,11 @@ package com.android.systemui.qs;
import android.app.Notification;
import android.app.PendingIntent;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.content.res.ColorStateList;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -35,6 +38,7 @@ import android.media.session.MediaSession;
import android.media.session.PlaybackState;
import android.text.TextUtils;
import android.util.Log;
+import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -54,6 +58,8 @@ import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.plugins.ActivityStarter;
+import java.util.List;
+
/**
* Single media player for carousel in QSPanel
*/
@@ -70,6 +76,83 @@ public class QSMediaPlayer {
private int mHeight;
private int mForegroundColor;
private int mBackgroundColor;
+ private ComponentName mRecvComponent;
+ private QSPanel mParent;
+
+ private MediaController.Callback mSessionCallback = new MediaController.Callback() {
+ @Override
+ public void onSessionDestroyed() {
+ Log.d(TAG, "session destroyed");
+ mController.unregisterCallback(mSessionCallback);
+
+ // Hide all the old buttons
+ final int[] actionIds = {
+ R.id.action0,
+ R.id.action1,
+ R.id.action2,
+ R.id.action3,
+ R.id.action4
+ };
+ for (int i = 0; i < actionIds.length; i++) {
+ ImageButton thisBtn = mMediaNotifView.findViewById(actionIds[i]);
+ if (thisBtn != null) {
+ thisBtn.setVisibility(View.GONE);
+ }
+ }
+
+ // Add a restart button
+ ImageButton btn = mMediaNotifView.findViewById(actionIds[0]);
+ btn.setOnClickListener(v -> {
+ Log.d(TAG, "Attempting to restart session");
+ // Send a media button event to previously found receiver
+ if (mRecvComponent != null) {
+ Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
+ intent.setComponent(mRecvComponent);
+ int keyCode = KeyEvent.KEYCODE_MEDIA_PLAY;
+ intent.putExtra(
+ Intent.EXTRA_KEY_EVENT,
+ new KeyEvent(KeyEvent.ACTION_DOWN, keyCode));
+ mContext.sendBroadcast(intent);
+ } else {
+ Log.d(TAG, "No receiver to restart");
+ // If we don't have a receiver, try relaunching the activity instead
+ try {
+ mController.getSessionActivity().send();
+ } catch (PendingIntent.CanceledException e) {
+ Log.e(TAG, "Pending intent was canceled");
+ e.printStackTrace();
+ }
+ }
+ });
+ btn.setImageDrawable(mContext.getResources().getDrawable(R.drawable.lb_ic_replay));
+ btn.setImageTintList(ColorStateList.valueOf(mForegroundColor));
+ btn.setVisibility(View.VISIBLE);
+
+ // Add long-click option to remove the player
+ ViewGroup mMediaCarousel = (ViewGroup) mMediaNotifView.getParent();
+ mMediaNotifView.setOnLongClickListener(v -> {
+ // Replace player view with delete/cancel view
+ v.setVisibility(View.GONE);
+
+ View options = LayoutInflater.from(mContext).inflate(
+ R.layout.qs_media_panel_options, null, false);
+ ImageButton btnDelete = options.findViewById(R.id.remove);
+ btnDelete.setOnClickListener(b -> {
+ mMediaCarousel.removeView(options);
+ mParent.removeMediaPlayer(QSMediaPlayer.this);
+ });
+ ImageButton btnCancel = options.findViewById(R.id.cancel);
+ btnCancel.setOnClickListener(b -> {
+ mMediaCarousel.removeView(options);
+ v.setVisibility(View.VISIBLE);
+ });
+
+ int pos = mMediaCarousel.indexOfChild(v);
+ mMediaCarousel.addView(options, pos, v.getLayoutParams());
+ return true; // consumed click
+ });
+ }
+ };
/**
*
@@ -92,7 +175,8 @@ public class QSMediaPlayer {
}
/**
- *
+ * Create or update the player view for the given media session
+ * @param parent the parent QSPanel
* @param token token for this media session
* @param icon app notification icon
* @param iconColor foreground color (for text, icons)
@@ -101,13 +185,30 @@ public class QSMediaPlayer {
* @param notif reference to original notification
* @param device current playback device
*/
- public void setMediaSession(MediaSession.Token token, Icon icon, int iconColor, int bgColor,
- View actionsContainer, Notification notif, MediaDevice device) {
- Log.d(TAG, "got media session: " + token);
+ public void setMediaSession(QSPanel parent, MediaSession.Token token, Icon icon, int iconColor,
+ int bgColor, View actionsContainer, Notification notif, MediaDevice device) {
+ mParent = parent;
mToken = token;
mForegroundColor = iconColor;
mBackgroundColor = bgColor;
mController = new MediaController(mContext, token);
+
+ // Try to find a receiver for the media button that matches this app
+ PackageManager pm = mContext.getPackageManager();
+ Intent it = new Intent(Intent.ACTION_MEDIA_BUTTON);
+ List info = pm.queryBroadcastReceiversAsUser(it, 0, mContext.getUser());
+ if (info != null) {
+ for (ResolveInfo inf : info) {
+ if (inf.activityInfo.packageName.equals(notif.contentIntent.getCreatorPackage())) {
+ Log.d(TAG, "Found receiver for package: " + inf);
+ mRecvComponent = inf.getComponentInfo().getComponentName();
+ }
+ }
+ }
+
+ // reset in case we had previously restarted the stream
+ mMediaNotifView.setOnLongClickListener(null);
+ mController.registerCallback(mSessionCallback);
MediaMetadata mMediaMetadata = mController.getMetadata();
if (mMediaMetadata == null) {
Log.e(TAG, "Media metadata was null");
@@ -235,7 +336,6 @@ public class QSMediaPlayer {
for (; i < actionIds.length; i++) {
ImageButton thisBtn = mMediaNotifView.findViewById(actionIds[i]);
thisBtn.setVisibility(View.GONE);
- Log.d(TAG, "hid a button");
}
}
@@ -266,8 +366,9 @@ public class QSMediaPlayer {
private void addAlbumArtBackground(MediaMetadata metadata, int bgColor, int width, int height) {
Bitmap albumArt = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
+ float radius = mContext.getResources().getDimension(R.dimen.qs_media_corner_radius);
if (albumArt != null) {
-
+ Log.d(TAG, "updating album art");
Bitmap original = albumArt.copy(Bitmap.Config.ARGB_8888, true);
Bitmap scaled = scaleBitmap(original, width, height);
Canvas canvas = new Canvas(scaled);
@@ -281,12 +382,15 @@ public class QSMediaPlayer {
RoundedBitmapDrawable roundedDrawable = RoundedBitmapDrawableFactory.create(
mContext.getResources(), scaled);
- roundedDrawable.setCornerRadius(20);
+ roundedDrawable.setCornerRadius(radius);
mMediaNotifView.setBackground(roundedDrawable);
} else {
Log.e(TAG, "No album art available");
- mMediaNotifView.setBackground(null);
+ GradientDrawable rect = new GradientDrawable();
+ rect.setCornerRadius(radius);
+ rect.setColor(bgColor);
+ mMediaNotifView.setBackground(rect);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 5e98f93564036..51e352b30019b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -284,7 +284,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
}
Log.d(TAG, "setting player session");
- player.setMediaSession(token, icon, iconColor, bgColor, actionsContainer,
+ player.setMediaSession(this, token, icon, iconColor, bgColor, actionsContainer,
notif.getNotification(), mDevice);
if (mMediaPlayers.size() > 0) {
@@ -303,6 +303,27 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
return mMediaCarousel;
}
+ /**
+ * Remove the media player from the carousel
+ * @param player Player to remove
+ * @return true if removed, false if player was not found
+ */
+ protected boolean removeMediaPlayer(QSMediaPlayer player) {
+ // Remove from list
+ if (!mMediaPlayers.remove(player)) {
+ return false;
+ }
+
+ // Check if we need to collapse the carousel now
+ mMediaCarousel.removeView(player.getView());
+ if (mMediaPlayers.size() == 0) {
+ ((View) mMediaCarousel.getParent()).setVisibility(View.GONE);
+ mLocalMediaManager.stopScan();
+ mLocalMediaManager.unregisterCallback(mDeviceCallback);
+ }
+ return true;
+ }
+
protected void addDivider() {
mDivider = LayoutInflater.from(mContext).inflate(R.layout.qs_divider, this, false);
mDivider.setBackgroundColor(Utils.applyAlpha(mDivider.getAlpha(),
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
index d7b8b83f01fba..5bb882e2355f9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
@@ -16,19 +16,27 @@
package com.android.systemui.qs;
+import android.app.PendingIntent;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
+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.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.Icon;
import android.media.MediaMetadata;
import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.media.session.PlaybackState;
import android.util.Log;
+import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -42,6 +50,8 @@ import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
import com.android.systemui.R;
+import java.util.List;
+
/**
* QQS mini media player
*/
@@ -53,6 +63,54 @@ public class QuickQSMediaPlayer {
private LinearLayout mMediaNotifView;
private MediaSession.Token mToken;
private MediaController mController;
+ private int mBackgroundColor;
+ private int mForegroundColor;
+ private ComponentName mRecvComponent;
+
+ private MediaController.Callback mSessionCallback = new MediaController.Callback() {
+ @Override
+ public void onSessionDestroyed() {
+ Log.d(TAG, "session destroyed");
+ mController.unregisterCallback(mSessionCallback);
+
+ // Hide all the old buttons
+ final int[] actionIds = {R.id.action0, R.id.action1, R.id.action2};
+ for (int i = 0; i < actionIds.length; i++) {
+ ImageButton thisBtn = mMediaNotifView.findViewById(actionIds[i]);
+ if (thisBtn != null) {
+ thisBtn.setVisibility(View.GONE);
+ }
+ }
+
+ // Add a restart button
+ ImageButton btn = mMediaNotifView.findViewById(actionIds[0]);
+ btn.setOnClickListener(v -> {
+ Log.d(TAG, "Attempting to restart session");
+ // Send a media button event to previously found receiver
+ if (mRecvComponent != null) {
+ Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
+ intent.setComponent(mRecvComponent);
+ int keyCode = KeyEvent.KEYCODE_MEDIA_PLAY;
+ intent.putExtra(
+ Intent.EXTRA_KEY_EVENT,
+ new KeyEvent(KeyEvent.ACTION_DOWN, keyCode));
+ mContext.sendBroadcast(intent);
+ } else {
+ Log.d(TAG, "No receiver to restart");
+ // If we don't have a receiver, try relaunching the activity instead
+ try {
+ mController.getSessionActivity().send();
+ } catch (PendingIntent.CanceledException e) {
+ Log.e(TAG, "Pending intent was canceled");
+ e.printStackTrace();
+ }
+ }
+ });
+ btn.setImageDrawable(mContext.getResources().getDrawable(R.drawable.lb_ic_replay));
+ btn.setImageTintList(ColorStateList.valueOf(mForegroundColor));
+ btn.setVisibility(View.VISIBLE);
+ }
+ };
/**
*
@@ -83,34 +141,50 @@ public class QuickQSMediaPlayer {
View actionsContainer, int[] actionsToShow) {
Log.d(TAG, "Setting media session: " + token);
mToken = token;
+ mForegroundColor = iconColor;
+ mBackgroundColor = bgColor;
mController = new MediaController(mContext, token);
MediaMetadata mMediaMetadata = mController.getMetadata();
+ // Try to find a receiver for the media button that matches this app
+ PackageManager pm = mContext.getPackageManager();
+ Intent it = new Intent(Intent.ACTION_MEDIA_BUTTON);
+ List info = pm.queryBroadcastReceiversAsUser(it, 0, mContext.getUser());
+ if (info != null) {
+ for (ResolveInfo inf : info) {
+ if (inf.activityInfo.packageName.equals(mController.getPackageName())) {
+ Log.d(TAG, "Found receiver for package: " + inf);
+ mRecvComponent = inf.getComponentInfo().getComponentName();
+ }
+ }
+ }
+ mController.registerCallback(mSessionCallback);
+
if (mMediaMetadata == null) {
Log.e(TAG, "Media metadata was null");
return;
}
// Album art
- addAlbumArtBackground(mMediaMetadata, bgColor);
+ addAlbumArtBackground(mMediaMetadata, mBackgroundColor);
// App icon
ImageView appIcon = mMediaNotifView.findViewById(R.id.icon);
Drawable iconDrawable = icon.loadDrawable(mContext);
- iconDrawable.setTint(iconColor);
+ iconDrawable.setTint(mForegroundColor);
appIcon.setImageDrawable(iconDrawable);
// Artist name
TextView appText = mMediaNotifView.findViewById(R.id.header_title);
String artistName = mMediaMetadata.getString(MediaMetadata.METADATA_KEY_ARTIST);
appText.setText(artistName);
- appText.setTextColor(iconColor);
+ appText.setTextColor(mForegroundColor);
// Song name
TextView titleText = mMediaNotifView.findViewById(R.id.header_text);
String songName = mMediaMetadata.getString(MediaMetadata.METADATA_KEY_TITLE);
titleText.setText(songName);
- titleText.setTextColor(iconColor);
+ titleText.setTextColor(mForegroundColor);
// Buttons we can display
final int[] actionIds = {R.id.action0, R.id.action1, R.id.action2};
@@ -178,6 +252,7 @@ public class QuickQSMediaPlayer {
private void addAlbumArtBackground(MediaMetadata metadata, int bgColor) {
Bitmap albumArt = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
+ float radius = mContext.getResources().getDimension(R.dimen.qs_media_corner_radius);
if (albumArt != null) {
Rect bounds = new Rect();
mMediaNotifView.getBoundsOnScreen(bounds);
@@ -197,12 +272,15 @@ public class QuickQSMediaPlayer {
RoundedBitmapDrawable roundedDrawable = RoundedBitmapDrawableFactory.create(
mContext.getResources(), scaled);
- roundedDrawable.setCornerRadius(20);
+ roundedDrawable.setCornerRadius(radius);
mMediaNotifView.setBackground(roundedDrawable);
} else {
Log.e(TAG, "No album art available");
- mMediaNotifView.setBackground(null);
+ GradientDrawable rect = new GradientDrawable();
+ rect.setCornerRadius(radius);
+ rect.setColor(bgColor);
+ mMediaNotifView.setBackground(rect);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
index 2a4b315b0aa62..352ba0f75a32a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
@@ -179,21 +179,22 @@ public class NotificationMediaTemplateViewWrapper extends NotificationTemplateVi
if (Utils.useQsMediaPlayer(mContext)) {
final int[] compactActions = mRow.getEntry().getSbn().getNotification().extras
.getIntArray(Notification.EXTRA_COMPACT_ACTIONS);
+ int tintColor = getNotificationHeader().getOriginalIconColor();
StatusBarWindowController ctrl = Dependency.get(StatusBarWindowController.class);
QuickQSPanel panel = ctrl.getStatusBarView().findViewById(
com.android.systemui.R.id.quick_qs_panel);
panel.getMediaPlayer().setMediaSession(token,
mRow.getEntry().getSbn().getNotification().getSmallIcon(),
- getNotificationHeader().getOriginalIconColor(),
- mRow.getCurrentBackgroundTint(),
+ tintColor,
+ mBackgroundColor,
mActions,
compactActions);
QSPanel bigPanel = ctrl.getStatusBarView().findViewById(
com.android.systemui.R.id.quick_settings_panel);
bigPanel.addMediaSession(token,
mRow.getEntry().getSbn().getNotification().getSmallIcon(),
- getNotificationHeader().getOriginalIconColor(),
- mRow.getCurrentBackgroundTint(),
+ tintColor,
+ mBackgroundColor,
mActions,
mRow.getEntry().getSbn());
}