Set MediaSessionConnector components separately

Also support preparation without a player, in line with PlayerView.

PiperOrigin-RevId: 227735260
This commit is contained in:
olly 2019-01-03 21:01:12 +00:00 committed by Oliver Woodman
parent d30375c9a1
commit d834eeab6f
3 changed files with 101 additions and 93 deletions

View File

@ -67,10 +67,10 @@ import java.util.Map;
*
* <ul>
* <li>Actions to initiate media playback ({@code PlaybackStateCompat#ACTION_PREPARE_*} and {@code
* PlaybackStateCompat#ACTION_PLAY_*}) can be handled by a {@link PlaybackPreparer} passed
* when calling {@link #setPlayer(Player, PlaybackPreparer, CustomActionProvider...)}. Custom
* actions can be handled by passing one or more {@link CustomActionProvider}s in a similar
* way.
* PlaybackStateCompat#ACTION_PLAY_*}) can be handled by a {@link PlaybackPreparer} passed to
* {@link #setPlaybackPreparer(PlaybackPreparer)}.
* <li>Custom actions can be handled by passing one or more {@link CustomActionProvider}s to
* {@link #setCustomActionProviders(CustomActionProvider...)}.
* <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 recommended for most use cases.
@ -339,21 +339,21 @@ public final class MediaSessionConnector {
/** The wrapped {@link MediaSessionCompat}. */
public final MediaSessionCompat mediaSession;
@Nullable private final MediaMetadataProvider mediaMetadataProvider;
private final ExoPlayerEventListener exoPlayerEventListener;
private final MediaSessionCallback mediaSessionCallback;
private final Looper looper;
private final ComponentListener componentListener;
private final ArrayList<CommandReceiver> commandReceivers;
private Player player;
private ControlDispatcher controlDispatcher;
private CustomActionProvider[] customActionProviders;
private Map<String, CustomActionProvider> customActionMap;
@Nullable private MediaMetadataProvider mediaMetadataProvider;
@Nullable private Player player;
@Nullable private ErrorMessageProvider<? super ExoPlaybackException> errorMessageProvider;
@Nullable private Pair<Integer, CharSequence> customError;
private PlaybackPreparer playbackPreparer;
private QueueNavigator queueNavigator;
private QueueEditor queueEditor;
private RatingCallback ratingCallback;
@Nullable private PlaybackPreparer playbackPreparer;
@Nullable private QueueNavigator queueNavigator;
@Nullable private QueueEditor queueEditor;
@Nullable private RatingCallback ratingCallback;
private long enabledPlaybackActions;
private int rewindMs;
@ -362,82 +362,60 @@ public final class MediaSessionConnector {
/**
* Creates an instance.
*
* <p>Equivalent to {@code MediaSessionConnector(mediaSession, new
* DefaultMediaMetadataProvider(mediaSession.getController(), null))}.
*
* @param mediaSession The {@link MediaSessionCompat} to connect to.
*/
public MediaSessionConnector(MediaSessionCompat mediaSession) {
this(
mediaSession,
new DefaultMediaMetadataProvider(mediaSession.getController(), null));
}
/**
* Creates an instance.
*
* @param mediaSession The {@link MediaSessionCompat} to connect to.
* @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 MediaMetadataProvider mediaMetadataProvider) {
this.mediaSession = mediaSession;
this.mediaMetadataProvider = mediaMetadataProvider;
mediaSession.setFlags(BASE_MEDIA_SESSION_FLAGS);
mediaSessionCallback = new MediaSessionCallback();
exoPlayerEventListener = new ExoPlayerEventListener();
controlDispatcher = new DefaultControlDispatcher();
customActionMap = Collections.emptyMap();
looper = Util.getLooper();
componentListener = new ComponentListener();
commandReceivers = new ArrayList<>();
controlDispatcher = new DefaultControlDispatcher();
customActionProviders = new CustomActionProvider[0];
customActionMap = Collections.emptyMap();
mediaMetadataProvider =
new DefaultMediaMetadataProvider(
mediaSession.getController(), /* metadataExtrasPrefix= */ null);
enabledPlaybackActions = DEFAULT_PLAYBACK_ACTIONS;
rewindMs = DEFAULT_REWIND_MS;
fastForwardMs = DEFAULT_FAST_FORWARD_MS;
mediaSession.setFlags(BASE_MEDIA_SESSION_FLAGS);
mediaSession.setCallback(componentListener, new Handler(looper));
}
/**
* Sets the player to be connected to the media session. Must be called on the same thread that is
* used to access the player.
*
* <p>The order in which any {@link CustomActionProvider}s are passed determines the order of the
* actions published with the playback state of the session.
*
* @param player The player to be connected to the {@code MediaSession}, or {@code null} to
* disconnect the current player.
* @param playbackPreparer An optional {@link PlaybackPreparer} for preparing the player.
* @param customActionProviders Optional {@link CustomActionProvider}s to publish and handle
* custom actions.
*/
public void setPlayer(
@Nullable Player player,
@Nullable PlaybackPreparer playbackPreparer,
CustomActionProvider... customActionProviders) {
Assertions.checkArgument(player == null || player.getApplicationLooper() == Looper.myLooper());
public void setPlayer(@Nullable Player player) {
Assertions.checkArgument(player == null || player.getApplicationLooper() == looper);
if (this.player != null) {
this.player.removeListener(exoPlayerEventListener);
mediaSession.setCallback(null);
this.player.removeListener(componentListener);
}
unregisterCommandReceiver(this.playbackPreparer);
this.player = player;
this.playbackPreparer = playbackPreparer;
registerCommandReceiver(playbackPreparer);
this.customActionProviders =
(player != null && customActionProviders != null)
? customActionProviders
: new CustomActionProvider[0];
if (player != null) {
Handler handler = new Handler(Util.getLooper());
mediaSession.setCallback(mediaSessionCallback, handler);
player.addListener(exoPlayerEventListener);
player.addListener(componentListener);
}
invalidateMediaSessionPlaybackState();
invalidateMediaSessionMetadata();
}
/**
* Sets the {@link PlaybackPreparer}.
*
* @param playbackPreparer The {@link PlaybackPreparer}.
*/
public void setPlaybackPreparer(@Nullable PlaybackPreparer playbackPreparer) {
if (this.playbackPreparer != playbackPreparer) {
unregisterCommandReceiver(this.playbackPreparer);
this.playbackPreparer = playbackPreparer;
registerCommandReceiver(playbackPreparer);
invalidateMediaSessionPlaybackState();
}
}
/**
* Sets the {@link ControlDispatcher}.
*
@ -570,6 +548,32 @@ public final class MediaSessionConnector {
invalidateMediaSessionPlaybackState();
}
/**
* Sets custom action providers. The order of the {@link CustomActionProvider}s determines the
* order in which the actions are published.
*
* @param customActionProviders The custom action providers, or null to remove all existing custom
* action providers.
*/
public void setCustomActionProviders(@Nullable CustomActionProvider... customActionProviders) {
this.customActionProviders =
customActionProviders == null ? new CustomActionProvider[0] : customActionProviders;
invalidateMediaSessionPlaybackState();
}
/**
* Sets a provider of metadata to be published to the media session.
*
* @param mediaMetadataProvider The provider of metadata to publish, or {@code null} if no
* metadata should be published.
*/
public void setMediaMetadataProvider(@Nullable MediaMetadataProvider mediaMetadataProvider) {
if (this.mediaMetadataProvider != mediaMetadataProvider) {
this.mediaMetadataProvider = mediaMetadataProvider;
invalidateMediaSessionMetadata();
}
}
/**
* Updates the metadata of the media session.
*
@ -577,9 +581,11 @@ public final class MediaSessionConnector {
* changed and the metadata should be updated immediately.
*/
public final void invalidateMediaSessionMetadata() {
if (mediaMetadataProvider != null && player != null) {
mediaSession.setMetadata(mediaMetadataProvider.getMetadata(player));
}
MediaMetadataCompat metadata =
mediaMetadataProvider != null && player != null
? mediaMetadataProvider.getMetadata(player)
: null;
mediaSession.setMetadata(metadata);
}
/**
@ -591,7 +597,7 @@ public final class MediaSessionConnector {
public final void invalidateMediaSessionPlaybackState() {
PlaybackStateCompat.Builder builder = new PlaybackStateCompat.Builder();
if (player == null) {
builder.setActions(/* capabilities= */ 0).setState(PlaybackStateCompat.STATE_NONE, 0, 0, 0);
builder.setActions(buildPrepareActions()).setState(PlaybackStateCompat.STATE_NONE, 0, 0, 0);
mediaSession.setPlaybackState(builder.build());
return;
}
@ -627,7 +633,7 @@ public final class MediaSessionConnector {
Bundle extras = new Bundle();
extras.putFloat(EXTRAS_PITCH, player.getPlaybackParameters().pitch);
builder
.setActions(buildPlaybackActions(player))
.setActions(buildPrepareActions() | buildPlaybackActions(player))
.setActiveQueueItemId(activeQueueItemId)
.setBufferedPosition(player.getBufferedPosition())
.setState(
@ -662,6 +668,12 @@ public final class MediaSessionConnector {
commandReceivers.remove(commandReceiver);
}
private long buildPrepareActions() {
return playbackPreparer == null
? 0
: (PlaybackPreparer.ACTIONS & playbackPreparer.getSupportedPrepareActions());
}
private long buildPlaybackActions(Player player) {
boolean enableSeeking = false;
boolean enableRewind = false;
@ -688,9 +700,6 @@ public final class MediaSessionConnector {
playbackActions &= enabledPlaybackActions;
long actions = playbackActions;
if (playbackPreparer != null) {
actions |= (PlaybackPreparer.ACTIONS & playbackPreparer.getSupportedPrepareActions());
}
if (queueNavigator != null) {
actions |=
(QueueNavigator.ACTIONS & queueNavigator.getSupportedQueueNavigatorActions(player));
@ -719,8 +728,7 @@ public final class MediaSessionConnector {
}
private boolean canDispatchToPlaybackPreparer(long action) {
return player != null
&& playbackPreparer != null
return playbackPreparer != null
&& (playbackPreparer.getSupportedPrepareActions() & action) != 0;
}
@ -738,6 +746,13 @@ public final class MediaSessionConnector {
return player != null && queueEditor != null;
}
private void stopPlayerForPrepare(boolean playWhenReady) {
if (player != null) {
player.stop();
player.setPlayWhenReady(playWhenReady);
}
}
private void rewind(Player player) {
if (player.isCurrentWindowSeekable() && rewindMs > 0) {
seekTo(player, player.getCurrentPosition() - rewindMs);
@ -865,11 +880,14 @@ public final class MediaSessionConnector {
}
}
private class ExoPlayerEventListener implements Player.EventListener {
private class ComponentListener extends MediaSessionCompat.Callback
implements Player.EventListener {
private int currentWindowIndex;
private int currentWindowCount;
// Player.EventListener implementation.
@Override
public void onTimelineChanged(
Timeline timeline, @Nullable Object manifest, @Player.TimelineChangeReason int reason) {
@ -932,9 +950,8 @@ public final class MediaSessionConnector {
public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
invalidateMediaSessionPlaybackState();
}
}
private class MediaSessionCallback extends MediaSessionCompat.Callback {
// MediaSessionCompat.Callback implementation.
@Override
public void onPlay() {
@ -1058,8 +1075,7 @@ public final class MediaSessionConnector {
@Override
public void onPrepare() {
if (canDispatchToPlaybackPreparer(PlaybackStateCompat.ACTION_PREPARE)) {
player.stop();
player.setPlayWhenReady(false);
stopPlayerForPrepare(/* playWhenReady= */ false);
playbackPreparer.onPrepare();
}
}
@ -1067,8 +1083,7 @@ public final class MediaSessionConnector {
@Override
public void onPrepareFromMediaId(String mediaId, Bundle extras) {
if (canDispatchToPlaybackPreparer(PlaybackStateCompat.ACTION_PREPARE_FROM_MEDIA_ID)) {
player.stop();
player.setPlayWhenReady(false);
stopPlayerForPrepare(/* playWhenReady= */ false);
playbackPreparer.onPrepareFromMediaId(mediaId, extras);
}
}
@ -1076,8 +1091,7 @@ public final class MediaSessionConnector {
@Override
public void onPrepareFromSearch(String query, Bundle extras) {
if (canDispatchToPlaybackPreparer(PlaybackStateCompat.ACTION_PREPARE_FROM_SEARCH)) {
player.stop();
player.setPlayWhenReady(false);
stopPlayerForPrepare(/* playWhenReady= */ false);
playbackPreparer.onPrepareFromSearch(query, extras);
}
}
@ -1085,8 +1099,7 @@ public final class MediaSessionConnector {
@Override
public void onPrepareFromUri(Uri uri, Bundle extras) {
if (canDispatchToPlaybackPreparer(PlaybackStateCompat.ACTION_PREPARE_FROM_URI)) {
player.stop();
player.setPlayWhenReady(false);
stopPlayerForPrepare(/* playWhenReady= */ false);
playbackPreparer.onPrepareFromUri(uri, extras);
}
}
@ -1094,8 +1107,7 @@ public final class MediaSessionConnector {
@Override
public void onPlayFromMediaId(String mediaId, Bundle extras) {
if (canDispatchToPlaybackPreparer(PlaybackStateCompat.ACTION_PLAY_FROM_MEDIA_ID)) {
player.stop();
player.setPlayWhenReady(true);
stopPlayerForPrepare(/* playWhenReady= */ true);
playbackPreparer.onPrepareFromMediaId(mediaId, extras);
}
}
@ -1103,8 +1115,7 @@ public final class MediaSessionConnector {
@Override
public void onPlayFromSearch(String query, Bundle extras) {
if (canDispatchToPlaybackPreparer(PlaybackStateCompat.ACTION_PLAY_FROM_SEARCH)) {
player.stop();
player.setPlayWhenReady(true);
stopPlayerForPrepare(/* playWhenReady= */ true);
playbackPreparer.onPrepareFromSearch(query, extras);
}
}
@ -1112,8 +1123,7 @@ public final class MediaSessionConnector {
@Override
public void onPlayFromUri(Uri uri, Bundle extras) {
if (canDispatchToPlaybackPreparer(PlaybackStateCompat.ACTION_PLAY_FROM_URI)) {
player.stop();
player.setPlayWhenReady(true);
stopPlayerForPrepare(/* playWhenReady= */ true);
playbackPreparer.onPrepareFromUri(uri, extras);
}
}

View File

@ -22,9 +22,7 @@ import com.google.android.exoplayer2.ControlDispatcher;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.util.RepeatModeUtil;
/**
* Provides a custom action for toggling repeat modes.
*/
/** Provides a custom action for toggling repeat modes. */
public final class RepeatModeActionProvider implements MediaSessionConnector.CustomActionProvider {
/** The default repeat toggle modes. */

View File

@ -41,7 +41,7 @@ public abstract class TimelineQueueNavigator implements MediaSessionConnector.Qu
private final MediaSessionCompat mediaSession;
private final Timeline.Window window;
protected final int maxQueueSize;
private final int maxQueueSize;
private long activeQueueItemId;