diff --git a/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/MediaSessionConnector.java b/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/MediaSessionConnector.java index de14cbf6d7..b4811f040a 100644 --- a/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/MediaSessionConnector.java +++ b/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/MediaSessionConnector.java @@ -67,10 +67,10 @@ import java.util.Map; * *
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. * - *
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); } } diff --git a/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/RepeatModeActionProvider.java b/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/RepeatModeActionProvider.java index b773396198..617b8781f4 100644 --- a/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/RepeatModeActionProvider.java +++ b/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/RepeatModeActionProvider.java @@ -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. */ diff --git a/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/TimelineQueueNavigator.java b/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/TimelineQueueNavigator.java index 5d2b37618f..d0047637dd 100644 --- a/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/TimelineQueueNavigator.java +++ b/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/TimelineQueueNavigator.java @@ -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;