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

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

Issue 2498683002: Add seek forward/backward controls in MediaNotification (Closed)
Patch Set: addressed nits from Anton Created 4 years, 1 month 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 bcd4e568cbe5befdbd7ac01f838185b11738748a..c0544a7ed577d085365406e042733ead949c1fda 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
@@ -39,6 +39,11 @@ import org.chromium.blink.mojom.MediaSessionAction;
import org.chromium.chrome.R;
import org.chromium.content_public.common.MediaMetadata;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
import javax.annotation.Nullable;
/**
@@ -58,7 +63,10 @@ public class MediaNotificationManager {
private static final int N_LARGE_ICON_SIZE_DP = 94;
// The maximum number of actions in CompactView media notification.
- private static final int MAXIMUM_NUM_ACTIONS_IN_COMPACT_VIEW = 3;
+ private static final int COMPACT_VIEW_ACTIONS_COUNT = 3;
+
+ // The maximum number of actions in BigView media notification.
+ private static final int BIG_VIEW_ACTIONS_COUNT = isRunningN() ? 5 : 3;
// We're always used on the UI thread but the LOCK is required by lint when creating the
// singleton.
@@ -87,6 +95,10 @@ public class MediaNotificationManager {
"MediaNotificationManager.ListenerService.PREVIOUS_TRACK";
private static final String ACTION_NEXT_TRACK =
"MediaNotificationManager.ListenerService.NEXT_TRACK";
+ private static final String ACTION_SEEK_FORWARD =
+ "MediaNotificationManager.ListenerService.SEEK_FORWARD";
+ private static final String ACTION_SEEK_BACKWARD =
+ "MediaNotificationmanager.ListenerService.SEEK_BACKWARD";
@Override
public IBinder onBind(Intent intent) {
@@ -161,6 +173,12 @@ public class MediaNotificationManager {
case KeyEvent.KEYCODE_MEDIA_NEXT:
manager.onMediaSessionAction(MediaSessionAction.NEXT_TRACK);
break;
+ case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
+ manager.onMediaSessionAction(MediaSessionAction.SEEK_FORWARD);
+ break;
+ case KeyEvent.KEYCODE_MEDIA_REWIND:
+ manager.onMediaSessionAction(MediaSessionAction.SEEK_BACKWARD);
+ break;
default:
break;
}
@@ -180,6 +198,10 @@ public class MediaNotificationManager {
manager.onMediaSessionAction(MediaSessionAction.PREVIOUS_TRACK);
} else if (ACTION_NEXT_TRACK.equals(action)) {
manager.onMediaSessionAction(MediaSessionAction.NEXT_TRACK);
+ } else if (ACTION_SEEK_FORWARD.equals(action)) {
+ manager.onMediaSessionAction(MediaSessionAction.SEEK_FORWARD);
+ } else if (ACTION_SEEK_BACKWARD.equals(action)) {
+ manager.onMediaSessionAction(MediaSessionAction.SEEK_BACKWARD);
}
}
}
@@ -465,11 +487,29 @@ public class MediaNotificationManager {
// ListenerService running for the notification. Only non-null when showing.
private ListenerService mService;
gone 2016/11/28 19:23:21 These member fields should all be grouped together
Zhiqiang Zhang (Slow) 2016/11/29 16:45:40 Done. Just realized the the definition of inner cl
- private final String mPlayDescription;
- private final String mPauseDescription;
- private final String mStopDescription;
- private final String mPreviousTrackDescription;
- private final String mNextTrackDescription;
+ /**
+ * The class containing all the information for adding a button in the notification for an
+ * action.
+ */
+ private static final class MediaButtonInfo {
+ // The resource id of this media button icon.
gone 2016/11/28 19:23:21 Use javadoc syntax here; Eclipse will show the com
Zhiqiang Zhang (Slow) 2016/11/29 16:45:40 Done.
+ public int iconResId;
+ // The resource id of this media button description.
+ public int descriptionResId;
+ // The intent string to be fired when this media button is clicked.
+ public String intentString;
+
+ public MediaButtonInfo(int buttonResId, int descriptionResId, String intentString) {
+ this.iconResId = buttonResId;
+ this.descriptionResId = descriptionResId;
+ this.intentString = intentString;
+ }
+ }
+
+ private SparseArray<MediaButtonInfo> mActionToButtonInfo = new SparseArray<MediaButtonInfo>();
+
+ @VisibleForTesting
+ static final int CUSTOM_MEDIA_SESSION_ACTION_STOP = MediaSessionAction.LAST + 1;
gone 2016/11/28 19:23:21 Any reason you're adding a random one here instead
Zhiqiang Zhang (Slow) 2016/11/29 16:45:40 The mojom enum is consistent with the API spec in
gone 2016/11/29 18:20:55 Acknowledged.
private NotificationCompat.Builder mNotificationBuilder;
@@ -506,16 +546,46 @@ public class MediaNotificationManager {
MediaNotificationManager.this.onMediaSessionAction(
MediaSessionAction.NEXT_TRACK);
}
+
+ @Override
+ public void onFastForward() {
+ MediaNotificationManager.this.onMediaSessionAction(
+ MediaSessionAction.SEEK_FORWARD);
+ }
+
+ @Override
+ public void onRewind() {
+ MediaNotificationManager.this.onMediaSessionAction(
+ MediaSessionAction.SEEK_BACKWARD);
+ }
};
private MediaNotificationManager(Context context, int notificationId) {
mContext = context;
- mPlayDescription = context.getResources().getString(R.string.accessibility_play);
- mPauseDescription = context.getResources().getString(R.string.accessibility_pause);
- mStopDescription = context.getResources().getString(R.string.accessibility_stop);
- mPreviousTrackDescription =
- context.getResources().getString(R.string.accessibility_previous_track);
- mNextTrackDescription = context.getResources().getString(R.string.accessibility_next_track);
+
+ mActionToButtonInfo.put(MediaSessionAction.PLAY,
+ new MediaButtonInfo(R.drawable.ic_media_control_play, R.string.accessibility_play,
+ ListenerService.ACTION_PLAY));
+ mActionToButtonInfo.put(MediaSessionAction.PAUSE,
+ new MediaButtonInfo(R.drawable.ic_media_control_pause, R.string.accessibility_pause,
+ ListenerService.ACTION_PAUSE));
+ mActionToButtonInfo.put(CUSTOM_MEDIA_SESSION_ACTION_STOP,
+ new MediaButtonInfo(R.drawable.ic_media_control_stop, R.string.accessibility_stop,
+ ListenerService.ACTION_STOP));
+ mActionToButtonInfo.put(MediaSessionAction.PREVIOUS_TRACK,
+ new MediaButtonInfo(R.drawable.ic_media_control_skip_previous,
+ R.string.accessibility_previous_track,
+ ListenerService.ACTION_PREVIOUS_TRACK));
+ mActionToButtonInfo.put(MediaSessionAction.NEXT_TRACK,
+ new MediaButtonInfo(R.drawable.ic_media_control_skip_next,
+ R.string.accessibility_next_track, ListenerService.ACTION_NEXT_TRACK));
+ mActionToButtonInfo.put(MediaSessionAction.SEEK_FORWARD,
+ new MediaButtonInfo(R.drawable.ic_media_control_fast_forward,
+ R.string.accessibility_seek_forward, ListenerService.ACTION_SEEK_FORWARD));
+ mActionToButtonInfo.put(MediaSessionAction.SEEK_BACKWARD,
+ new MediaButtonInfo(R.drawable.ic_media_control_fast_rewind,
+ R.string.accessibility_seek_backward,
+ ListenerService.ACTION_SEEK_BACKWARD));
}
/**
@@ -708,6 +778,12 @@ public class MediaNotificationManager {
if (mMediaNotificationInfo.mediaSessionActions.contains(MediaSessionAction.NEXT_TRACK)) {
actions |= PlaybackStateCompat.ACTION_SKIP_TO_NEXT;
}
+ if (mMediaNotificationInfo.mediaSessionActions.contains(MediaSessionAction.SEEK_FORWARD)) {
+ actions |= PlaybackStateCompat.ACTION_FAST_FORWARD;
+ }
+ if (mMediaNotificationInfo.mediaSessionActions.contains(MediaSessionAction.SEEK_BACKWARD)) {
+ actions |= PlaybackStateCompat.ACTION_REWIND;
+ }
return actions;
}
@@ -761,48 +837,48 @@ public class MediaNotificationManager {
}
private void addNotificationButtons(NotificationCompat.Builder builder) {
+ Set<Integer> actions = new HashSet<Integer>();
+
+ // TODO(zqzhang): handle other actions when play/pause is not supported? See
+ // https://crbug.com/667500
+ if (mMediaNotificationInfo.supportsPlayPause()) {
+ actions.addAll(mMediaNotificationInfo.mediaSessionActions);
+ if (mMediaNotificationInfo.isPaused) {
+ actions.remove(MediaSessionAction.PAUSE);
+ actions.add(MediaSessionAction.PLAY);
+ } else {
+ actions.remove(MediaSessionAction.PLAY);
+ actions.add(MediaSessionAction.PAUSE);
+ }
+ }
+
+ if (mMediaNotificationInfo.supportsStop()) {
+ actions.add(CUSTOM_MEDIA_SESSION_ACTION_STOP);
+ }
+
+ List<Integer> bigViewActions = computeBigViewActions(actions);
+
+ int numAddedActions = 0;
gone 2016/11/28 19:23:21 Where do you use this variable?
Zhiqiang Zhang (Slow) 2016/11/29 16:45:40 Removed. Thanks!
+
+ for (int action : bigViewActions) {
+ MediaButtonInfo buttonInfo = mActionToButtonInfo.get(action);
+ builder.addAction(buttonInfo.iconResId,
+ mContext.getResources().getString(buttonInfo.descriptionResId),
+ createPendingIntent(buttonInfo.intentString));
+ }
+
// Only apply MediaStyle when NotificationInfo supports play/pause.
if (mMediaNotificationInfo.supportsPlayPause()) {
NotificationCompat.MediaStyle style = new NotificationCompat.MediaStyle();
style.setMediaSession(mMediaSession.getSessionToken());
- int numAddedActions = 0;
+ int[] compactViewActionIndices = computeCompactViewActionIndices(bigViewActions);
- if (mMediaNotificationInfo.mediaSessionActions.contains(
- MediaSessionAction.PREVIOUS_TRACK)) {
- builder.addAction(R.drawable.ic_media_control_skip_previous,
- mPreviousTrackDescription,
- createPendingIntent(ListenerService.ACTION_PREVIOUS_TRACK));
- ++numAddedActions;
- }
- if (mMediaNotificationInfo.isPaused) {
- builder.addAction(R.drawable.ic_media_control_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_media_control_pause, mPauseDescription,
- createPendingIntent(ListenerService.ACTION_PAUSE));
- }
- ++numAddedActions;
- if (mMediaNotificationInfo.mediaSessionActions.contains(
- MediaSessionAction.NEXT_TRACK)) {
- builder.addAction(R.drawable.ic_media_control_skip_next, mNextTrackDescription,
- createPendingIntent(ListenerService.ACTION_NEXT_TRACK));
- ++numAddedActions;
- }
- numAddedActions = Math.min(numAddedActions, MAXIMUM_NUM_ACTIONS_IN_COMPACT_VIEW);
- int[] compactViewActions = new int[numAddedActions];
- for (int i = 0; i < numAddedActions; ++i) compactViewActions[i] = i;
- style.setShowActionsInCompactView(compactViewActions);
+ style.setShowActionsInCompactView(compactViewActionIndices);
style.setCancelButtonIntent(createPendingIntent(ListenerService.ACTION_CANCEL));
style.setShowCancelButton(true);
builder.setStyle(style);
}
-
- if (mMediaNotificationInfo.supportsStop()) {
- builder.addAction(R.drawable.ic_media_control_stop, mStopDescription,
- createPendingIntent(ListenerService.ACTION_STOP));
- }
}
private Bitmap drawableToBitmap(Drawable drawable) {
@@ -832,4 +908,108 @@ public class MediaNotificationManager {
}
return artist + " - " + album;
}
+
+ /**
+ * Compute the actions to be shown in BigView media notification.
+ * The method assumes exactly one of PLAY/PAUSE/STOP can be present.
+ * Actions in pairs are preferred if there are more actions than |BIG_VIEW_ACTIONS_COUNT|.
+ *
+ * TODO(zqzhang): get UX feedback to decide which actions to select when the number of actions
+ * is greater than that can be displayed. See https://crbug.com/667500
+ */
+ private List<Integer> computeBigViewActions(Set<Integer> actions) {
+ // Exactly one of PLAY/PAUSE/STOP can exist.
+ assert (actions.contains(MediaSessionAction.PLAY) ? 1 : 0)
+ + (actions.contains(MediaSessionAction.PAUSE) ? 1 : 0)
+ + (actions.contains(CUSTOM_MEDIA_SESSION_ACTION_STOP) ? 1 : 0)
+ == 1;
+ int[] actionByOrder = {
+ MediaSessionAction.PREVIOUS_TRACK,
+ MediaSessionAction.SEEK_BACKWARD,
+ MediaSessionAction.PLAY,
+ MediaSessionAction.PAUSE,
+ MediaSessionAction.SEEK_FORWARD,
+ MediaSessionAction.NEXT_TRACK,
+ CUSTOM_MEDIA_SESSION_ACTION_STOP,
+ };
+
+ // First, select at most |BIG_VIEW_ACTIONS_COUNT| actions by priority.
+ Set<Integer> selectedActions;
+ if (actions.size() <= BIG_VIEW_ACTIONS_COUNT) {
+ selectedActions = actions;
+ } else {
+ selectedActions = new HashSet<Integer>();
gone 2016/11/28 19:23:21 new HashSet<>();
Zhiqiang Zhang (Slow) 2016/11/29 16:45:40 Done.
+ if (actions.contains(CUSTOM_MEDIA_SESSION_ACTION_STOP)) {
+ selectedActions.add(CUSTOM_MEDIA_SESSION_ACTION_STOP);
+ } else {
+ if (actions.contains(MediaSessionAction.PLAY)) {
+ selectedActions.add(MediaSessionAction.PLAY);
+ } else {
+ selectedActions.add(MediaSessionAction.PAUSE);
+ }
+ if (actions.contains(MediaSessionAction.PREVIOUS_TRACK)
+ && actions.contains(MediaSessionAction.NEXT_TRACK)) {
+ selectedActions.add(MediaSessionAction.PREVIOUS_TRACK);
+ selectedActions.add(MediaSessionAction.NEXT_TRACK);
+ } else {
+ selectedActions.add(MediaSessionAction.SEEK_BACKWARD);
+ selectedActions.add(MediaSessionAction.SEEK_FORWARD);
+ }
+ }
+ }
+
+ // Second, sort the selected actions.
+ List<Integer> sortedActions = new ArrayList<Integer>();
gone 2016/11/28 19:23:21 new ArrayList<>();
Zhiqiang Zhang (Slow) 2016/11/29 16:45:40 Done.
+ for (int action : actionByOrder) {
+ if (selectedActions.contains(action)) sortedActions.add(action);
+ }
+
+ return sortedActions;
+ }
+
+ /**
+ * Compute the actions to be shown in CompactView media notification.
+ * The method assumes exactly one of PLAY/PAUSE/STOP can be present.
gone 2016/11/28 19:23:21 Should assert this directly in the code. Without
Zhiqiang Zhang (Slow) 2016/11/29 16:45:40 Done.
+ * Actions in pairs are preferred if there are more actions than |COMPACT_VIEW_ACTIONS_COUNT|.
+ */
+ @VisibleForTesting
+ static int[] computeCompactViewActionIndices(List<Integer> actions) {
+ if (actions.size() <= COMPACT_VIEW_ACTIONS_COUNT) {
+ // If the number of actions is less than |COMPACT_VIEW_ACTIONS_COUNT|, just return an
+ // array of 0, 1, ..., |actions.size()|-1.
+ int[] actionsArray = new int[actions.size()];
+ for (int i = 0; i < actions.size(); ++i) actionsArray[i] = i;
+ return actionsArray;
+ }
+
+ if (actions.contains(CUSTOM_MEDIA_SESSION_ACTION_STOP)) {
+ int[] actionsArray = new int[1];
+ actionsArray[0] = actions.indexOf(CUSTOM_MEDIA_SESSION_ACTION_STOP);
+ }
+
+ int[] actionsArray = new int[COMPACT_VIEW_ACTIONS_COUNT];
+ if (actions.contains(MediaSessionAction.PREVIOUS_TRACK)
+ && actions.contains(MediaSessionAction.NEXT_TRACK)) {
+ actionsArray[0] = actions.indexOf(MediaSessionAction.PREVIOUS_TRACK);
+ if (actions.contains(MediaSessionAction.PLAY)) {
+ actionsArray[1] = actions.indexOf(MediaSessionAction.PLAY);
+ } else {
+ actionsArray[1] = actions.indexOf(MediaSessionAction.PAUSE);
+ }
+ actionsArray[2] = actions.indexOf(MediaSessionAction.NEXT_TRACK);
+ return actionsArray;
+ }
+
+ assert actions.contains(MediaSessionAction.SEEK_BACKWARD)
+ && actions.contains(MediaSessionAction.SEEK_FORWARD);
+ actionsArray[0] = actions.indexOf(MediaSessionAction.SEEK_BACKWARD);
+ if (actions.contains(MediaSessionAction.PLAY)) {
+ actionsArray[1] = actions.indexOf(MediaSessionAction.PLAY);
+ } else {
+ actionsArray[1] = actions.indexOf(MediaSessionAction.PAUSE);
+ }
+ actionsArray[2] = actions.indexOf(MediaSessionAction.SEEK_FORWARD);
+
+ return actionsArray;
+ }
}

Powered by Google App Engine
This is Rietveld 408576698