Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1110)

Unified Diff: chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java

Issue 1847063005: [Media, UI] Change MediaNotification style to MediaStyle (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fix tests and rebase Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java b/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java
index 10a9ba724baca9db7893b47957417eaeb18384da..73b3e03b1a39f5bb1c8ed210c67bc44473a9cae5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java
@@ -18,11 +18,11 @@ import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.os.Build;
import android.os.IBinder;
-import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationManagerCompat;
import android.support.v4.media.MediaMetadataCompat;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.PlaybackStateCompat;
+import android.support.v7.app.NotificationCompat;
import android.support.v7.media.MediaRouter;
import android.text.TextUtils;
import android.util.SparseArray;
@@ -33,6 +33,7 @@ import android.widget.RemoteViews;
import org.chromium.base.ApiCompatibilityUtils;
import org.chromium.base.VisibleForTesting;
import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeFeatureList;
import javax.annotation.Nullable;
@@ -43,9 +44,11 @@ import javax.annotation.Nullable;
* There's one service started for a distinct notification id.
*/
public class MediaNotificationManager {
-
private static final String TAG = "MediaNotification";
+ private static final int WEARABLE_NOTIFICATION_BACKGROUND_WIDTH = 400;
+ private static final int WEARABLE_NOTIFICATION_BACKGROUND_HEIGHT = 400;
mlamouri (slow - plz ping) 2016/04/07 19:47:34 Where did you find these numbers?
Zhiqiang Zhang (Slow) 2016/04/11 09:26:28 http://developer.android.com/training/wearables/no
+
// We're always used on the UI thread but the LOCK is required by lint when creating the
// singleton.
private static final Object LOCK = new Object();
@@ -480,55 +483,6 @@ public class MediaNotificationManager {
clearNotification();
}
- private RemoteViews createContentView() {
- RemoteViews contentView =
- new RemoteViews(mContext.getPackageName(), R.layout.playback_notification_bar);
-
- // By default, play/pause button is the only one.
- int playPauseButtonId = R.id.button1;
- // On Android pre-L, dismissing the notification when the service is no longer in foreground
- // doesn't work. Instead, a STOP button is shown.
- if (mMediaNotificationInfo.supportsSwipeAway()
- && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP
- || mMediaNotificationInfo.supportsStop()) {
- contentView.setOnClickPendingIntent(R.id.button1,
- createPendingIntent(ListenerService.ACTION_STOP));
- contentView.setContentDescription(R.id.button1, mStopDescription);
-
- // If the play/pause needs to be shown, it moves over to the second button from the end.
- playPauseButtonId = R.id.button2;
- }
-
- contentView.setTextViewText(R.id.title, mMediaNotificationInfo.metadata.getTitle());
- contentView.setTextViewText(R.id.status, mMediaNotificationInfo.origin);
- if (mNotificationIcon != null) {
- contentView.setImageViewBitmap(R.id.icon, mNotificationIcon);
- } else {
- contentView.setImageViewResource(R.id.icon, mMediaNotificationInfo.icon);
- }
-
- if (mMediaNotificationInfo.supportsPlayPause()) {
- if (mMediaNotificationInfo.isPaused) {
- contentView.setImageViewResource(playPauseButtonId, R.drawable.ic_vidcontrol_play);
- contentView.setContentDescription(playPauseButtonId, mPlayDescription);
- contentView.setOnClickPendingIntent(playPauseButtonId,
- createPendingIntent(ListenerService.ACTION_PLAY));
- } else {
- // If we're here, the notification supports play/pause button and is playing.
- contentView.setImageViewResource(playPauseButtonId, R.drawable.ic_vidcontrol_pause);
- contentView.setContentDescription(playPauseButtonId, mPauseDescription);
- contentView.setOnClickPendingIntent(playPauseButtonId,
- createPendingIntent(ListenerService.ACTION_PAUSE));
- }
-
- contentView.setViewVisibility(playPauseButtonId, View.VISIBLE);
- } else {
- contentView.setViewVisibility(playPauseButtonId, View.GONE);
- }
-
- return contentView;
- }
-
private MediaMetadataCompat createMetadata() {
MediaMetadataCompat.Builder metadataBuilder = new MediaMetadataCompat.Builder();
@@ -541,8 +495,13 @@ public class MediaNotificationManager {
mMediaNotificationInfo.metadata.getTitle());
metadataBuilder.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE,
mMediaNotificationInfo.origin);
- metadataBuilder.putBitmap(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON,
- mediaSessionImage);
+ if (mMediaNotificationInfo.largeIcon == null) {
+ metadataBuilder.putBitmap(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON,
+ mediaSessionImage);
+ } else {
+ metadataBuilder.putBitmap(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON,
+ scaleBitmapForWearable(mMediaNotificationInfo.largeIcon));
+ }
// METADATA_KEY_ART is optional and should only be used if we can provide something
// better than the default image.
mlamouri (slow - plz ping) 2016/04/07 19:47:34 Should we try to use METADATA_KEY_ART with the lar
Zhiqiang Zhang (Slow) 2016/04/11 09:26:28 Done. Also I merged MediaNotificationInfo.image an
if (mMediaNotificationInfo.image != null) {
@@ -554,7 +513,12 @@ public class MediaNotificationManager {
mMediaNotificationInfo.metadata.getTitle());
metadataBuilder.putString(MediaMetadataCompat.METADATA_KEY_ARTIST,
mMediaNotificationInfo.origin);
- metadataBuilder.putBitmap(MediaMetadataCompat.METADATA_KEY_ART, mediaSessionImage);
+ if (mMediaNotificationInfo.largeIcon == null) {
+ metadataBuilder.putBitmap(MediaMetadataCompat.METADATA_KEY_ART, mediaSessionImage);
+ } else {
+ metadataBuilder.putBitmap(MediaMetadataCompat.METADATA_KEY_ART,
+ mMediaNotificationInfo.largeIcon);
+ }
}
if (!TextUtils.isEmpty(mMediaNotificationInfo.metadata.getArtist())) {
@@ -574,21 +538,19 @@ public class MediaNotificationManager {
if (mMediaNotificationInfo == null) return;
- // Android doesn't badge the icons for RemoteViews automatically when
- // running the app under the Work profile.
- if (mNotificationIcon == null) {
- Drawable notificationIconDrawable = ApiCompatibilityUtils.getUserBadgedIcon(
- mContext, mMediaNotificationInfo.icon);
- mNotificationIcon = drawableToBitmap(notificationIconDrawable);
- }
+ updateMediaSession();
- if (mNotificationBuilder == null) {
- mNotificationBuilder = new NotificationCompat.Builder(mContext)
- .setSmallIcon(mMediaNotificationInfo.icon)
- .setAutoCancel(false)
- .setLocalOnly(true)
- .setDeleteIntent(createPendingIntent(ListenerService.ACTION_STOP));
+ mNotificationBuilder = new NotificationCompat.Builder(mContext);
+ if (ChromeFeatureList.isEnabled(ChromeFeatureList.USE_NEW_MEDIA_NOTIFICATION)) {
+ setMediaStyleLayoutForNotificationBuilder(mNotificationBuilder);
+ } else {
+ setCustomLayoutForNotificationBuilder(mNotificationBuilder);
}
+ // TODO: smallIcon set to Chrome's icon?
+ mNotificationBuilder.setSmallIcon(mMediaNotificationInfo.icon);
+ mNotificationBuilder.setAutoCancel(false);
+ mNotificationBuilder.setLocalOnly(true);
+ mNotificationBuilder.setDeleteIntent(createPendingIntent(ListenerService.ACTION_STOP));
if (mMediaNotificationInfo.supportsSwipeAway()) {
mNotificationBuilder.setOngoing(!mMediaNotificationInfo.isPaused);
@@ -602,39 +564,10 @@ public class MediaNotificationManager {
mMediaNotificationInfo.contentIntent, 0));
}
- mNotificationBuilder.setContent(createContentView());
mNotificationBuilder.setVisibility(
mMediaNotificationInfo.isPrivate ? NotificationCompat.VISIBILITY_PRIVATE
: NotificationCompat.VISIBILITY_PUBLIC);
-
- if (mMediaNotificationInfo.supportsPlayPause()) {
-
- if (mMediaSession == null) mMediaSession = createMediaSession();
- try {
- // Tell the MediaRouter about the session, so that Chrome can control the volume
- // on the remote cast device (if any).
- // Pre-MR1 versions of JB do not have the complete MediaRouter APIs,
- // so getting the MediaRouter instance will throw an exception.
- MediaRouter.getInstance(mContext).setMediaSessionCompat(mMediaSession);
- } catch (NoSuchMethodError e) {
- // Do nothing. Chrome can't be casting without a MediaRouter, so there is nothing
- // to do here.
- }
- mMediaSession.setMetadata(createMetadata());
-
- PlaybackStateCompat.Builder playbackStateBuilder = new PlaybackStateCompat.Builder()
- .setActions(PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompat.ACTION_PAUSE);
- if (mMediaNotificationInfo.isPaused) {
- playbackStateBuilder.setState(PlaybackStateCompat.STATE_PAUSED,
- PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN, 1.0f);
- } else {
- playbackStateBuilder.setState(PlaybackStateCompat.STATE_PLAYING,
- PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN, 1.0f);
- }
- mMediaSession.setPlaybackState(playbackStateBuilder.build());
- }
-
Notification notification = mNotificationBuilder.build();
// We keep the service as a foreground service while the media is playing. When it is not,
@@ -651,6 +584,37 @@ public class MediaNotificationManager {
}
}
+ private void updateMediaSession() {
+ if (!mMediaNotificationInfo.supportsPlayPause()) return;
+
+ if (mMediaSession == null) mMediaSession = createMediaSession();
+
+ try {
+ // Tell the MediaRouter about the session, so that Chrome can control the volume
+ // on the remote cast device (if any).
+ // Pre-MR1 versions of JB do not have the complete MediaRouter APIs,
+ // so getting the MediaRouter instance will throw an exception.
+ MediaRouter.getInstance(mContext).setMediaSessionCompat(mMediaSession);
+ } catch (NoSuchMethodError e) {
+ // Do nothing. Chrome can't be casting without a MediaRouter, so there is nothing
+ // to do here.
+ }
+
+ mMediaSession.setMetadata(createMetadata());
+
+ PlaybackStateCompat.Builder playbackStateBuilder = new PlaybackStateCompat.Builder()
+ .setActions(PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompat.ACTION_PAUSE);
+ if (mMediaNotificationInfo.isPaused) {
+ playbackStateBuilder.setState(PlaybackStateCompat.STATE_PAUSED,
+ PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN, 1.0f);
+ } else {
+ // If notification only supports stop, still pretend
+ playbackStateBuilder.setState(PlaybackStateCompat.STATE_PLAYING,
+ PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN, 1.0f);
+ }
+ mMediaSession.setPlaybackState(playbackStateBuilder.build());
+ }
+
private MediaSessionCompat createMediaSession() {
MediaSessionCompat mediaSession = new MediaSessionCompat(
mContext,
@@ -678,6 +642,124 @@ public class MediaNotificationManager {
return mediaSession;
}
+ private void setMediaStyleLayoutForNotificationBuilder(NotificationCompat.Builder builder) {
+ NotificationCompat.MediaStyle style = new NotificationCompat.MediaStyle();
+ if (mMediaSession != null) {
mlamouri (slow - plz ping) 2016/04/07 19:47:34 Is such a thing even possible?
Zhiqiang Zhang (Slow) 2016/04/11 09:26:28 Done. Merged all logic when mNotificationInfo.supp
+ style.setMediaSession(mMediaSession.getSessionToken());
+ }
+
+ // TODO: with custom notifications, can do:
+ // - setContentTitle for track title,
+ // - setContetText() for track artist,
+ // - setSubText() for track album
+ // or use subText() for origin and setContentText for artist + album.
mlamouri (slow - plz ping) 2016/04/07 19:47:34 After we ship the new style, we should see how to
Zhiqiang Zhang (Slow) 2016/04/11 09:26:28 Done.
+ builder.setContentTitle(mMediaNotificationInfo.metadata.getTitle());
+ builder.setContentText(mMediaNotificationInfo.origin);
+ // TODO: have our own "Media" icon so we can have large resolution in case of we have no
+ // large icon?
+ if (mMediaNotificationInfo.largeIcon != null) {
+ builder.setLargeIcon(mMediaNotificationInfo.largeIcon);
+ } else {
+ builder.setLargeIcon(mDefaultMediaSessionImage);
+ }
+ builder.setShowWhen(false);
+
+ if (mMediaNotificationInfo.supportsPlayPause()) {
mlamouri (slow - plz ping) 2016/04/07 19:47:34 Add a TODO: if it doesn't supports play/pause, we
Zhiqiang Zhang (Slow) 2016/04/11 09:26:28 The logic is already that. Added comments.
+ if (mMediaNotificationInfo.isPaused) {
+ builder.addAction(R.drawable.ic_vidcontrol_play, mPlayDescription,
+ createPendingIntent(ListenerService.ACTION_PLAY));
+ } else {
+ // If we're here, the notification supports play/pause button and is playing.
+ builder.addAction(R.drawable.ic_vidcontrol_pause, mPauseDescription,
+ createPendingIntent(ListenerService.ACTION_PAUSE));
+ }
+ style.setShowActionsInCompactView(0);
+ }
+
+ if (mMediaNotificationInfo.supportsStop()) {
+ builder.addAction(R.drawable.ic_vidcontrol_stop, mStopDescription,
+ createPendingIntent(ListenerService.ACTION_STOP));
+ }
+ if (mMediaSession != null) {
+ builder.setStyle(style);
+ }
+ }
+
+ private void setCustomLayoutForNotificationBuilder(NotificationCompat.Builder builder) {
+ builder.setContent(createContentView());
+ }
+
+ private RemoteViews createContentView() {
+ RemoteViews contentView =
+ new RemoteViews(mContext.getPackageName(), R.layout.playback_notification_bar);
+
+ // By default, play/pause button is the only one.
+ int playPauseButtonId = R.id.button1;
+ // On Android pre-L, dismissing the notification when the service is no longer in foreground
+ // doesn't work. Instead, a STOP button is shown.
+ if (mMediaNotificationInfo.supportsSwipeAway()
+ && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP
+ || mMediaNotificationInfo.supportsStop()) {
+ contentView.setOnClickPendingIntent(R.id.button1,
+ createPendingIntent(ListenerService.ACTION_STOP));
+ contentView.setContentDescription(R.id.button1, mStopDescription);
+
+ // If the play/pause needs to be shown, it moves over to the second button from the end.
+ playPauseButtonId = R.id.button2;
+ }
+
+ contentView.setTextViewText(R.id.title, mMediaNotificationInfo.metadata.getTitle());
+ contentView.setTextViewText(R.id.status, mMediaNotificationInfo.origin);
+
+ // Android doesn't badge the icons for RemoteViews automatically when
+ // running the app under the Work profile.
+ if (mNotificationIcon == null) {
+ Drawable notificationIconDrawable = ApiCompatibilityUtils.getUserBadgedIcon(
+ mContext, mMediaNotificationInfo.icon);
+ mNotificationIcon = drawableToBitmap(notificationIconDrawable);
+ }
+
+ if (mNotificationIcon != null) {
+ contentView.setImageViewBitmap(R.id.icon, mNotificationIcon);
+ } else {
+ contentView.setImageViewResource(R.id.icon, mMediaNotificationInfo.icon);
+ }
+
+ if (mMediaNotificationInfo.supportsPlayPause()) {
+ if (mMediaNotificationInfo.isPaused) {
+ contentView.setImageViewResource(playPauseButtonId, R.drawable.ic_vidcontrol_play);
+ contentView.setContentDescription(playPauseButtonId, mPlayDescription);
+ contentView.setOnClickPendingIntent(playPauseButtonId,
+ createPendingIntent(ListenerService.ACTION_PLAY));
+ } else {
+ // If we're here, the notification supports play/pause button and is playing.
+ contentView.setImageViewResource(playPauseButtonId, R.drawable.ic_vidcontrol_pause);
+ contentView.setContentDescription(playPauseButtonId, mPauseDescription);
+ contentView.setOnClickPendingIntent(playPauseButtonId,
+ createPendingIntent(ListenerService.ACTION_PAUSE));
+ }
+
+ contentView.setViewVisibility(playPauseButtonId, View.VISIBLE);
+ } else {
+ contentView.setViewVisibility(playPauseButtonId, View.GONE);
+ }
+
+ return contentView;
+ }
+
+ /**
+ * Scales the Bitmap to have better looking on Wearable devices.
+ * The returned Bitmap size will be exactly 400*400.
+ */
+ private Bitmap scaleBitmapForWearable(Bitmap original) {
+ Bitmap result = Bitmap.createScaledBitmap(
+ original,
+ WEARABLE_NOTIFICATION_BACKGROUND_WIDTH,
+ WEARABLE_NOTIFICATION_BACKGROUND_HEIGHT,
+ true);
+ return result;
+ }
+
private Bitmap drawableToBitmap(Drawable drawable) {
if (!(drawable instanceof BitmapDrawable)) return null;

Powered by Google App Engine
This is Rietveld 408576698