Introduce MediaSessionConnector.CommandReceiver interface and add TimelineQueueEditor.
------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=166475351
This commit is contained in:
parent
1b9c904dba
commit
01f4819844
@ -15,6 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2.ext.mediasession;
|
package com.google.android.exoplayer2.ext.mediasession;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.ResultReceiver;
|
||||||
import android.support.v4.media.session.PlaybackStateCompat;
|
import android.support.v4.media.session.PlaybackStateCompat;
|
||||||
|
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
@ -125,4 +127,14 @@ public class DefaultPlaybackController implements MediaSessionConnector.Playback
|
|||||||
player.stop();
|
player.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getCommands() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCommand(Player player, String command, Bundle extras, ResultReceiver cb) {
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -79,10 +79,24 @@ public final class MediaSessionConnector {
|
|||||||
private static final int EDITOR_MEDIA_SESSION_FLAGS = BASE_MEDIA_SESSION_FLAGS
|
private static final int EDITOR_MEDIA_SESSION_FLAGS = BASE_MEDIA_SESSION_FLAGS
|
||||||
| MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS;
|
| MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receiver of media commands sent by a media controller.
|
||||||
|
*/
|
||||||
|
public interface CommandReceiver {
|
||||||
|
/**
|
||||||
|
* Returns the commands the receiver handles, or {@code null} if no commands need to be handled.
|
||||||
|
*/
|
||||||
|
String[] getCommands();
|
||||||
|
/**
|
||||||
|
* See {@link MediaSessionCompat.Callback#onCommand(String, Bundle, ResultReceiver)}.
|
||||||
|
*/
|
||||||
|
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 {
|
public interface PlaybackPreparer extends CommandReceiver {
|
||||||
|
|
||||||
long ACTIONS = PlaybackStateCompat.ACTION_PREPARE
|
long ACTIONS = PlaybackStateCompat.ACTION_PREPARE
|
||||||
| PlaybackStateCompat.ACTION_PREPARE_FROM_MEDIA_ID
|
| PlaybackStateCompat.ACTION_PREPARE_FROM_MEDIA_ID
|
||||||
@ -121,16 +135,12 @@ public final class MediaSessionConnector {
|
|||||||
* 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);
|
||||||
/**
|
|
||||||
* See {@link MediaSessionCompat.Callback#onCommand(String, Bundle, ResultReceiver)}.
|
|
||||||
*/
|
|
||||||
void onCommand(String command, Bundle extras, ResultReceiver cb);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface to which playback actions are delegated.
|
* Interface to which playback actions are delegated.
|
||||||
*/
|
*/
|
||||||
public interface PlaybackController {
|
public interface PlaybackController extends CommandReceiver {
|
||||||
|
|
||||||
long ACTIONS = PlaybackStateCompat.ACTION_PLAY_PAUSE | PlaybackStateCompat.ACTION_PLAY
|
long ACTIONS = PlaybackStateCompat.ACTION_PLAY_PAUSE | PlaybackStateCompat.ACTION_PLAY
|
||||||
| PlaybackStateCompat.ACTION_PAUSE | PlaybackStateCompat.ACTION_SEEK_TO
|
| PlaybackStateCompat.ACTION_PAUSE | PlaybackStateCompat.ACTION_SEEK_TO
|
||||||
@ -178,7 +188,7 @@ public final class MediaSessionConnector {
|
|||||||
* 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 MediaSessionCompat.setQueue()}.
|
* {@code MediaSessionCompat.setQueue()}.
|
||||||
*/
|
*/
|
||||||
public interface QueueNavigator {
|
public interface QueueNavigator extends CommandReceiver {
|
||||||
|
|
||||||
long ACTIONS = PlaybackStateCompat.ACTION_SKIP_TO_QUEUE_ITEM
|
long ACTIONS = PlaybackStateCompat.ACTION_SKIP_TO_QUEUE_ITEM
|
||||||
| PlaybackStateCompat.ACTION_SKIP_TO_NEXT | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS
|
| PlaybackStateCompat.ACTION_SKIP_TO_NEXT | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS
|
||||||
@ -240,7 +250,7 @@ public final class MediaSessionConnector {
|
|||||||
/**
|
/**
|
||||||
* Handles media session queue edits.
|
* Handles media session queue edits.
|
||||||
*/
|
*/
|
||||||
public interface QueueEditor {
|
public interface QueueEditor extends CommandReceiver {
|
||||||
|
|
||||||
long ACTIONS = PlaybackStateCompat.ACTION_SET_RATING;
|
long ACTIONS = PlaybackStateCompat.ACTION_SET_RATING;
|
||||||
|
|
||||||
@ -309,6 +319,7 @@ public final class MediaSessionConnector {
|
|||||||
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 Map<String, CommandReceiver> commandMap;
|
||||||
|
|
||||||
private Player player;
|
private Player player;
|
||||||
private CustomActionProvider[] customActionProviders;
|
private CustomActionProvider[] customActionProviders;
|
||||||
@ -328,7 +339,7 @@ public final class MediaSessionConnector {
|
|||||||
* @param mediaSession The {@link MediaSessionCompat} to connect to.
|
* @param mediaSession The {@link MediaSessionCompat} to connect to.
|
||||||
*/
|
*/
|
||||||
public MediaSessionConnector(MediaSessionCompat mediaSession) {
|
public MediaSessionConnector(MediaSessionCompat mediaSession) {
|
||||||
this(mediaSession, new DefaultPlaybackController());
|
this(mediaSession, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -350,7 +361,8 @@ public final class MediaSessionConnector {
|
|||||||
* instances passed to {@link #setPlayer(Player, PlaybackPreparer, CustomActionProvider...)}.
|
* instances passed to {@link #setPlayer(Player, PlaybackPreparer, CustomActionProvider...)}.
|
||||||
*
|
*
|
||||||
* @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, or
|
||||||
|
* {@code 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. If
|
||||||
* {@code false}, you need to maintain the metadata of the media session yourself (provide at
|
* {@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).
|
* least the duration to allow clients to show a progress bar).
|
||||||
@ -358,7 +370,8 @@ public final class MediaSessionConnector {
|
|||||||
public MediaSessionConnector(MediaSessionCompat mediaSession,
|
public MediaSessionConnector(MediaSessionCompat mediaSession,
|
||||||
PlaybackController playbackController, boolean doMaintainMetadata) {
|
PlaybackController playbackController, boolean doMaintainMetadata) {
|
||||||
this.mediaSession = mediaSession;
|
this.mediaSession = mediaSession;
|
||||||
this.playbackController = playbackController;
|
this.playbackController = playbackController != null ? playbackController
|
||||||
|
: new DefaultPlaybackController();
|
||||||
this.handler = new Handler(Looper.myLooper() != null ? Looper.myLooper()
|
this.handler = new Handler(Looper.myLooper() != null ? Looper.myLooper()
|
||||||
: Looper.getMainLooper());
|
: Looper.getMainLooper());
|
||||||
this.doMaintainMetadata = doMaintainMetadata;
|
this.doMaintainMetadata = doMaintainMetadata;
|
||||||
@ -367,6 +380,8 @@ public final class MediaSessionConnector {
|
|||||||
mediaSessionCallback = new MediaSessionCallback();
|
mediaSessionCallback = new MediaSessionCallback();
|
||||||
exoPlayerEventListener = new ExoPlayerEventListener();
|
exoPlayerEventListener = new ExoPlayerEventListener();
|
||||||
customActionMap = Collections.emptyMap();
|
customActionMap = Collections.emptyMap();
|
||||||
|
commandMap = new HashMap<>();
|
||||||
|
registerCommandReceiver(playbackController);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -386,8 +401,12 @@ public final class MediaSessionConnector {
|
|||||||
this.player.removeListener(exoPlayerEventListener);
|
this.player.removeListener(exoPlayerEventListener);
|
||||||
mediaSession.setCallback(null);
|
mediaSession.setCallback(null);
|
||||||
}
|
}
|
||||||
this.playbackPreparer = playbackPreparer;
|
unregisterCommandReceiver(this.playbackPreparer);
|
||||||
|
|
||||||
this.player = player;
|
this.player = player;
|
||||||
|
this.playbackPreparer = playbackPreparer;
|
||||||
|
registerCommandReceiver(playbackPreparer);
|
||||||
|
|
||||||
this.customActionProviders = (player != null && customActionProviders != null)
|
this.customActionProviders = (player != null && customActionProviders != null)
|
||||||
? customActionProviders : new CustomActionProvider[0];
|
? customActionProviders : new CustomActionProvider[0];
|
||||||
if (player != null) {
|
if (player != null) {
|
||||||
@ -416,7 +435,9 @@ public final class MediaSessionConnector {
|
|||||||
* @param queueNavigator The queue navigator.
|
* @param queueNavigator The queue navigator.
|
||||||
*/
|
*/
|
||||||
public void setQueueNavigator(QueueNavigator queueNavigator) {
|
public void setQueueNavigator(QueueNavigator queueNavigator) {
|
||||||
|
unregisterCommandReceiver(this.queueNavigator);
|
||||||
this.queueNavigator = queueNavigator;
|
this.queueNavigator = queueNavigator;
|
||||||
|
registerCommandReceiver(queueNavigator);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -425,11 +446,29 @@ public final class MediaSessionConnector {
|
|||||||
* @param queueEditor The queue editor.
|
* @param queueEditor The queue editor.
|
||||||
*/
|
*/
|
||||||
public void setQueueEditor(QueueEditor queueEditor) {
|
public void setQueueEditor(QueueEditor queueEditor) {
|
||||||
|
unregisterCommandReceiver(this.queueEditor);
|
||||||
this.queueEditor = queueEditor;
|
this.queueEditor = queueEditor;
|
||||||
|
registerCommandReceiver(queueEditor);
|
||||||
mediaSession.setFlags(queueEditor == null ? BASE_MEDIA_SESSION_FLAGS
|
mediaSession.setFlags(queueEditor == null ? BASE_MEDIA_SESSION_FLAGS
|
||||||
: EDITOR_MEDIA_SESSION_FLAGS);
|
: EDITOR_MEDIA_SESSION_FLAGS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 void updateMediaSessionPlaybackState() {
|
private void updateMediaSessionPlaybackState() {
|
||||||
PlaybackStateCompat.Builder builder = new PlaybackStateCompat.Builder();
|
PlaybackStateCompat.Builder builder = new PlaybackStateCompat.Builder();
|
||||||
if (player == null) {
|
if (player == null) {
|
||||||
@ -473,11 +512,8 @@ public final class MediaSessionConnector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private long buildPlaybackActions() {
|
private long buildPlaybackActions() {
|
||||||
long actions = 0;
|
long actions = (PlaybackController.ACTIONS
|
||||||
if (playbackController != null) {
|
& playbackController.getSupportedPlaybackActions(player));
|
||||||
actions |= (PlaybackController.ACTIONS & playbackController
|
|
||||||
.getSupportedPlaybackActions(player));
|
|
||||||
}
|
|
||||||
if (playbackPreparer != null) {
|
if (playbackPreparer != null) {
|
||||||
actions |= (PlaybackPreparer.ACTIONS & playbackPreparer.getSupportedPrepareActions());
|
actions |= (PlaybackPreparer.ACTIONS & playbackPreparer.getSupportedPrepareActions());
|
||||||
}
|
}
|
||||||
@ -562,7 +598,7 @@ public final class MediaSessionConnector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean canDispatchToPlaybackController(long action) {
|
private boolean canDispatchToPlaybackController(long action) {
|
||||||
return playbackController != null && (playbackController.getSupportedPlaybackActions(player)
|
return (playbackController.getSupportedPlaybackActions(player)
|
||||||
& PlaybackController.ACTIONS & action) != 0;
|
& PlaybackController.ACTIONS & action) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -583,10 +619,15 @@ public final class MediaSessionConnector {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTimelineChanged(Timeline timeline, Object manifest) {
|
public void onTimelineChanged(Timeline timeline, Object manifest) {
|
||||||
|
int windowCount = player.getCurrentTimeline().getWindowCount();
|
||||||
|
int windowIndex = player.getCurrentWindowIndex();
|
||||||
if (queueNavigator != null) {
|
if (queueNavigator != null) {
|
||||||
queueNavigator.onTimelineChanged(player);
|
queueNavigator.onTimelineChanged(player);
|
||||||
|
updateMediaSessionPlaybackState();
|
||||||
|
} else if (currentWindowCount != windowCount || currentWindowIndex != windowIndex) {
|
||||||
|
// active queue item and queue navigation actions may need to be updated
|
||||||
|
updateMediaSessionPlaybackState();
|
||||||
}
|
}
|
||||||
int windowCount = player.getCurrentTimeline().getWindowCount();
|
|
||||||
if (currentWindowCount != windowCount) {
|
if (currentWindowCount != windowCount) {
|
||||||
// active queue item and queue navigation actions may need to be updated
|
// active queue item and queue navigation actions may need to be updated
|
||||||
updateMediaSessionPlaybackState();
|
updateMediaSessionPlaybackState();
|
||||||
@ -638,8 +679,8 @@ public final class MediaSessionConnector {
|
|||||||
if (queueNavigator != null) {
|
if (queueNavigator != null) {
|
||||||
queueNavigator.onCurrentWindowIndexChanged(player);
|
queueNavigator.onCurrentWindowIndexChanged(player);
|
||||||
}
|
}
|
||||||
updateMediaSessionMetadata();
|
|
||||||
currentWindowIndex = player.getCurrentWindowIndex();
|
currentWindowIndex = player.getCurrentWindowIndex();
|
||||||
|
updateMediaSessionMetadata();
|
||||||
}
|
}
|
||||||
updateMediaSessionPlaybackState();
|
updateMediaSessionPlaybackState();
|
||||||
}
|
}
|
||||||
@ -732,8 +773,9 @@ public final class MediaSessionConnector {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCommand(String command, Bundle extras, ResultReceiver cb) {
|
public void onCommand(String command, Bundle extras, ResultReceiver cb) {
|
||||||
if (playbackPreparer != null) {
|
CommandReceiver commandReceiver = commandMap.get(command);
|
||||||
playbackPreparer.onCommand(command, extras, cb);
|
if (commandReceiver != null) {
|
||||||
|
commandReceiver.onCommand(player, command, extras, cb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,226 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.google.android.exoplayer2.ext.mediasession;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.ResultReceiver;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v4.media.MediaDescriptionCompat;
|
||||||
|
import android.support.v4.media.RatingCompat;
|
||||||
|
import android.support.v4.media.session.MediaControllerCompat;
|
||||||
|
import android.support.v4.media.session.MediaSessionCompat;
|
||||||
|
import com.google.android.exoplayer2.C;
|
||||||
|
import com.google.android.exoplayer2.Player;
|
||||||
|
import com.google.android.exoplayer2.source.DynamicConcatenatingMediaSource;
|
||||||
|
import com.google.android.exoplayer2.source.MediaSource;
|
||||||
|
import com.google.android.exoplayer2.util.Util;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link MediaSessionConnector.QueueEditor} implementation based on the
|
||||||
|
* {@link DynamicConcatenatingMediaSource}.
|
||||||
|
* <p>
|
||||||
|
* This class implements the {@link MediaSessionConnector.CommandReceiver} interface and handles
|
||||||
|
* the {@link #COMMAND_MOVE_QUEUE_ITEM} to move a queue item instead of removing and inserting it.
|
||||||
|
* This allows to move the currently playing window without interrupting playback.
|
||||||
|
*/
|
||||||
|
public final class TimelineQueueEditor implements MediaSessionConnector.QueueEditor,
|
||||||
|
MediaSessionConnector.CommandReceiver {
|
||||||
|
|
||||||
|
public static final String COMMAND_MOVE_QUEUE_ITEM = "exo_move_window";
|
||||||
|
public static final String EXTRA_FROM_INDEX = "from_index";
|
||||||
|
public static final String EXTRA_TO_INDEX = "to_index";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory to create {@link MediaSource}s.
|
||||||
|
*/
|
||||||
|
public interface MediaSourceFactory {
|
||||||
|
/**
|
||||||
|
* Creates a {@link MediaSource} for the given {@link MediaDescriptionCompat}.
|
||||||
|
*
|
||||||
|
* @param description The {@link MediaDescriptionCompat} to create a media source for.
|
||||||
|
* @return A {@link MediaSource} or {@code null} if no source can be created for the given
|
||||||
|
* description.
|
||||||
|
*/
|
||||||
|
@Nullable MediaSource createMediaSource(MediaDescriptionCompat description);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapter to get {@link MediaDescriptionCompat} of items in the queue and to notify the
|
||||||
|
* application about changes in the queue to sync the data structure backing the
|
||||||
|
* {@link MediaSessionConnector}.
|
||||||
|
*/
|
||||||
|
public interface QueueDataAdapter {
|
||||||
|
/**
|
||||||
|
* Gets the {@link MediaDescriptionCompat} for a {@code position}.
|
||||||
|
*
|
||||||
|
* @param position The position in the queue for which to provide a description.
|
||||||
|
* @return A {@link MediaDescriptionCompat}.
|
||||||
|
*/
|
||||||
|
MediaDescriptionCompat getMediaDescription(int position);
|
||||||
|
/**
|
||||||
|
* Adds a {@link MediaDescriptionCompat} at the given {@code position}.
|
||||||
|
*
|
||||||
|
* @param position The position at which to add.
|
||||||
|
* @param description The {@link MediaDescriptionCompat} to be added.
|
||||||
|
*/
|
||||||
|
void add(int position, MediaDescriptionCompat description);
|
||||||
|
/**
|
||||||
|
* Removes the item at the given {@code position}.
|
||||||
|
*
|
||||||
|
* @param position The position at which to remove the item.
|
||||||
|
*/
|
||||||
|
void remove(int position);
|
||||||
|
/**
|
||||||
|
* Moves a queue item from position {@code from} to position {@code to}.
|
||||||
|
*
|
||||||
|
* @param from The position from which to remove the item.
|
||||||
|
* @param to The target position to which to move the item.
|
||||||
|
*/
|
||||||
|
void move(int from, int to);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to evaluate whether two {@link MediaDescriptionCompat} are considered equal.
|
||||||
|
*/
|
||||||
|
interface MediaDescriptionEqualityChecker {
|
||||||
|
/**
|
||||||
|
* Returns {@code true} whether the descriptions are considered equal.
|
||||||
|
*
|
||||||
|
* @param d1 The first {@link MediaDescriptionCompat}.
|
||||||
|
* @param d2 The second {@link MediaDescriptionCompat}.
|
||||||
|
* @return {@code true} if considered equal.
|
||||||
|
*/
|
||||||
|
boolean equals(MediaDescriptionCompat d1, MediaDescriptionCompat d2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Media description comparator comparing the media IDs. Media IDs are considered equals if both
|
||||||
|
* are {@code null}.
|
||||||
|
*/
|
||||||
|
public static final class MediaIdEqualityChecker implements MediaDescriptionEqualityChecker {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(MediaDescriptionCompat d1, MediaDescriptionCompat d2) {
|
||||||
|
return Util.areEqual(d1.getMediaId(), d2.getMediaId());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private final MediaControllerCompat mediaController;
|
||||||
|
private final QueueDataAdapter queueDataAdapter;
|
||||||
|
private final MediaSourceFactory sourceFactory;
|
||||||
|
private final MediaDescriptionEqualityChecker equalityChecker;
|
||||||
|
private final DynamicConcatenatingMediaSource queueMediaSource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link TimelineQueueEditor} with a given mediaSourceFactory.
|
||||||
|
*
|
||||||
|
* @param mediaController A {@link MediaControllerCompat} to read the current queue.
|
||||||
|
* @param queueMediaSource The {@link DynamicConcatenatingMediaSource} to
|
||||||
|
* manipulate.
|
||||||
|
* @param queueDataAdapter A {@link QueueDataAdapter} to change the backing data.
|
||||||
|
* @param sourceFactory The {@link MediaSourceFactory} to build media sources.
|
||||||
|
*/
|
||||||
|
public TimelineQueueEditor(@NonNull MediaControllerCompat mediaController,
|
||||||
|
@NonNull DynamicConcatenatingMediaSource queueMediaSource,
|
||||||
|
@NonNull QueueDataAdapter queueDataAdapter, @NonNull MediaSourceFactory sourceFactory) {
|
||||||
|
this(mediaController, queueMediaSource, queueDataAdapter, sourceFactory,
|
||||||
|
new MediaIdEqualityChecker());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link TimelineQueueEditor} with a given mediaSourceFactory.
|
||||||
|
*
|
||||||
|
* @param mediaController A {@link MediaControllerCompat} to read the current queue.
|
||||||
|
* @param queueMediaSource The {@link DynamicConcatenatingMediaSource} to
|
||||||
|
* manipulate.
|
||||||
|
* @param queueDataAdapter A {@link QueueDataAdapter} to change the backing data.
|
||||||
|
* @param sourceFactory The {@link MediaSourceFactory} to build media sources.
|
||||||
|
* @param equalityChecker The {@link MediaDescriptionEqualityChecker} to match queue items.
|
||||||
|
*/
|
||||||
|
public TimelineQueueEditor(@NonNull MediaControllerCompat mediaController,
|
||||||
|
@NonNull DynamicConcatenatingMediaSource queueMediaSource,
|
||||||
|
@NonNull QueueDataAdapter queueDataAdapter, @NonNull MediaSourceFactory sourceFactory,
|
||||||
|
@NonNull MediaDescriptionEqualityChecker equalityChecker) {
|
||||||
|
this.mediaController = mediaController;
|
||||||
|
this.queueMediaSource = queueMediaSource;
|
||||||
|
this.queueDataAdapter = queueDataAdapter;
|
||||||
|
this.sourceFactory = sourceFactory;
|
||||||
|
this.equalityChecker = equalityChecker;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getSupportedQueueEditorActions(@Nullable Player player) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAddQueueItem(Player player, MediaDescriptionCompat description) {
|
||||||
|
onAddQueueItem(player, description, player.getCurrentTimeline().getWindowCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAddQueueItem(Player player, MediaDescriptionCompat description, int index) {
|
||||||
|
MediaSource mediaSource = sourceFactory.createMediaSource(description);
|
||||||
|
if (mediaSource != null) {
|
||||||
|
queueDataAdapter.add(index, description);
|
||||||
|
queueMediaSource.addMediaSource(index, mediaSource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRemoveQueueItem(Player player, MediaDescriptionCompat description) {
|
||||||
|
List<MediaSessionCompat.QueueItem> queue = mediaController.getQueue();
|
||||||
|
for (int i = 0; i < queue.size(); i++) {
|
||||||
|
if (equalityChecker.equals(queue.get(i).getDescription(), description)) {
|
||||||
|
onRemoveQueueItemAt(player, i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRemoveQueueItemAt(Player player, int index) {
|
||||||
|
queueDataAdapter.remove(index);
|
||||||
|
queueMediaSource.removeMediaSource(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSetRating(Player player, RatingCompat rating) {
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
|
||||||
|
// CommandReceiver implementation.
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public String[] getCommands() {
|
||||||
|
return new String[] {COMMAND_MOVE_QUEUE_ITEM};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCommand(Player player, String command, Bundle extras, ResultReceiver cb) {
|
||||||
|
int from = extras.getInt(EXTRA_FROM_INDEX, C.INDEX_UNSET);
|
||||||
|
int to = extras.getInt(EXTRA_TO_INDEX, C.INDEX_UNSET);
|
||||||
|
if (from != C.INDEX_UNSET && to != C.INDEX_UNSET) {
|
||||||
|
queueDataAdapter.move(from, to);
|
||||||
|
queueMediaSource.moveMediaSource(from, to);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -15,6 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2.ext.mediasession;
|
package com.google.android.exoplayer2.ext.mediasession;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.ResultReceiver;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.v4.media.MediaDescriptionCompat;
|
import android.support.v4.media.MediaDescriptionCompat;
|
||||||
import android.support.v4.media.session.MediaSessionCompat;
|
import android.support.v4.media.session.MediaSessionCompat;
|
||||||
@ -164,6 +166,18 @@ public abstract class TimelineQueueNavigator implements MediaSessionConnector.Qu
|
|||||||
player.setShuffleModeEnabled(shuffleMode == PlaybackStateCompat.SHUFFLE_MODE_ALL);
|
player.setShuffleModeEnabled(shuffleMode == PlaybackStateCompat.SHUFFLE_MODE_ALL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CommandReceiver implementation.
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getCommands() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCommand(Player player, String command, Bundle extras, ResultReceiver cb) {
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
|
||||||
private void publishFloatingQueueWindow(Player player) {
|
private void publishFloatingQueueWindow(Player player) {
|
||||||
if (player.getCurrentTimeline().isEmpty()) {
|
if (player.getCurrentTimeline().isEmpty()) {
|
||||||
mediaSession.setQueue(Collections.<MediaSessionCompat.QueueItem>emptyList());
|
mediaSession.setQueue(Collections.<MediaSessionCompat.QueueItem>emptyList());
|
||||||
|
Loading…
x
Reference in New Issue
Block a user