[go: nahoru, domu]

blob: 417ab1b4ef420e3c1953c3bf082a44b2e8834ba4 [file] [log] [blame]
/*
* Copyright 2019 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 androidx.media2.session;
import static androidx.media2.common.MediaMetadata.METADATA_KEY_DISPLAY_TITLE;
import static androidx.media2.common.MediaMetadata.METADATA_KEY_TITLE;
import static androidx.media2.session.MediaUtils.TRANSACTION_SIZE_LIMIT_IN_BYTES;
import static androidx.media2.session.SessionCommand.COMMAND_VERSION_CURRENT;
import static androidx.media2.session.SessionResult.RESULT_SUCCESS;
import android.content.Context;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.support.v4.media.MediaDescriptionCompat;
import android.support.v4.media.RatingCompat;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.MediaSessionCompat.QueueItem;
import android.support.v4.media.session.PlaybackStateCompat;
import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.util.ObjectsCompat;
import androidx.media.MediaSessionManager;
import androidx.media.MediaSessionManager.RemoteUserInfo;
import androidx.media2.common.MediaItem;
import androidx.media2.common.MediaMetadata;
import androidx.media2.common.SessionPlayer.PlayerResult;
import androidx.media2.common.SessionPlayer.TrackInfo;
import androidx.media2.common.SubtitleData;
import androidx.media2.common.VideoSize;
import androidx.media2.session.MediaController.PlaybackInfo;
import androidx.media2.session.MediaLibraryService.LibraryParams;
import androidx.media2.session.MediaSession.CommandButton;
import androidx.media2.session.MediaSession.ControllerInfo;
import androidx.media2.session.SessionCommand.CommandCode;
import java.util.List;
import java.util.Set;
// Getting the commands from MediaControllerCompat'
class MediaSessionLegacyStub extends MediaSessionCompat.Callback {
private static final String TAG = "MediaSessionLegacyStub";
static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@SuppressWarnings("WeakerAccess") /* synthetic access */
static final SparseArray<SessionCommand> sCommandsForOnCommandRequest =
new SparseArray<>();
static {
SessionCommandGroup group = new SessionCommandGroup.Builder()
.addAllPlayerCommands(COMMAND_VERSION_CURRENT)
.addAllVolumeCommands(COMMAND_VERSION_CURRENT)
.build();
Set<SessionCommand> commands = group.getCommands();
for (SessionCommand command : commands) {
sCommandsForOnCommandRequest.append(command.getCommandCode(), command);
}
}
final ConnectedControllersManager<RemoteUserInfo> mConnectedControllersManager;
final Object mLock = new Object();
final MediaSession.MediaSessionImpl mSessionImpl;
final MediaSessionManager mSessionManager;
final Context mContext;
final ControllerInfo mControllerInfoForAll;
MediaSessionLegacyStub(MediaSession.MediaSessionImpl session) {
mSessionImpl = session;
mContext = mSessionImpl.getContext();
mSessionManager = MediaSessionManager.getSessionManager(mContext);
mControllerInfoForAll = new ControllerInfo(
new RemoteUserInfo(
RemoteUserInfo.LEGACY_CONTROLLER, Process.myPid(), Process.myUid()),
MediaUtils.VERSION_UNKNOWN, false /* trusted */,
new ControllerLegacyCbForAll(), null /* connectionHints */);
mConnectedControllersManager = new ConnectedControllersManager<>(session);
}
@Override
public void onCommand(final String commandName, final Bundle args, final ResultReceiver cb) {
if (commandName == null) {
return;
}
final SessionCommand command = new SessionCommand(commandName, null);
dispatchSessionTask(command, new SessionTask() {
@Override
public void run(final ControllerInfo controller) throws RemoteException {
SessionResult result = mSessionImpl.getCallback().onCustomCommand(
mSessionImpl.getInstance(), controller, command, args);
if (cb != null) {
cb.send(result.getResultCode(), result.getCustomCommandResult());
}
}
});
}
@Override
public void onPrepare() {
dispatchSessionTask(SessionCommand.COMMAND_CODE_PLAYER_PREPARE, new SessionTask() {
// TODO(b/138091975) Do not ignore the returned Future.
@SuppressWarnings("FutureReturnValueIgnored")
@Override
public void run(ControllerInfo controller) throws RemoteException {
mSessionImpl.prepare();
}
});
}
@Override
public void onPrepareFromMediaId(final String mediaId, final Bundle extras) {
dispatchSessionTask(SessionCommand.COMMAND_CODE_SESSION_PREPARE_FROM_MEDIA_ID,
new SessionTask() {
@Override
public void run(ControllerInfo controller) throws RemoteException {
if (TextUtils.isEmpty(mediaId)) {
Log.w(TAG, "onPrepareFromMediaId(): Ignoring empty mediaId from "
+ controller);
return;
}
mSessionImpl.getCallback().onPrepareFromMediaId(mSessionImpl.getInstance(),
controller, mediaId, extras);
}
});
}
@Override
public void onPrepareFromSearch(final String query, final Bundle extras) {
dispatchSessionTask(SessionCommand.COMMAND_CODE_SESSION_PREPARE_FROM_SEARCH,
new SessionTask() {
@Override
public void run(ControllerInfo controller) throws RemoteException {
if (TextUtils.isEmpty(query)) {
Log.w(TAG, "onPrepareFromSearch(): Ignoring empty query from "
+ controller);
return;
}
mSessionImpl.getCallback().onPrepareFromSearch(mSessionImpl.getInstance(),
controller, query, extras);
}
});
}
@Override
public void onPrepareFromUri(final Uri uri, final Bundle extras) {
if (uri == null) {
return;
}
dispatchSessionTask(SessionCommand.COMMAND_CODE_SESSION_PREPARE_FROM_URI,
new SessionTask() {
@Override
public void run(ControllerInfo controller) throws RemoteException {
mSessionImpl.getCallback().onPrepareFromUri(mSessionImpl.getInstance(),
controller, uri, extras);
}
});
}
@Override
public void onPlay() {
dispatchSessionTask(SessionCommand.COMMAND_CODE_PLAYER_PLAY, new SessionTask() {
// TODO(b/138091975) Do not ignore the returned Future.
@SuppressWarnings("FutureReturnValueIgnored")
@Override
public void run(ControllerInfo controller) throws RemoteException {
mSessionImpl.play();
}
});
}
@Override
public void onPlayFromMediaId(final String mediaId, final Bundle extras) {
dispatchSessionTask(SessionCommand.COMMAND_CODE_SESSION_PLAY_FROM_MEDIA_ID,
new SessionTask() {
@Override
public void run(ControllerInfo controller) throws RemoteException {
if (TextUtils.isEmpty(mediaId)) {
Log.w(TAG, "onPlayFromMediaId(): Ignoring empty mediaId from "
+ controller);
return;
}
mSessionImpl.getCallback().onPlayFromMediaId(mSessionImpl.getInstance(),
controller, mediaId, extras);
}
});
}
@Override
public void onPlayFromSearch(final String query, final Bundle extras) {
dispatchSessionTask(SessionCommand.COMMAND_CODE_SESSION_PLAY_FROM_SEARCH,
new SessionTask() {
@Override
public void run(ControllerInfo controller) throws RemoteException {
if (TextUtils.isEmpty(query)) {
Log.w(TAG, "onPlayFromSearch(): Ignoring empty query from "
+ controller);
return;
}
mSessionImpl.getCallback().onPlayFromSearch(mSessionImpl.getInstance(),
controller, query, extras);
}
});
}
@Override
public void onPlayFromUri(final Uri uri, final Bundle extras) {
if (uri == null) {
return;
}
dispatchSessionTask(SessionCommand.COMMAND_CODE_SESSION_PLAY_FROM_URI,
new SessionTask() {
@Override
public void run(ControllerInfo controller) throws RemoteException {
mSessionImpl.getCallback().onPlayFromUri(mSessionImpl.getInstance(),
controller, uri, extras);
}
});
}
@Override
public void onPause() {
dispatchSessionTask(SessionCommand.COMMAND_CODE_PLAYER_PAUSE, new SessionTask() {
// TODO(b/138091975) Do not ignore the returned Future.
@SuppressWarnings("FutureReturnValueIgnored")
@Override
public void run(ControllerInfo controller) throws RemoteException {
mSessionImpl.pause();
}
});
}
@Override
public void onStop() {
// Here, we don't call SessionPlayer#reset() since it may result removing
// all callbacks from the player. Instead, we pause and seek to zero.
// Here, we check both permissions: Pause / SeekTo.
dispatchSessionTask(SessionCommand.COMMAND_CODE_PLAYER_PAUSE, new SessionTask() {
@Override
public void run(ControllerInfo controller) throws RemoteException {
handleTaskOnExecutor(controller, null,
SessionCommand.COMMAND_CODE_PLAYER_SEEK_TO, new SessionTask() {
// TODO(b/138091975) Do not ignore the returned Future.
@SuppressWarnings("FutureReturnValueIgnored")
@Override
public void run(ControllerInfo controller) throws RemoteException {
mSessionImpl.pause();
mSessionImpl.seekTo(0);
}
});
}
});
}
@Override
public void onSeekTo(final long pos) {
dispatchSessionTask(SessionCommand.COMMAND_CODE_PLAYER_SEEK_TO, new SessionTask() {
// TODO(b/138091975) Do not ignore the returned Future.
@SuppressWarnings("FutureReturnValueIgnored")
@Override
public void run(ControllerInfo controller) throws RemoteException {
mSessionImpl.seekTo(pos);
}
});
}
@Override
public void onSkipToNext() {
dispatchSessionTask(SessionCommand.COMMAND_CODE_PLAYER_SKIP_TO_NEXT_PLAYLIST_ITEM,
new SessionTask() {
// TODO(b/138091975) Do not ignore the returned Future.
@SuppressWarnings("FutureReturnValueIgnored")
@Override
public void run(ControllerInfo controller) throws RemoteException {
mSessionImpl.skipToNextItem();
}
});
}
@Override
public void onSkipToPrevious() {
dispatchSessionTask(SessionCommand.COMMAND_CODE_PLAYER_SKIP_TO_PREVIOUS_PLAYLIST_ITEM,
new SessionTask() {
// TODO(b/138091975) Do not ignore the returned Future.
@SuppressWarnings("FutureReturnValueIgnored")
@Override
public void run(ControllerInfo controller) throws RemoteException {
mSessionImpl.skipToPreviousItem();
}
});
}
@Override
public void onSetPlaybackSpeed(final float speed) {
dispatchSessionTask(SessionCommand.COMMAND_CODE_PLAYER_SET_SPEED, new SessionTask() {
// TODO(b/138091975) Do not ignore the returned Future.
@SuppressWarnings("FutureReturnValueIgnored")
@Override
public void run(ControllerInfo controller) throws RemoteException {
mSessionImpl.setPlaybackSpeed(speed);
}
});
}
@Override
public void onSkipToQueueItem(final long queueId) {
dispatchSessionTask(SessionCommand.COMMAND_CODE_PLAYER_SKIP_TO_PLAYLIST_ITEM,
new SessionTask() {
// TODO(b/138091975) Do not ignore the returned Future.
@SuppressWarnings("FutureReturnValueIgnored")
@Override
public void run(ControllerInfo controller) throws RemoteException {
List<MediaItem> playlist = mSessionImpl.getPlayer().getPlaylist();
if (playlist == null) {
return;
}
// Use queueId as an index as we've published {@link QueueItem} as so.
// see: {@link MediaUtils#convertToQueueItemList}.
mSessionImpl.skipToPlaylistItem((int) queueId);
}
});
}
@Override
public void onFastForward() {
dispatchSessionTask(SessionCommand.COMMAND_CODE_SESSION_FAST_FORWARD,
new SessionTask() {
@Override
public void run(ControllerInfo controller) throws RemoteException {
mSessionImpl.getCallback().onFastForward(
mSessionImpl.getInstance(), controller);
}
});
}
@Override
public void onRewind() {
dispatchSessionTask(SessionCommand.COMMAND_CODE_SESSION_REWIND,
new SessionTask() {
@Override
public void run(ControllerInfo controller) throws RemoteException {
mSessionImpl.getCallback().onRewind(mSessionImpl.getInstance(), controller);
}
});
}
@Override
public void onSetRating(final RatingCompat rating) {
onSetRating(rating, null);
}
@Override
public void onSetRating(final RatingCompat rating, Bundle extras) {
if (rating == null) {
return;
}
// extras is ignored.
dispatchSessionTask(SessionCommand.COMMAND_CODE_SESSION_SET_RATING,
new SessionTask() {
@Override
public void run(ControllerInfo controller) throws RemoteException {
MediaItem currentItem = mSessionImpl.getCurrentMediaItem();
if (currentItem == null) {
return;
}
mSessionImpl.getCallback().onSetRating(mSessionImpl.getInstance(),
controller, currentItem.getMediaId(),
MediaUtils.convertToRating(rating));
}
});
}
@Override
public void onCustomAction(@NonNull String action, @Nullable Bundle extras) {
// no-op
}
@Override
public void onSetCaptioningEnabled(boolean enabled) {
// no-op
}
@Override
public void onSetRepeatMode(final int repeatMode) {
dispatchSessionTask(SessionCommand.COMMAND_CODE_PLAYER_SET_REPEAT_MODE,
new SessionTask() {
// TODO(b/138091975) Do not ignore the returned Future.
@SuppressWarnings("FutureReturnValueIgnored")
@Override
public void run(ControllerInfo controller) throws RemoteException {
mSessionImpl.setRepeatMode(repeatMode);
}
});
}
@Override
public void onSetShuffleMode(final int shuffleMode) {
dispatchSessionTask(SessionCommand.COMMAND_CODE_PLAYER_SET_SHUFFLE_MODE,
new SessionTask() {
// TODO(b/138091975) Do not ignore the returned Future.
@SuppressWarnings("FutureReturnValueIgnored")
@Override
public void run(ControllerInfo controller) throws RemoteException {
mSessionImpl.setShuffleMode(shuffleMode);
}
});
}
@Override
public void onAddQueueItem(final MediaDescriptionCompat description) {
onAddQueueItem(description, Integer.MAX_VALUE);
}
@Override
public void onAddQueueItem(final MediaDescriptionCompat description, final int index) {
if (description == null) {
return;
}
dispatchSessionTask(SessionCommand.COMMAND_CODE_PLAYER_ADD_PLAYLIST_ITEM,
new SessionTask() {
// TODO(b/138091975) Do not ignore the returned Future.
@SuppressWarnings("FutureReturnValueIgnored")
@Override
public void run(ControllerInfo controller) throws RemoteException {
String mediaId = description.getMediaId();
if (TextUtils.isEmpty(mediaId)) {
Log.w(TAG, "onAddQueueItem(): Media ID shouldn't be empty");
return;
}
MediaItem newItem = mSessionImpl.getCallback().onCreateMediaItem(
mSessionImpl.getInstance(), controller, mediaId);
mSessionImpl.addPlaylistItem(index, newItem);
}
});
}
@Override
public void onRemoveQueueItem(final MediaDescriptionCompat description) {
if (description == null) {
return;
}
dispatchSessionTask(SessionCommand.COMMAND_CODE_PLAYER_REMOVE_PLAYLIST_ITEM,
new SessionTask() {
// TODO(b/138091975) Do not ignore the returned Future.
@SuppressWarnings("FutureReturnValueIgnored")
@Override
public void run(ControllerInfo controller) throws RemoteException {
String mediaId = description.getMediaId();
if (TextUtils.isEmpty(mediaId)) {
Log.w(TAG, "onRemoveQueueItem(): Media ID shouldn't be null");
return;
}
List<MediaItem> playlist = mSessionImpl.getPlaylist();
for (int i = 0; i < playlist.size(); i++) {
MediaItem item = playlist.get(i);
if (TextUtils.equals(item.getMediaId(), mediaId)) {
mSessionImpl.removePlaylistItem(i);
return;
}
}
}
});
}
@Override
public void onRemoveQueueItemAt(final int index) {
dispatchSessionTask(SessionCommand.COMMAND_CODE_PLAYER_REMOVE_PLAYLIST_ITEM,
new SessionTask() {
// TODO(b/138091975) Do not ignore the returned Future.
@SuppressWarnings("FutureReturnValueIgnored")
@Override
public void run(ControllerInfo controller) throws RemoteException {
if (index < 0) {
Log.w(TAG, "onRemoveQueueItem(): index shouldn't be negative");
return;
}
mSessionImpl.removePlaylistItem(index);
}
});
}
ControllerInfo getControllersForAll() {
return mControllerInfoForAll;
}
ConnectedControllersManager<RemoteUserInfo> getConnectedControllersManager() {
return mConnectedControllersManager;
}
private void dispatchSessionTask(@CommandCode final int commandCode,
@NonNull final SessionTask task) {
dispatchSessionTaskInternal(null, commandCode, task);
}
private void dispatchSessionTask(@NonNull final SessionCommand sessionCommand,
@NonNull final SessionTask task) {
dispatchSessionTaskInternal(sessionCommand, SessionCommand.COMMAND_CODE_CUSTOM, task);
}
private void dispatchSessionTaskInternal(@Nullable final SessionCommand sessionCommand,
@CommandCode final int commandCode, @NonNull final SessionTask task) {
if (mSessionImpl.isClosed()) {
return;
}
final RemoteUserInfo remoteUserInfo =
mSessionImpl.getSessionCompat().getCurrentControllerInfo();
if (remoteUserInfo == null) {
Log.d(TAG, "RemoteUserInfo is null, ignoring command=" + sessionCommand
+ ", commandCode=" + commandCode);
return;
}
mSessionImpl.getCallbackExecutor().execute(new Runnable() {
@Override
public void run() {
if (mSessionImpl.isClosed()) {
return;
}
final ControllerInfo controller;
ControllerInfo ctrl = mConnectedControllersManager.getController(remoteUserInfo);
if (ctrl != null) {
controller = ctrl;
} else {
controller = new ControllerInfo(
remoteUserInfo, MediaUtils.VERSION_UNKNOWN,
mSessionManager.isTrustedForMediaControl(remoteUserInfo),
new ControllerLegacyCb(remoteUserInfo), null /* connectionHints */);
}
if (!mConnectedControllersManager.isConnected(controller)) {
SessionCommandGroup allowedCommands = mSessionImpl.getCallback().onConnect(
mSessionImpl.getInstance(), controller);
if (allowedCommands == null) {
try {
controller.getControllerCb().onDisconnected(0);
} catch (RemoteException ex) {
// Controller may have died prematurely.
}
return;
}
mConnectedControllersManager.addController(
controller.getRemoteUserInfo(), controller, allowedCommands);
}
handleTaskOnExecutor(controller, sessionCommand, commandCode, task);
}
});
}
@SuppressWarnings("WeakerAccess") /* synthetic access */
void handleTaskOnExecutor(@NonNull final ControllerInfo controller,
@Nullable final SessionCommand sessionCommand, @CommandCode final int commandCode,
@NonNull final SessionTask task) {
SessionCommand command;
if (sessionCommand != null) {
if (!mConnectedControllersManager.isAllowedCommand(controller, sessionCommand)) {
return;
}
command = sCommandsForOnCommandRequest.get(sessionCommand.getCommandCode());
} else {
if (!mConnectedControllersManager.isAllowedCommand(controller, commandCode)) {
return;
}
command = sCommandsForOnCommandRequest.get(commandCode);
}
if (command != null) {
int resultCode = mSessionImpl.getCallback().onCommandRequest(
mSessionImpl.getInstance(), controller, command);
if (resultCode != RESULT_SUCCESS) {
// Don't run rejected command.
if (DEBUG) {
Log.d(TAG, "Command (" + command + ") from "
+ controller + " was rejected by " + mSessionImpl);
}
return;
}
}
try {
task.run(controller);
} catch (RemoteException e) {
// Currently it's TransactionTooLargeException or DeadSystemException.
// We'd better to leave log for those cases because
// - TransactionTooLargeException means that we may need to fix our code.
// (e.g. add pagination or special way to deliver Bitmap)
// - DeadSystemException means that errors around it can be ignored.
Log.w(TAG, "Exception in " + controller, e);
}
}
@FunctionalInterface
private interface SessionTask {
void run(ControllerInfo controller) throws RemoteException;
}
@SuppressWarnings("ClassCanBeStatic")
final class ControllerLegacyCb extends MediaSession.ControllerCb {
private final RemoteUserInfo mRemoteUserInfo;
ControllerLegacyCb(RemoteUserInfo remoteUserInfo) {
mRemoteUserInfo = remoteUserInfo;
}
@Override
void onPlayerResult(int seq, PlayerResult result) throws RemoteException {
// no-op.
}
@Override
void onSessionResult(int seq, SessionResult result) throws RemoteException {
// no-op.
}
@Override
void onLibraryResult(int seq, LibraryResult result) throws RemoteException {
// no-op
}
@Override
void setCustomLayout(int seq, @NonNull List<CommandButton> layout) throws RemoteException {
// no-op.
}
@Override
void onPlaybackInfoChanged(int seq, @NonNull PlaybackInfo info) throws RemoteException {
throw new AssertionError("This shouldn't be called");
}
@Override
void onAllowedCommandsChanged(int seq, @NonNull SessionCommandGroup commands)
throws RemoteException {
// no-op
}
@Override
void sendCustomCommand(int seq, @NonNull SessionCommand command, Bundle args)
throws RemoteException {
// no-op
}
@Override
void onPlayerStateChanged(int seq, long eventTimeMs, long positionMs, int playerState)
throws RemoteException {
throw new AssertionError("This shouldn't be called");
}
@Override
void onPlaybackSpeedChanged(int seq, long eventTimeMs, long positionMs, float speed)
throws RemoteException {
throw new AssertionError("This shouldn't be called");
}
@Override
void onBufferingStateChanged(int seq, @NonNull MediaItem item, int bufferingState,
long bufferedPositionMs, long eventTimeMs, long positionMs) throws RemoteException {
throw new AssertionError("This shouldn't be called");
}
@Override
void onSeekCompleted(int seq, long eventTimeMs, long positionMs, long position)
throws RemoteException {
throw new AssertionError("This shouldn't be called");
}
@Override
void onCurrentMediaItemChanged(int seq, MediaItem item, int currentIdx, int previousIdx,
int nextIdx) throws RemoteException {
throw new AssertionError("This shouldn't be called");
}
@Override
void onPlaylistChanged(int seq, @NonNull List<MediaItem> playlist, MediaMetadata metadata,
int currentIdx, int previousIdx, int nextIdx) throws RemoteException {
throw new AssertionError("This shouldn't be called");
}
@Override
void onPlaylistMetadataChanged(int seq, MediaMetadata metadata) throws RemoteException {
throw new AssertionError("This shouldn't be called");
}
@Override
void onShuffleModeChanged(int seq, int shuffleMode, int currentIdx, int previousIdx,
int nextIdx) throws RemoteException {
throw new AssertionError("This shouldn't be called");
}
@Override
void onRepeatModeChanged(int seq, int repeatMode, int currentIdx, int previousIdx,
int nextIdx) throws RemoteException {
throw new AssertionError("This shouldn't be called");
}
@Override
void onPlaybackCompleted(int seq) throws RemoteException {
throw new AssertionError("This shouldn't be called");
}
@Override
void onChildrenChanged(int seq, @NonNull String parentId, int itemCount,
LibraryParams params) throws RemoteException {
// no-op
}
@Override
void onSearchResultChanged(int seq, @NonNull String query, int itemCount,
LibraryParams params) throws RemoteException {
// no-op
}
@Override
void onDisconnected(int seq) throws RemoteException {
// no-op
}
@Override
void onVideoSizeChanged(int seq, @NonNull VideoSize videoSize) throws RemoteException {
// no-op
}
@Override
void onTracksChanged(int seq, List<TrackInfo> tracks,
TrackInfo selectedVideoTrack, TrackInfo selectedAudioTrack,
TrackInfo selectedSubtitleTrack, TrackInfo selectedMetadataTrack)
throws RemoteException {
// no-op
}
@Override
void onTrackSelected(int seq, TrackInfo trackInfo) throws RemoteException {
// no-op
}
@Override
void onTrackDeselected(int seq, TrackInfo trackInfo) throws RemoteException {
// no-op
}
@Override
void onSubtitleData(int seq, @NonNull MediaItem item,
@NonNull TrackInfo track, @NonNull SubtitleData data) {
// no-op
}
@Override
public int hashCode() {
return ObjectsCompat.hash(mRemoteUserInfo);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || obj.getClass() != ControllerLegacyCb.class) {
return false;
}
ControllerLegacyCb other = (ControllerLegacyCb) obj;
return ObjectsCompat.equals(mRemoteUserInfo, other.mRemoteUserInfo);
}
}
// TODO: Find a way to notify error through PlaybackStateCompat
final class ControllerLegacyCbForAll extends MediaSession.ControllerCb {
ControllerLegacyCbForAll() {
}
@Override
void onPlayerResult(int seq, PlayerResult result) throws RemoteException {
// no-op
}
@Override
void onSessionResult(int seq, SessionResult result) throws RemoteException {
// no-op
}
@Override
void onLibraryResult(int seq, LibraryResult result) throws RemoteException {
// no-op
}
@Override
void setCustomLayout(int seq, @NonNull List<CommandButton> layout) throws RemoteException {
throw new AssertionError("This shouldn't be called");
}
@Override
void onPlaybackInfoChanged(int seq, @NonNull PlaybackInfo info) throws RemoteException {
// no-op. Calling MediaSessionCompat#setPlaybackToLocal/Remote
// is already done in updatePlayerConnector().
}
@Override
void onAllowedCommandsChanged(int seq, @NonNull SessionCommandGroup commands)
throws RemoteException {
throw new AssertionError("This shouldn't be called");
}
@Override
void sendCustomCommand(int seq, @NonNull SessionCommand command, Bundle args)
throws RemoteException {
// no-op
}
@Override
void onPlayerStateChanged(int seq, long eventTimeMs, long positionMs, int playerState)
throws RemoteException {
// Note: This method does not use any of the given arguments.
mSessionImpl.getSessionCompat().setPlaybackState(
mSessionImpl.createPlaybackStateCompat());
}
@Override
void onPlaybackSpeedChanged(int seq, long eventTimeMs, long positionMs, float speed)
throws RemoteException {
// Note: This method does not use any of the given arguments.
mSessionImpl.getSessionCompat().setPlaybackState(
mSessionImpl.createPlaybackStateCompat());
}
@Override
void onBufferingStateChanged(int seq, @NonNull MediaItem item, int bufferingState,
long bufferedPositionMs, long eventTimeMs, long positionMs) throws RemoteException {
// Note: This method does not use any of the given arguments.
mSessionImpl.getSessionCompat().setPlaybackState(
mSessionImpl.createPlaybackStateCompat());
}
@Override
void onSeekCompleted(int seq, long eventTimeMs, long positionMs, long position)
throws RemoteException {
// Note: This method does not use any of the given arguments.
mSessionImpl.getSessionCompat().setPlaybackState(
mSessionImpl.createPlaybackStateCompat());
}
@Override
void onCurrentMediaItemChanged(int seq, MediaItem item, int currentIdx, int previousIdx,
int nextIdx) throws RemoteException {
mSessionImpl.getSessionCompat().setMetadata(item == null ? null
: MediaUtils.convertToMediaMetadataCompat(item.getMetadata()));
}
@Override
void onPlaylistChanged(int seq, @NonNull List<MediaItem> playlist, MediaMetadata metadata,
int currentIdx, int previousIdx, int nextIdx) throws RemoteException {
if (Build.VERSION.SDK_INT < 21) {
if (playlist == null) {
mSessionImpl.getSessionCompat().setQueue(null);
} else {
// In order to avoid TransactionTooLargeException for below API 21, we need to
// cut the list so that it doesn't exceed the binder transaction limit.
List<QueueItem> queueItemList = MediaUtils.convertToQueueItemList(playlist);
List<QueueItem> truncatedList = MediaUtils.truncateListBySize(
queueItemList, TRANSACTION_SIZE_LIMIT_IN_BYTES);
if (truncatedList.size() != playlist.size()) {
Log.i(TAG, "Sending " + truncatedList.size() + " items out of "
+ playlist.size());
}
mSessionImpl.getSessionCompat().setQueue(truncatedList);
}
} else {
// Framework MediaSession#setQueue() uses ParceledListSlice,
// which means we can safely send long lists.
mSessionImpl.getSessionCompat().setQueue(
MediaUtils.convertToQueueItemList(playlist));
}
onPlaylistMetadataChanged(seq, metadata);
}
@Override
void onPlaylistMetadataChanged(int seq, MediaMetadata metadata) throws RemoteException {
// Since there is no 'queue metadata', only set title of the queue.
CharSequence oldTitle = mSessionImpl.getSessionCompat().getController().getQueueTitle();
CharSequence newTitle = null;
if (metadata != null) {
newTitle = metadata.getText(METADATA_KEY_DISPLAY_TITLE);
if (newTitle == null) {
newTitle = metadata.getText(METADATA_KEY_TITLE);
}
}
if (!TextUtils.equals(oldTitle, newTitle)) {
mSessionImpl.getSessionCompat().setQueueTitle(newTitle);
}
}
@Override
void onShuffleModeChanged(int seq, int shuffleMode, int currentIdx, int previousIdx,
int nextIdx) throws RemoteException {
mSessionImpl.getSessionCompat().setShuffleMode(shuffleMode);
}
@Override
void onRepeatModeChanged(int seq, int repeatMode, int currentIdx, int previousIdx,
int nextIdx) throws RemoteException {
mSessionImpl.getSessionCompat().setRepeatMode(repeatMode);
}
@Override
void onPlaybackCompleted(int seq) throws RemoteException {
PlaybackStateCompat state = mSessionImpl.createPlaybackStateCompat();
if (state.getState() != PlaybackStateCompat.STATE_PAUSED) {
state = new PlaybackStateCompat.Builder(state)
.setState(PlaybackStateCompat.STATE_PAUSED, state.getPosition(),
state.getPlaybackSpeed())
.build();
}
mSessionImpl.getSessionCompat().setPlaybackState(state);
}
@Override
void onChildrenChanged(int seq, @NonNull String parentId, int itemCount,
LibraryParams params) throws RemoteException {
// no-op
}
@Override
void onSearchResultChanged(int seq, @NonNull String query, int itemCount,
LibraryParams params) throws RemoteException {
// no-op
}
@Override
void onDisconnected(int seq) throws RemoteException {
// no-op. Calling MediaSessionCompat#release() is already done in close().
}
@Override
void onVideoSizeChanged(int seq, @NonNull VideoSize videoSize) throws RemoteException {
// no-op
}
@Override
void onTracksChanged(int seq, List<TrackInfo> tracks,
TrackInfo selectedVideoTrack, TrackInfo selectedAudioTrack,
TrackInfo selectedSubtitleTrack, TrackInfo selectedMetadataTrack)
throws RemoteException {
// no-op
}
@Override
void onTrackSelected(int seq, TrackInfo trackInfo) throws RemoteException {
// no-op
}
@Override
void onTrackDeselected(int seq, TrackInfo trackInfo) throws RemoteException {
// no-op
}
@Override
void onSubtitleData(int seq, @NonNull MediaItem item,
@NonNull TrackInfo track, @NonNull SubtitleData data) {
// no-op
}
}
}