Commit 62ee13b1 by bachinger Committed by Oliver Woodman

allow apps to set custom metadata

[]

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=201350930
parent 4f2b5960
...@@ -29,6 +29,8 @@ ...@@ -29,6 +29,8 @@
([#4023](https://github.com/google/ExoPlayer/issues/4023)). ([#4023](https://github.com/google/ExoPlayer/issues/4023)).
* Add support for mu-law and A-law PCM with the ffmpeg extension * Add support for mu-law and A-law PCM with the ffmpeg extension
([#4360](https://github.com/google/ExoPlayer/issues/4360)). ([#4360](https://github.com/google/ExoPlayer/issues/4360)).
* MediaSession extension:
* Allow apps to set custom metadata with a MediaMetadataProvider.
* Add `PlayerView.isControllerVisible` * Add `PlayerView.isControllerVisible`
([#4385](https://github.com/google/ExoPlayer/issues/4385)). ([#4385](https://github.com/google/ExoPlayer/issues/4385)).
* Expose all internal ID3 data stored in MP4 udta boxes, and switch from using * Expose all internal ID3 data stored in MP4 udta boxes, and switch from using
......
...@@ -46,25 +46,26 @@ import java.util.Map; ...@@ -46,25 +46,26 @@ import java.util.Map;
/** /**
* Connects a {@link MediaSessionCompat} to a {@link Player}. * Connects a {@link MediaSessionCompat} to a {@link Player}.
* <p> *
* The connector listens for actions sent by the media session's controller and implements these * <p>The connector listens for actions sent by the media session's controller and implements these
* actions by calling appropriate player methods. The playback state of the media session is * actions by calling appropriate player methods. The playback state of the media session is
* automatically synced with the player. The connector can also be optionally extended by providing * automatically synced with the player. The connector can also be optionally extended by providing
* various collaborators: * various collaborators:
*
* <ul> * <ul>
* <li>Actions to initiate media playback ({@code PlaybackStateCompat#ACTION_PREPARE_*} and * <li>Actions to initiate media playback ({@code PlaybackStateCompat#ACTION_PREPARE_*} and {@code
* {@code PlaybackStateCompat#ACTION_PLAY_*}) can be handled by a {@link PlaybackPreparer} passed * PlaybackStateCompat#ACTION_PLAY_*}) can be handled by a {@link PlaybackPreparer} passed
* when calling {@link #setPlayer(Player, PlaybackPreparer, CustomActionProvider...)}. Custom * when calling {@link #setPlayer(Player, PlaybackPreparer, CustomActionProvider...)}. Custom
* actions can be handled by passing one or more {@link CustomActionProvider}s in a similar way. * actions can be handled by passing one or more {@link CustomActionProvider}s in a similar
* </li> * way.
* <li>To enable a media queue and navigation within it, you can set a {@link QueueNavigator} by * <li>To enable a media queue and navigation within it, you can set a {@link QueueNavigator} by
* calling {@link #setQueueNavigator(QueueNavigator)}. Use of {@link TimelineQueueNavigator} is * calling {@link #setQueueNavigator(QueueNavigator)}. Use of {@link TimelineQueueNavigator}
* recommended for most use cases.</li> * is recommended for most use cases.
* <li>To enable editing of the media queue, you can set a {@link QueueEditor} by calling * <li>To enable editing of the media queue, you can set a {@link QueueEditor} by calling {@link
* {@link #setQueueEditor(QueueEditor)}.</li> * #setQueueEditor(QueueEditor)}.
* <li>An {@link ErrorMessageProvider} for providing human readable error messages and * <li>An {@link ErrorMessageProvider} for providing human readable error messages and
* corresponding error codes can be set by calling * corresponding error codes can be set by calling {@link
* {@link #setErrorMessageProvider(ErrorMessageProvider)}.</li> * #setErrorMessageProvider(ErrorMessageProvider)}.
* </ul> * </ul>
*/ */
public final class MediaSessionConnector { public final class MediaSessionConnector {
...@@ -74,35 +75,30 @@ public final class MediaSessionConnector { ...@@ -74,35 +75,30 @@ public final class MediaSessionConnector {
} }
/** /**
* The default repeat toggle modes which is the bitmask of * The default repeat toggle modes which is the bitmask of {@link
* {@link RepeatModeUtil#REPEAT_TOGGLE_MODE_ONE} and * RepeatModeUtil#REPEAT_TOGGLE_MODE_ONE} and {@link RepeatModeUtil#REPEAT_TOGGLE_MODE_ALL}.
* {@link RepeatModeUtil#REPEAT_TOGGLE_MODE_ALL}.
*/ */
public static final @RepeatModeUtil.RepeatToggleModes int DEFAULT_REPEAT_TOGGLE_MODES = public static final @RepeatModeUtil.RepeatToggleModes int DEFAULT_REPEAT_TOGGLE_MODES =
RepeatModeUtil.REPEAT_TOGGLE_MODE_ONE | RepeatModeUtil.REPEAT_TOGGLE_MODE_ALL; RepeatModeUtil.REPEAT_TOGGLE_MODE_ONE | RepeatModeUtil.REPEAT_TOGGLE_MODE_ALL;
public static final String EXTRAS_PITCH = "EXO_PITCH"; public static final String EXTRAS_PITCH = "EXO_PITCH";
private static final int BASE_MEDIA_SESSION_FLAGS = MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS private static final int BASE_MEDIA_SESSION_FLAGS =
| MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS; MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS
private static final int EDITOR_MEDIA_SESSION_FLAGS = BASE_MEDIA_SESSION_FLAGS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS;
| MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS; private static final int EDITOR_MEDIA_SESSION_FLAGS =
BASE_MEDIA_SESSION_FLAGS | MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS;
/** /** Receiver of media commands sent by a media controller. */
* Receiver of media commands sent by a media controller.
*/
public interface CommandReceiver { public interface CommandReceiver {
/** /**
* Returns the commands the receiver handles, or {@code null} if no commands need to be handled. * Returns the commands the receiver handles, or {@code null} if no commands need to be handled.
*/ */
String[] getCommands(); String[] getCommands();
/** /** See {@link MediaSessionCompat.Callback#onCommand(String, Bundle, ResultReceiver)}. */
* See {@link MediaSessionCompat.Callback#onCommand(String, Bundle, ResultReceiver)}.
*/
void onCommand(Player player, String command, Bundle extras, ResultReceiver cb); void onCommand(Player player, String command, Bundle extras, ResultReceiver cb);
} }
/** /** Interface to which playback preparation actions are delegated. */
* Interface to which playback preparation actions are delegated.
*/
public interface PlaybackPreparer extends CommandReceiver { public interface PlaybackPreparer extends CommandReceiver {
long ACTIONS = long ACTIONS =
...@@ -127,96 +123,77 @@ public final class MediaSessionConnector { ...@@ -127,96 +123,77 @@ public final class MediaSessionConnector {
* @return The bitmask of the supported media actions. * @return The bitmask of the supported media actions.
*/ */
long getSupportedPrepareActions(); long getSupportedPrepareActions();
/** /** See {@link MediaSessionCompat.Callback#onPrepare()}. */
* See {@link MediaSessionCompat.Callback#onPrepare()}.
*/
void onPrepare(); void onPrepare();
/** /** See {@link MediaSessionCompat.Callback#onPrepareFromMediaId(String, Bundle)}. */
* See {@link MediaSessionCompat.Callback#onPrepareFromMediaId(String, Bundle)}.
*/
void onPrepareFromMediaId(String mediaId, Bundle extras); void onPrepareFromMediaId(String mediaId, Bundle extras);
/** /** See {@link MediaSessionCompat.Callback#onPrepareFromSearch(String, Bundle)}. */
* See {@link MediaSessionCompat.Callback#onPrepareFromSearch(String, Bundle)}.
*/
void onPrepareFromSearch(String query, Bundle extras); void onPrepareFromSearch(String query, Bundle extras);
/** /** See {@link MediaSessionCompat.Callback#onPrepareFromUri(Uri, Bundle)}. */
* See {@link MediaSessionCompat.Callback#onPrepareFromUri(Uri, Bundle)}.
*/
void onPrepareFromUri(Uri uri, Bundle extras); void onPrepareFromUri(Uri uri, Bundle extras);
} }
/** /** Interface to which playback actions are delegated. */
* Interface to which playback actions are delegated.
*/
public interface PlaybackController extends CommandReceiver { public interface PlaybackController extends CommandReceiver {
long ACTIONS = PlaybackStateCompat.ACTION_PLAY_PAUSE | PlaybackStateCompat.ACTION_PLAY long ACTIONS =
| PlaybackStateCompat.ACTION_PAUSE | PlaybackStateCompat.ACTION_SEEK_TO PlaybackStateCompat.ACTION_PLAY_PAUSE
| PlaybackStateCompat.ACTION_FAST_FORWARD | PlaybackStateCompat.ACTION_REWIND | PlaybackStateCompat.ACTION_PLAY
| PlaybackStateCompat.ACTION_STOP | PlaybackStateCompat.ACTION_SET_REPEAT_MODE | PlaybackStateCompat.ACTION_PAUSE
| PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE; | PlaybackStateCompat.ACTION_SEEK_TO
| PlaybackStateCompat.ACTION_FAST_FORWARD
| PlaybackStateCompat.ACTION_REWIND
| PlaybackStateCompat.ACTION_STOP
| PlaybackStateCompat.ACTION_SET_REPEAT_MODE
| PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE;
/** /**
* Returns the actions which are supported by the controller. The supported actions must be a * Returns the actions which are supported by the controller. The supported actions must be a
* bitmask combined out of {@link PlaybackStateCompat#ACTION_PLAY_PAUSE}, * bitmask combined out of {@link PlaybackStateCompat#ACTION_PLAY_PAUSE}, {@link
* {@link PlaybackStateCompat#ACTION_PLAY}, {@link PlaybackStateCompat#ACTION_PAUSE}, * PlaybackStateCompat#ACTION_PLAY}, {@link PlaybackStateCompat#ACTION_PAUSE}, {@link
* {@link PlaybackStateCompat#ACTION_SEEK_TO}, {@link PlaybackStateCompat#ACTION_FAST_FORWARD}, * PlaybackStateCompat#ACTION_SEEK_TO}, {@link PlaybackStateCompat#ACTION_FAST_FORWARD}, {@link
* {@link PlaybackStateCompat#ACTION_REWIND}, {@link PlaybackStateCompat#ACTION_STOP}, * PlaybackStateCompat#ACTION_REWIND}, {@link PlaybackStateCompat#ACTION_STOP}, {@link
* {@link PlaybackStateCompat#ACTION_SET_REPEAT_MODE} and * PlaybackStateCompat#ACTION_SET_REPEAT_MODE} and {@link
* {@link PlaybackStateCompat#ACTION_SET_SHUFFLE_MODE}. * PlaybackStateCompat#ACTION_SET_SHUFFLE_MODE}.
* *
* @param player The player. * @param player The player.
* @return The bitmask of the supported media actions. * @return The bitmask of the supported media actions.
*/ */
long getSupportedPlaybackActions(@Nullable Player player); long getSupportedPlaybackActions(@Nullable Player player);
/** /** See {@link MediaSessionCompat.Callback#onPlay()}. */
* See {@link MediaSessionCompat.Callback#onPlay()}.
*/
void onPlay(Player player); void onPlay(Player player);
/** /** See {@link MediaSessionCompat.Callback#onPause()}. */
* See {@link MediaSessionCompat.Callback#onPause()}.
*/
void onPause(Player player); void onPause(Player player);
/** /** See {@link MediaSessionCompat.Callback#onSeekTo(long)}. */
* See {@link MediaSessionCompat.Callback#onSeekTo(long)}.
*/
void onSeekTo(Player player, long position); void onSeekTo(Player player, long position);
/** /** See {@link MediaSessionCompat.Callback#onFastForward()}. */
* See {@link MediaSessionCompat.Callback#onFastForward()}.
*/
void onFastForward(Player player); void onFastForward(Player player);
/** /** See {@link MediaSessionCompat.Callback#onRewind()}. */
* See {@link MediaSessionCompat.Callback#onRewind()}.
*/
void onRewind(Player player); void onRewind(Player player);
/** /** See {@link MediaSessionCompat.Callback#onStop()}. */
* See {@link MediaSessionCompat.Callback#onStop()}.
*/
void onStop(Player player); void onStop(Player player);
/** /** See {@link MediaSessionCompat.Callback#onSetShuffleMode(int)}. */
* See {@link MediaSessionCompat.Callback#onSetShuffleMode(int)}.
*/
void onSetShuffleMode(Player player, int shuffleMode); void onSetShuffleMode(Player player, int shuffleMode);
/** /** See {@link MediaSessionCompat.Callback#onSetRepeatMode(int)}. */
* See {@link MediaSessionCompat.Callback#onSetRepeatMode(int)}.
*/
void onSetRepeatMode(Player player, int repeatMode); void onSetRepeatMode(Player player, int repeatMode);
} }
/** /**
* Handles queue navigation actions, and updates the media session queue by calling * Handles queue navigation actions, and updates the media session queue by calling {@code
* {@code MediaSessionCompat.setQueue()}. * MediaSessionCompat.setQueue()}.
*/ */
public interface QueueNavigator extends CommandReceiver { public interface QueueNavigator extends CommandReceiver {
long ACTIONS = PlaybackStateCompat.ACTION_SKIP_TO_QUEUE_ITEM long ACTIONS =
| PlaybackStateCompat.ACTION_SKIP_TO_NEXT | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS; PlaybackStateCompat.ACTION_SKIP_TO_QUEUE_ITEM
| PlaybackStateCompat.ACTION_SKIP_TO_NEXT
| PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS;
/** /**
* Returns the actions which are supported by the navigator. The supported actions must be a * Returns the actions which are supported by the navigator. The supported actions must be a
* bitmask combined out of {@link PlaybackStateCompat#ACTION_SKIP_TO_QUEUE_ITEM}, * bitmask combined out of {@link PlaybackStateCompat#ACTION_SKIP_TO_QUEUE_ITEM}, {@link
* {@link PlaybackStateCompat#ACTION_SKIP_TO_NEXT}, * PlaybackStateCompat#ACTION_SKIP_TO_NEXT}, {@link
* {@link PlaybackStateCompat#ACTION_SKIP_TO_PREVIOUS}. * PlaybackStateCompat#ACTION_SKIP_TO_PREVIOUS}.
* *
* @param player The {@link Player}. * @param player The {@link Player}.
* @return The bitmask of the supported media actions. * @return The bitmask of the supported media actions.
...@@ -235,34 +212,26 @@ public final class MediaSessionConnector { ...@@ -235,34 +212,26 @@ public final class MediaSessionConnector {
*/ */
void onCurrentWindowIndexChanged(Player player); void onCurrentWindowIndexChanged(Player player);
/** /**
* Gets the id of the currently active queue item, or * Gets the id of the currently active queue item, or {@link
* {@link MediaSessionCompat.QueueItem#UNKNOWN_ID} if the active item is unknown. * MediaSessionCompat.QueueItem#UNKNOWN_ID} if the active item is unknown.
* <p> *
* To let the connector publish metadata for the active queue item, the queue item with the * <p>To let the connector publish metadata for the active queue item, the queue item with the
* returned id must be available in the list of items returned by * returned id must be available in the list of items returned by {@link
* {@link MediaControllerCompat#getQueue()}. * MediaControllerCompat#getQueue()}.
* *
* @param player The player connected to the media session. * @param player The player connected to the media session.
* @return The id of the active queue item. * @return The id of the active queue item.
*/ */
long getActiveQueueItemId(@Nullable Player player); long getActiveQueueItemId(@Nullable Player player);
/** /** See {@link MediaSessionCompat.Callback#onSkipToPrevious()}. */
* See {@link MediaSessionCompat.Callback#onSkipToPrevious()}.
*/
void onSkipToPrevious(Player player); void onSkipToPrevious(Player player);
/** /** See {@link MediaSessionCompat.Callback#onSkipToQueueItem(long)}. */
* See {@link MediaSessionCompat.Callback#onSkipToQueueItem(long)}.
*/
void onSkipToQueueItem(Player player, long id); void onSkipToQueueItem(Player player, long id);
/** /** See {@link MediaSessionCompat.Callback#onSkipToNext()}. */
* See {@link MediaSessionCompat.Callback#onSkipToNext()}.
*/
void onSkipToNext(Player player); void onSkipToNext(Player player);
} }
/** /** Handles media session queue edits. */
* Handles media session queue edits.
*/
public interface QueueEditor extends CommandReceiver { public interface QueueEditor extends CommandReceiver {
/** /**
...@@ -270,8 +239,8 @@ public final class MediaSessionConnector { ...@@ -270,8 +239,8 @@ public final class MediaSessionConnector {
*/ */
void onAddQueueItem(Player player, MediaDescriptionCompat description); void onAddQueueItem(Player player, MediaDescriptionCompat description);
/** /**
* See {@link MediaSessionCompat.Callback#onAddQueueItem(MediaDescriptionCompat description, * See {@link MediaSessionCompat.Callback#onAddQueueItem(MediaDescriptionCompat description, int
* int index)}. * index)}.
*/ */
void onAddQueueItem(Player player, MediaDescriptionCompat description, int index); void onAddQueueItem(Player player, MediaDescriptionCompat description, int index);
/** /**
...@@ -279,9 +248,7 @@ public final class MediaSessionConnector { ...@@ -279,9 +248,7 @@ public final class MediaSessionConnector {
* description)}. * description)}.
*/ */
void onRemoveQueueItem(Player player, MediaDescriptionCompat description); void onRemoveQueueItem(Player player, MediaDescriptionCompat description);
/** /** See {@link MediaSessionCompat.Callback#onRemoveQueueItemAt(int index)}. */
* See {@link MediaSessionCompat.Callback#onRemoveQueueItemAt(int index)}.
*/
void onRemoveQueueItemAt(Player player, int index); void onRemoveQueueItemAt(Player player, int index);
} }
...@@ -308,26 +275,33 @@ public final class MediaSessionConnector { ...@@ -308,26 +275,33 @@ public final class MediaSessionConnector {
void onCustomAction(String action, Bundle extras); void onCustomAction(String action, Bundle extras);
/** /**
* Returns a {@link PlaybackStateCompat.CustomAction} which will be published to the * Returns a {@link PlaybackStateCompat.CustomAction} which will be published to the media
* media session by the connector or {@code null} if this action should not be published at the * session by the connector or {@code null} if this action should not be published at the given
* given player state. * player state.
* *
* @return The custom action to be included in the session playback state or {@code null}. * @return The custom action to be included in the session playback state or {@code null}.
*/ */
PlaybackStateCompat.CustomAction getCustomAction(); PlaybackStateCompat.CustomAction getCustomAction();
} }
/** /** Provides a {@link MediaMetadataCompat} for a given player state. */
* The wrapped {@link MediaSessionCompat}. public interface MediaMetadataProvider {
*/ /**
* Gets the {@link MediaMetadataCompat} to be published to the session.
*
* @param player The player for which to provide metadata.
* @return The {@link MediaMetadataCompat} to be published to the session.
*/
MediaMetadataCompat getMetadata(Player player);
}
/** The wrapped {@link MediaSessionCompat}. */
public final MediaSessionCompat mediaSession; public final MediaSessionCompat mediaSession;
private final MediaControllerCompat mediaController; private @Nullable final MediaMetadataProvider mediaMetadataProvider;
private final boolean doMaintainMetadata;
private final ExoPlayerEventListener exoPlayerEventListener; private final ExoPlayerEventListener exoPlayerEventListener;
private final MediaSessionCallback mediaSessionCallback; private final MediaSessionCallback mediaSessionCallback;
private final PlaybackController playbackController; private final PlaybackController playbackController;
private final String metadataExtrasPrefix;
private final Map<String, CommandReceiver> commandMap; private final Map<String, CommandReceiver> commandMap;
private Player player; private Player player;
...@@ -353,14 +327,18 @@ public final class MediaSessionConnector { ...@@ -353,14 +327,18 @@ public final class MediaSessionConnector {
/** /**
* Creates an instance. * Creates an instance.
* *
* <p>Equivalent to {@code MediaSessionConnector(mediaSession, playbackController, true, null)}. * <p>Equivalent to {@code MediaSessionConnector(mediaSession, playbackController, new
* DefaultMediaMetadataProvider(mediaSession.getController(), null))}.
* *
* @param mediaSession The {@link MediaSessionCompat} to connect to. * @param mediaSession The {@link MediaSessionCompat} to connect to.
* @param playbackController A {@link PlaybackController} for handling playback actions. * @param playbackController A {@link PlaybackController} for handling playback actions.
*/ */
public MediaSessionConnector( public MediaSessionConnector(
MediaSessionCompat mediaSession, PlaybackController playbackController) { MediaSessionCompat mediaSession, PlaybackController playbackController) {
this(mediaSession, playbackController, true, null); this(
mediaSession,
playbackController,
new DefaultMediaMetadataProvider(mediaSession.getController(), null));
} }
/** /**
...@@ -369,24 +347,46 @@ public final class MediaSessionConnector { ...@@ -369,24 +347,46 @@ public final class MediaSessionConnector {
* @param mediaSession The {@link MediaSessionCompat} to connect to. * @param mediaSession The {@link MediaSessionCompat} to connect to.
* @param playbackController A {@link PlaybackController} for handling playback actions, or {@code * @param playbackController A {@link PlaybackController} for handling playback actions, or {@code
* null} if the connector should handle playback actions directly. * null} if the connector should handle playback actions directly.
* @param doMaintainMetadata Whether the connector should maintain the metadata of the session. If * @param doMaintainMetadata Whether the connector should maintain the metadata of the session.
* {@code false}, you need to maintain the metadata of the media session yourself (provide at
* least the duration to allow clients to show a progress bar).
* @param metadataExtrasPrefix A string to prefix extra keys which are propagated from the active * @param metadataExtrasPrefix A string to prefix extra keys which are propagated from the active
* queue item to the session metadata. * queue item to the session metadata.
* @deprecated Use {@link MediaSessionConnector#MediaSessionConnector(MediaSessionCompat,
* PlaybackController, MediaMetadataProvider)}.
*/ */
@Deprecated
public MediaSessionConnector( public MediaSessionConnector(
MediaSessionCompat mediaSession, MediaSessionCompat mediaSession,
PlaybackController playbackController, @Nullable PlaybackController playbackController,
boolean doMaintainMetadata, boolean doMaintainMetadata,
@Nullable String metadataExtrasPrefix) { @Nullable String metadataExtrasPrefix) {
this(
mediaSession,
playbackController,
doMaintainMetadata
? new DefaultMediaMetadataProvider(mediaSession.getController(), metadataExtrasPrefix)
: null);
}
/**
* Creates an instance. Must be called on the same thread that is used to construct the player
* instances passed to {@link #setPlayer(Player, PlaybackPreparer, CustomActionProvider...)}.
*
* @param mediaSession The {@link MediaSessionCompat} to connect to.
* @param playbackController A {@link PlaybackController} for handling playback actions, or {@code
* null} if the connector should handle playback actions directly.
* @param mediaMetadataProvider A {@link MediaMetadataProvider} for providing a custom metadata
* object to be published to the media session, or {@code null} if metadata shouldn't be
* published.
*/
public MediaSessionConnector(
MediaSessionCompat mediaSession,
@Nullable PlaybackController playbackController,
@Nullable MediaMetadataProvider mediaMetadataProvider) {
this.mediaSession = mediaSession; this.mediaSession = mediaSession;
this.playbackController = playbackController != null ? playbackController this.playbackController =
: new DefaultPlaybackController(); playbackController != null ? playbackController : new DefaultPlaybackController();
this.metadataExtrasPrefix = metadataExtrasPrefix != null ? metadataExtrasPrefix : ""; this.mediaMetadataProvider = mediaMetadataProvider;
this.doMaintainMetadata = doMaintainMetadata;
mediaSession.setFlags(BASE_MEDIA_SESSION_FLAGS); mediaSession.setFlags(BASE_MEDIA_SESSION_FLAGS);
mediaController = mediaSession.getController();
mediaSessionCallback = new MediaSessionCallback(); mediaSessionCallback = new MediaSessionCallback();
exoPlayerEventListener = new ExoPlayerEventListener(); exoPlayerEventListener = new ExoPlayerEventListener();
customActionMap = Collections.emptyMap(); customActionMap = Collections.emptyMap();
...@@ -420,8 +420,10 @@ public final class MediaSessionConnector { ...@@ -420,8 +420,10 @@ public final class MediaSessionConnector {
this.playbackPreparer = playbackPreparer; this.playbackPreparer = playbackPreparer;
registerCommandReceiver(playbackPreparer); registerCommandReceiver(playbackPreparer);
this.customActionProviders = (player != null && customActionProviders != null) this.customActionProviders =
? customActionProviders : new CustomActionProvider[0]; (player != null && customActionProviders != null)
? customActionProviders
: new CustomActionProvider[0];
if (player != null) { if (player != null) {
Handler handler = new Handler(Util.getLooper()); Handler handler = new Handler(Util.getLooper());
mediaSession.setCallback(mediaSessionCallback, handler); mediaSession.setCallback(mediaSessionCallback, handler);
...@@ -486,19 +488,15 @@ public final class MediaSessionConnector { ...@@ -486,19 +488,15 @@ public final class MediaSessionConnector {
} }
} }
private void registerCommandReceiver(CommandReceiver commandReceiver) { /**
if (commandReceiver != null && commandReceiver.getCommands() != null) { * Updates the metadata of the media session.
for (String command : commandReceiver.getCommands()) { *
commandMap.put(command, commandReceiver); * <p>Apps normally only need to call this method when the backing data for a given media item has
} * changed and the metadata should be updated immediately.
} */
} public final void updateMediaSessionMetadata() {
if (mediaMetadataProvider != null) {
private void unregisterCommandReceiver(CommandReceiver commandReceiver) { mediaSession.setMetadata(mediaMetadataProvider.getMetadata(player));
if (commandReceiver != null && commandReceiver.getCommands() != null) {
for (String command : commandReceiver.getCommands()) {
commandMap.remove(command);
}
} }
} }
...@@ -531,28 +529,50 @@ public final class MediaSessionConnector { ...@@ -531,28 +529,50 @@ public final class MediaSessionConnector {
Pair<Integer, String> message = errorMessageProvider.getErrorMessage(playbackError); Pair<Integer, String> message = errorMessageProvider.getErrorMessage(playbackError);
builder.setErrorMessage(message.first, message.second); builder.setErrorMessage(message.first, message.second);
} }
long activeQueueItemId = queueNavigator != null ? queueNavigator.getActiveQueueItemId(player) long activeQueueItemId =
: MediaSessionCompat.QueueItem.UNKNOWN_ID; queueNavigator != null
? queueNavigator.getActiveQueueItemId(player)
: MediaSessionCompat.QueueItem.UNKNOWN_ID;
Bundle extras = new Bundle(); Bundle extras = new Bundle();
extras.putFloat(EXTRAS_PITCH, player.getPlaybackParameters().pitch); extras.putFloat(EXTRAS_PITCH, player.getPlaybackParameters().pitch);
builder.setActions(buildPlaybackActions()) builder
.setActions(buildPlaybackActions())
.setActiveQueueItemId(activeQueueItemId) .setActiveQueueItemId(activeQueueItemId)
.setBufferedPosition(player.getBufferedPosition()) .setBufferedPosition(player.getBufferedPosition())
.setState(sessionPlaybackState, player.getCurrentPosition(), .setState(
player.getPlaybackParameters().speed, SystemClock.elapsedRealtime()) sessionPlaybackState,
player.getCurrentPosition(),
player.getPlaybackParameters().speed,
SystemClock.elapsedRealtime())
.setExtras(extras); .setExtras(extras);
mediaSession.setPlaybackState(builder.build()); mediaSession.setPlaybackState(builder.build());
} }
private void registerCommandReceiver(CommandReceiver commandReceiver) {
if (commandReceiver != null && commandReceiver.getCommands() != null) {
for (String command : commandReceiver.getCommands()) {
commandMap.put(command, commandReceiver);
}
}
}
private void unregisterCommandReceiver(CommandReceiver commandReceiver) {
if (commandReceiver != null && commandReceiver.getCommands() != null) {
for (String command : commandReceiver.getCommands()) {
commandMap.remove(command);
}
}
}
private long buildPlaybackActions() { private long buildPlaybackActions() {
long actions = (PlaybackController.ACTIONS long actions =
& playbackController.getSupportedPlaybackActions(player)); (PlaybackController.ACTIONS & playbackController.getSupportedPlaybackActions(player));
if (playbackPreparer != null) { if (playbackPreparer != null) {
actions |= (PlaybackPreparer.ACTIONS & playbackPreparer.getSupportedPrepareActions()); actions |= (PlaybackPreparer.ACTIONS & playbackPreparer.getSupportedPrepareActions());
} }
if (queueNavigator != null) { if (queueNavigator != null) {
actions |= (QueueNavigator.ACTIONS & queueNavigator.getSupportedQueueNavigatorActions( actions |=
player)); (QueueNavigator.ACTIONS & queueNavigator.getSupportedQueueNavigatorActions(player));
} }
if (ratingCallback != null) { if (ratingCallback != null) {
actions |= RatingCallback.ACTIONS; actions |= RatingCallback.ACTIONS;
...@@ -560,17 +580,76 @@ public final class MediaSessionConnector { ...@@ -560,17 +580,76 @@ public final class MediaSessionConnector {
return actions; return actions;
} }
private void updateMediaSessionMetadata() { private int mapPlaybackState(int exoPlayerPlaybackState, boolean playWhenReady) {
if (doMaintainMetadata) { switch (exoPlayerPlaybackState) {
case Player.STATE_BUFFERING:
return PlaybackStateCompat.STATE_BUFFERING;
case Player.STATE_READY:
return playWhenReady ? PlaybackStateCompat.STATE_PLAYING : PlaybackStateCompat.STATE_PAUSED;
case Player.STATE_ENDED:
return PlaybackStateCompat.STATE_PAUSED;
default:
return PlaybackStateCompat.STATE_NONE;
}
}
private boolean canDispatchToPlaybackPreparer(long action) {
return playbackPreparer != null
&& (playbackPreparer.getSupportedPrepareActions() & PlaybackPreparer.ACTIONS & action) != 0;
}
private boolean canDispatchToRatingCallback(long action) {
return ratingCallback != null && (RatingCallback.ACTIONS & action) != 0;
}
private boolean canDispatchToPlaybackController(long action) {
return (playbackController.getSupportedPlaybackActions(player)
& PlaybackController.ACTIONS
& action)
!= 0;
}
private boolean canDispatchToQueueNavigator(long action) {
return queueNavigator != null
&& (queueNavigator.getSupportedQueueNavigatorActions(player)
& QueueNavigator.ACTIONS
& action)
!= 0;
}
/**
* Provides a default {@link MediaMetadataCompat} with properties and extras propagated from the
* active queue item to the session metadata.
*/
public static final class DefaultMediaMetadataProvider implements MediaMetadataProvider {
private final MediaControllerCompat mediaController;
private final String metadataExtrasPrefix;
/**
* Creates a new instance.
*
* @param mediaController The {@link MediaControllerCompat}.
* @param metadataExtrasPrefix A string to prefix extra keys which are propagated from the
* active queue item to the session metadata.
*/
public DefaultMediaMetadataProvider(
MediaControllerCompat mediaController, @Nullable String metadataExtrasPrefix) {
this.mediaController = mediaController;
this.metadataExtrasPrefix = metadataExtrasPrefix != null ? metadataExtrasPrefix : "";
}
@Override
public MediaMetadataCompat getMetadata(Player player) {
MediaMetadataCompat.Builder builder = new MediaMetadataCompat.Builder(); MediaMetadataCompat.Builder builder = new MediaMetadataCompat.Builder();
if (player != null && player.isPlayingAd()) { if (player != null && player.isPlayingAd()) {
builder.putLong(MediaMetadataCompat.METADATA_KEY_ADVERTISEMENT, 1); builder.putLong(MediaMetadataCompat.METADATA_KEY_ADVERTISEMENT, 1);
} }
builder.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, player == null ? 0 builder.putLong(
: player.getDuration() == C.TIME_UNSET ? -1 : player.getDuration()); MediaMetadataCompat.METADATA_KEY_DURATION,
player == null ? 0 : player.getDuration() == C.TIME_UNSET ? -1 : player.getDuration());
if (queueNavigator != null) { long activeQueueItemId = mediaController.getPlaybackState().getActiveQueueItemId();
long activeQueueItemId = queueNavigator.getActiveQueueItemId(player); if (activeQueueItemId != MediaSessionCompat.QueueItem.UNKNOWN_ID) {
List<MediaSessionCompat.QueueItem> queue = mediaController.getQueue(); List<MediaSessionCompat.QueueItem> queue = mediaController.getQueue();
for (int i = 0; queue != null && i < queue.size(); i++) { for (int i = 0; queue != null && i < queue.size(); i++) {
MediaSessionCompat.QueueItem queueItem = queue.get(i); MediaSessionCompat.QueueItem queueItem = queue.get(i);
...@@ -601,77 +680,50 @@ public final class MediaSessionConnector { ...@@ -601,77 +680,50 @@ public final class MediaSessionConnector {
builder.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, title); builder.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, title);
} }
if (description.getSubtitle() != null) { if (description.getSubtitle() != null) {
builder.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, builder.putString(
MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE,
String.valueOf(description.getSubtitle())); String.valueOf(description.getSubtitle()));
} }
if (description.getDescription() != null) { if (description.getDescription() != null) {
builder.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_DESCRIPTION, builder.putString(
MediaMetadataCompat.METADATA_KEY_DISPLAY_DESCRIPTION,
String.valueOf(description.getDescription())); String.valueOf(description.getDescription()));
} }
if (description.getIconBitmap() != null) { if (description.getIconBitmap() != null) {
builder.putBitmap(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON, builder.putBitmap(
description.getIconBitmap()); MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON, description.getIconBitmap());
} }
if (description.getIconUri() != null) { if (description.getIconUri() != null) {
builder.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI, builder.putString(
MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI,
String.valueOf(description.getIconUri())); String.valueOf(description.getIconUri()));
} }
if (description.getMediaId() != null) { if (description.getMediaId() != null) {
builder.putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, builder.putString(
MediaMetadataCompat.METADATA_KEY_MEDIA_ID,
String.valueOf(description.getMediaId())); String.valueOf(description.getMediaId()));
} }
if (description.getMediaUri() != null) { if (description.getMediaUri() != null) {
builder.putString(MediaMetadataCompat.METADATA_KEY_MEDIA_URI, builder.putString(
MediaMetadataCompat.METADATA_KEY_MEDIA_URI,
String.valueOf(description.getMediaUri())); String.valueOf(description.getMediaUri()));
} }
break; break;
} }
} }
} }
mediaSession.setMetadata(builder.build()); return builder.build();
} }
} }
private int mapPlaybackState(int exoPlayerPlaybackState, boolean playWhenReady) {
switch (exoPlayerPlaybackState) {
case Player.STATE_BUFFERING:
return PlaybackStateCompat.STATE_BUFFERING;
case Player.STATE_READY:
return playWhenReady ? PlaybackStateCompat.STATE_PLAYING : PlaybackStateCompat.STATE_PAUSED;
case Player.STATE_ENDED:
return PlaybackStateCompat.STATE_PAUSED;
default:
return PlaybackStateCompat.STATE_NONE;
}
}
private boolean canDispatchToPlaybackPreparer(long action) {
return playbackPreparer != null && (playbackPreparer.getSupportedPrepareActions()
& PlaybackPreparer.ACTIONS & action) != 0;
}
private boolean canDispatchToRatingCallback(long action) {
return ratingCallback != null && (RatingCallback.ACTIONS & action) != 0;
}
private boolean canDispatchToPlaybackController(long action) {
return (playbackController.getSupportedPlaybackActions(player)
& PlaybackController.ACTIONS & action) != 0;
}
private boolean canDispatchToQueueNavigator(long action) {
return queueNavigator != null && (queueNavigator.getSupportedQueueNavigatorActions(player)
& QueueNavigator.ACTIONS & action) != 0;
}
private class ExoPlayerEventListener extends Player.DefaultEventListener { private class ExoPlayerEventListener extends Player.DefaultEventListener {
private int currentWindowIndex; private int currentWindowIndex;
private int currentWindowCount; private int currentWindowCount;
@Override @Override
public void onTimelineChanged(Timeline timeline, Object manifest, public void onTimelineChanged(
@Player.TimelineChangeReason int reason) { Timeline timeline, Object manifest, @Player.TimelineChangeReason int reason) {
int windowCount = player.getCurrentTimeline().getWindowCount(); int windowCount = player.getCurrentTimeline().getWindowCount();
int windowIndex = player.getCurrentWindowIndex(); int windowIndex = player.getCurrentWindowIndex();
if (queueNavigator != null) { if (queueNavigator != null) {
...@@ -693,16 +745,21 @@ public final class MediaSessionConnector { ...@@ -693,16 +745,21 @@ public final class MediaSessionConnector {
@Override @Override
public void onRepeatModeChanged(@Player.RepeatMode int repeatMode) { public void onRepeatModeChanged(@Player.RepeatMode int repeatMode) {
mediaSession.setRepeatMode(repeatMode == Player.REPEAT_MODE_ONE mediaSession.setRepeatMode(
? PlaybackStateCompat.REPEAT_MODE_ONE : repeatMode == Player.REPEAT_MODE_ALL repeatMode == Player.REPEAT_MODE_ONE
? PlaybackStateCompat.REPEAT_MODE_ALL : PlaybackStateCompat.REPEAT_MODE_NONE); ? PlaybackStateCompat.REPEAT_MODE_ONE
: repeatMode == Player.REPEAT_MODE_ALL
? PlaybackStateCompat.REPEAT_MODE_ALL
: PlaybackStateCompat.REPEAT_MODE_NONE);
updateMediaSessionPlaybackState(); updateMediaSessionPlaybackState();
} }
@Override @Override
public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) { public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
mediaSession.setShuffleMode(shuffleModeEnabled ? PlaybackStateCompat.SHUFFLE_MODE_ALL mediaSession.setShuffleMode(
: PlaybackStateCompat.SHUFFLE_MODE_NONE); shuffleModeEnabled
? PlaybackStateCompat.SHUFFLE_MODE_ALL
: PlaybackStateCompat.SHUFFLE_MODE_NONE);
updateMediaSessionPlaybackState(); updateMediaSessionPlaybackState();
} }
...@@ -713,7 +770,11 @@ public final class MediaSessionConnector { ...@@ -713,7 +770,11 @@ public final class MediaSessionConnector {
queueNavigator.onCurrentWindowIndexChanged(player); queueNavigator.onCurrentWindowIndexChanged(player);
} }
currentWindowIndex = player.getCurrentWindowIndex(); currentWindowIndex = player.getCurrentWindowIndex();
// Update playback state after queueNavigator.onCurrentWindowIndexChanged has been called
// and before updating metadata.
updateMediaSessionPlaybackState();
updateMediaSessionMetadata(); updateMediaSessionMetadata();
return;
} }
updateMediaSessionPlaybackState(); updateMediaSessionPlaybackState();
} }
...@@ -722,7 +783,6 @@ public final class MediaSessionConnector { ...@@ -722,7 +783,6 @@ public final class MediaSessionConnector {
public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) { public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
updateMediaSessionPlaybackState(); updateMediaSessionPlaybackState();
} }
} }
private class MediaSessionCallback extends MediaSessionCompat.Callback { private class MediaSessionCallback extends MediaSessionCompat.Callback {
...@@ -918,7 +978,5 @@ public final class MediaSessionConnector { ...@@ -918,7 +978,5 @@ public final class MediaSessionConnector {
queueEditor.onRemoveQueueItemAt(player, index); queueEditor.onRemoveQueueItemAt(player, index);
} }
} }
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment