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

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: fixed nits 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 b36cc9faffc5b28a81de23e6c366eeab18b7ab75..643cc79de0fc1bbd4aa44d8815e1195de3e30266 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,13 @@ import javax.annotation.Nullable;
* There's one service started for a distinct notification id.
*/
public class MediaNotificationManager {
-
private static final String TAG = "MediaNotification";
+ // The background notification size on Android Wear. See:
+ // http://developer.android.com/training/wearables/notifications/creating.html
+ private static final int WEARABLE_NOTIFICATION_BACKGROUND_WIDTH = 400;
+ private static final int WEARABLE_NOTIFICATION_BACKGROUND_HEIGHT = 400;
+
// 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();
@@ -482,61 +487,13 @@ 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();
// Choose the image to use as the icon.
- Bitmap mediaSessionImage = mMediaNotificationInfo.image == null ? mDefaultMediaSessionImage
- : mMediaNotificationInfo.image;
+ Bitmap mediaSessionImage = mMediaNotificationInfo.largeIcon == null
+ ? mDefaultMediaSessionImage
+ : mMediaNotificationInfo.largeIcon;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
metadataBuilder.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE,
@@ -544,12 +501,12 @@ public class MediaNotificationManager {
metadataBuilder.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE,
mMediaNotificationInfo.origin);
metadataBuilder.putBitmap(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON,
- mediaSessionImage);
+ scaleBitmapForWearable(mediaSessionImage));
// METADATA_KEY_ART is optional and should only be used if we can provide something
// better than the default image.
- if (mMediaNotificationInfo.image != null) {
- metadataBuilder.putBitmap(MediaMetadataCompat.METADATA_KEY_ART,
- mMediaNotificationInfo.image);
+ if (mMediaNotificationInfo.largeIcon != null) {
+ metadataBuilder.putBitmap(
+ MediaMetadataCompat.METADATA_KEY_ART, mMediaNotificationInfo.largeIcon);
}
} else {
metadataBuilder.putString(MediaMetadataCompat.METADATA_KEY_TITLE,
@@ -576,21 +533,17 @@ 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.MEDIA_STYLE_NOTIFICATION)) {
+ setMediaStyleLayoutForNotificationBuilder(mNotificationBuilder);
+ } else {
+ setCustomLayoutForNotificationBuilder(mNotificationBuilder);
}
+ mNotificationBuilder.setSmallIcon(mMediaNotificationInfo.icon);
+ mNotificationBuilder.setAutoCancel(false);
+ mNotificationBuilder.setLocalOnly(true);
if (mMediaNotificationInfo.supportsSwipeAway()) {
mNotificationBuilder.setOngoing(!mMediaNotificationInfo.isPaused);
@@ -604,39 +557,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,
@@ -653,6 +577,38 @@ 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,
@@ -680,6 +636,124 @@ public class MediaNotificationManager {
return mediaSession;
}
+ private void setMediaStyleLayoutForNotificationBuilder(NotificationCompat.Builder builder) {
+ // TODO(zqzhang): After we ship the new style, we should see how to present the
+ // metadata.artist and metadata.album. See http://crbug.com/599937
+ builder.setContentTitle(mMediaNotificationInfo.metadata.getTitle());
+ builder.setContentText(mMediaNotificationInfo.origin);
+ // TODO(zqzhang): Update the default icon when a new one in provided.
+ // See http://crbug.com/600396.
+ if (mMediaNotificationInfo.largeIcon != null) {
+ builder.setLargeIcon(mMediaNotificationInfo.largeIcon);
+ } else {
+ builder.setLargeIcon(mDefaultMediaSessionImage);
+ }
+ // TODO(zqzhang): It's weird that setShowWhen() don't work on K. Calling setWhen() to force
+ // removing the time.
+ builder.setShowWhen(false).setWhen(0);
+
+ // Only apply MediaStyle when NotificationInfo supports play/pause.
+ if (mMediaNotificationInfo.supportsPlayPause()) {
+ NotificationCompat.MediaStyle style = new NotificationCompat.MediaStyle();
+ style.setMediaSession(mMediaSession.getSessionToken());
+
+ 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);
+ style.setCancelButtonIntent(createPendingIntent(ListenerService.ACTION_STOP));
+ style.setShowCancelButton(true);
+ builder.setStyle(style);
+ }
+
+ if (mMediaNotificationInfo.supportsStop()) {
+ builder.addAction(R.drawable.ic_vidcontrol_stop, mStopDescription,
+ createPendingIntent(ListenerService.ACTION_STOP));
+ }
+ }
+
+ 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 make the notification background on Wearable devices look better.
+ * The returned Bitmap size will be exactly 400x400.
+ * According to http://developer.android.com/training/wearables/notifications/creating.html,
+ * the background image of Android Wear notifications should be of size 400x400 or 640x400.
+ * Otherwise, it will be scaled to fit the desired size. However for some reason (maybe battery
+ * concern), the smoothing filter is not applied when scaling the image, so we need to manually
+ * scale the image with filter applied before sending to Android Wear.
+ */
+ 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