Introducing Notification.MediaStyle.
Allows you to create a nice music player with all the
trimmings, including:
* up to 5 actions in the bigContentView, styled as
transport control buttons
* up to 2 of those actions in the 1U (!)
* a nice custom progress bar
* a tinted background (taken from your Notification.color)
* a special place to put your MediaSessionToken
Bug: 15147533
Change-Id: Ic20a2b369eb6c5fe4853987a44ffd9bace720c7f
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 4524822..dfd927f 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -23,6 +23,7 @@
import android.graphics.Bitmap;
import android.graphics.PorterDuff;
import android.media.AudioManager;
+import android.media.session.MediaSessionToken;
import android.net.Uri;
import android.os.BadParcelableException;
import android.os.Build;
@@ -686,6 +687,11 @@
* notifications, each of which was supplied to {@link InboxStyle#addLine(CharSequence)}.
*/
public static final String EXTRA_TEXT_LINES = "android.textLines";
+
+ /**
+ * {@link #extras} key: A string representing the name of the specific
+ * {@link android.app.Notification.Style} used to create this notification.
+ */
public static final String EXTRA_TEMPLATE = "android.template";
/**
@@ -726,6 +732,13 @@
public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
/**
+ * {@link #extras} key: A
+ * {@link android.media.session.MediaSessionToken} associated with a
+ * {@link android.app.Notification.MediaStyle} notification.
+ */
+ public static final String EXTRA_MEDIA_SESSION = "android.mediaSession";
+
+ /**
* Value for {@link #EXTRA_AS_HEADS_UP}.
* @hide
*/
@@ -2298,14 +2311,12 @@
int N = mActions.size();
if (N > 0) {
- // Log.d("Notification", "has actions: " + mContentText);
big.setViewVisibility(R.id.actions, View.VISIBLE);
big.setViewVisibility(R.id.action_divider, View.VISIBLE);
if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
big.removeAllViews(R.id.actions);
for (int i=0; i<N; i++) {
final RemoteViews button = generateActionButton(mActions.get(i));
- //Log.d("Notification", "adding action " + i + ": " + mActions.get(i).title);
big.addView(R.id.actions, button);
}
}
@@ -3014,4 +3025,142 @@
return wip;
}
}
+
+ /**
+ * Notification style for media playback notifications.
+ *
+ * In the expanded form, {@link Notification#bigContentView}, up to 5
+ * {@link Notification.Action}s specified with
+ * {@link Notification.Builder#addAction(int, CharSequence, PendingIntent) addAction} will be
+ * shown as icon-only pushbuttons, suitable for transport controls. The Bitmap given to
+ * {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap) setLargeIcon()} will be
+ * treated as album artwork.
+ *
+ * Unlike the other styles provided here, MediaStyle can also modify the standard-size
+ * {@link Notification#contentView}; by providing action indices to
+ * {@link #setShowActionsInCompactView(int...)} you can promote up to 2 actions to be displayed
+ * in the standard view alongside the usual content.
+ *
+ * Finally, if you attach a {@link android.media.session.MediaSessionToken} using
+ * {@link android.app.Notification.MediaStyle#setMediaSession(MediaSessionToken)},
+ * the System UI can identify this as a notification representing an active media session
+ * and respond accordingly (by showing album artwork in the lockscreen, for example).
+ *
+ * To use this style with your Notification, feed it to
+ * {@link Notification.Builder#setStyle(android.app.Notification.Style)} like so:
+ * <pre class="prettyprint">
+ * Notification noti = new Notification.Builder()
+ * .setSmallIcon(R.drawable.ic_stat_player)
+ * .setContentTitle("Track title") // these three lines are optional
+ * .setContentText("Artist - Album") // if you use
+ * .setLargeIcon(albumArtBitmap)) // setMediaSession(token, true)
+ * .setMediaSession(mySession, true)
+ * .setStyle(<b>new Notification.MediaStyle()</b>)
+ * .build();
+ * </pre>
+ *
+ * @see Notification#bigContentView
+ */
+ public static class MediaStyle extends Style {
+ static final int MAX_MEDIA_BUTTONS_IN_COMPACT = 2;
+ static final int MAX_MEDIA_BUTTONS = 5;
+
+ private int[] mActionsToShowInCompact = null;
+ private MediaSessionToken mToken;
+
+ public MediaStyle() {
+ }
+
+ public MediaStyle(Builder builder) {
+ setBuilder(builder);
+ }
+
+ /**
+ * Request up to 2 actions (by index in the order of addition) to be shown in the compact
+ * notification view.
+ */
+ public MediaStyle setShowActionsInCompactView(int...actions) {
+ mActionsToShowInCompact = actions;
+ return this;
+ }
+
+ /**
+ * Attach a {@link android.media.session.MediaSessionToken} to this Notification to provide
+ * additional playback information and control to the SystemUI.
+ */
+ public MediaStyle setMediaSession(MediaSessionToken token) {
+ mToken = token;
+ return this;
+ }
+
+ @Override
+ public Notification buildStyled(Notification wip) {
+ wip.contentView = makeMediaContentView();
+ wip.bigContentView = makeMediaBigContentView();
+
+ return wip;
+ }
+
+ /** @hide */
+ @Override
+ public void addExtras(Bundle extras) {
+ super.addExtras(extras);
+
+ if (mToken != null) {
+ extras.putParcelable(EXTRA_MEDIA_SESSION, mToken);
+ }
+ }
+
+ private RemoteViews generateMediaActionButton(Action action) {
+ final boolean tombstone = (action.actionIntent == null);
+ RemoteViews button = new RemoteViews(mBuilder.mContext.getPackageName(),
+ R.layout.notification_quantum_media_action);
+ button.setImageViewResource(R.id.action0, action.icon);
+ if (!tombstone) {
+ button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
+ }
+ button.setContentDescription(R.id.action0, action.title);
+ return button;
+ }
+
+ private RemoteViews makeMediaContentView() {
+ RemoteViews view = mBuilder.applyStandardTemplate(
+ R.layout.notification_template_quantum_media, true /* 1U */);
+
+ final int numActions = mBuilder.mActions.size();
+ final int N = mActionsToShowInCompact == null
+ ? 0
+ : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
+ if (N > 0) {
+ view.removeAllViews(R.id.actions);
+ for (int i = 0; i < N; i++) {
+ if (i >= numActions) {
+ throw new IllegalArgumentException(String.format(
+ "setShowActionsInCompactView: action %d out of bounds (max %d)",
+ i, numActions - 1));
+ }
+
+ final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]);
+ final RemoteViews button = generateMediaActionButton(action);
+ view.addView(R.id.actions, button);
+ }
+ }
+ return view;
+ }
+
+ private RemoteViews makeMediaBigContentView() {
+ RemoteViews big = mBuilder.applyStandardTemplate(
+ R.layout.notification_template_quantum_big_media, false);
+
+ final int N = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS);
+ if (N > 0) {
+ big.removeAllViews(R.id.actions);
+ for (int i=0; i<N; i++) {
+ final RemoteViews button = generateMediaActionButton(mBuilder.mActions.get(i));
+ big.addView(R.id.actions, button);
+ }
+ }
+ return big;
+ }
+ }
}