java.lang.Object
↳androidx.media2.SessionPlayer
Subclasses:
RemoteSessionPlayer, MediaPlayer, RoutePlayer
Gradle dependencies
compile group: 'androidx.media2', name: 'media2', version: '1.0.0-alpha04'
- groupId: androidx.media2
- artifactId: media2
- version: 1.0.0-alpha04
Artifact androidx.media2:media2:1.0.0-alpha04 it located at Google repository (https://maven.google.com/)
Androidx artifact mapping:
androidx.media2:media2 com.android.support:media2
Overview
Base interface for all media players that want media session.
APIs that return should be the asynchronous calls and shouldn't block
the calling thread. This guarantees the APIs are safe to be called on the main thread.
Topics covered here are:
- Best practices
- Player states
- Invalid method calls
Best practices
Here are best practices when implementing/using SessionPlayer:
- When updating UI, you should respond to SessionPlayer.PlayerCallback invocations instead of
SessionPlayer.PlayerResult objects since the player can be controlled by others.
- When a SessionPlayer object is no longer being used, call
close
as soon as
possible to release the resources used by the internal player engine associated with the
SessionPlayer. For example, if a player uses hardware decoder, other player instances may
fallback to software decoders or fail to play. You cannot use SessionPlayer instance after
you call close
. There is no way to reuse the instance.
- The current playback position can be retrieved with a call to SessionPlayer.getCurrentPosition(),
which is helpful for applications such as a music player that need to keep track of the playback
progress.
- The playback position can be adjusted with a call to SessionPlayer.seekTo(long). Although the
asynchronous SessionPlayer.seekTo(long) call returns right away, the actual seek operation may take a
while to finish, especially for audio/video being streamed.
- You can call SessionPlayer.seekTo(long) from the SessionPlayer.PLAYER_STATE_PAUSED. In these cases, if
you are playing a video stream and the requested position is valid, one video frame may be
displayed.
Player states
The playback control of audio/video files is managed as a state machine. The SessionPlayer
defines four states:
- SessionPlayer.PLAYER_STATE_IDLE: Initial state after the instantiation.
While in this state, you should call SessionPlayer.setMediaItem(MediaItem) or
SessionPlayer.setPlaylist(List, MediaMetadata). Check returned for
potential error.
Calling SessionPlayer.prepare() transfers this object to SessionPlayer.PLAYER_STATE_PAUSED.
- SessionPlayer.PLAYER_STATE_PAUSED: State when the audio/video playback is paused.
Call SessionPlayer.play() to resume or start playback from the position where it paused.
- SessionPlayer.PLAYER_STATE_PLAYING: State when the player plays the media item.
In this state, SessionPlayer.PlayerCallback.onBufferingStateChanged(SessionPlayer, MediaItem, int) will be called regularly to tell the buffering status.
Playback state would remain SessionPlayer.PLAYER_STATE_PLAYING when the currently playing
media item is changed.
When the playback reaches the end of stream, the behavior depends on repeat mode, set by
SessionPlayer.setRepeatMode(int). If the repeat mode was set to SessionPlayer.REPEAT_MODE_NONE,
the player will transfer to the SessionPlayer.PLAYER_STATE_PAUSED. Otherwise, the
SessionPlayer object remains in the SessionPlayer.PLAYER_STATE_PLAYING and playback will be
ongoing.
- SessionPlayer.PLAYER_STATE_ERROR: State when the playback failed and player cannot be
recovered by itself.
In general, playback might fail due to various reasons such as unsupported audio/video
format, poorly interleaved audio/video, resolution too high, streaming timeout, and
others. In addition, due to programming errors, a playback control operation might be
performed from an invalid state. In these cases the player
may transition to this state.
Invalid method calls
The only method you safely call from the
SessionPlayer.PLAYER_STATE_ERROR is
close
.
Any other methods might throw an exception or return meaningless data.
Subclasses of the SessionPlayer may have extra methods that are safe to be called in the error
state and/or provide a method to recover from the error state. Take a look at documentations of
specific class that you're interested in.
Most methods can be called from any non-Error state. They will either perform their work or
silently have no effect. The following table lists the methods that aren't guaranteed to
successfully running if they're called from the associated invalid states.
Method Name | Invalid States |
setMediaItem | {Paused, Playing} |
setPlaylist | {Paused, Playing} |
prepare | {Paused, Playing} |
play | {Idle} |
pause | {Idle} |
seekTo | {Idle} |
Summary
Fields |
---|
public static final int | BUFFERING_STATE_BUFFERING_AND_PLAYABLE Buffering state indicating the player is buffering but enough has been buffered
for this player to be able to play the content. |
public static final int | BUFFERING_STATE_BUFFERING_AND_STARVED Buffering state indicating the player is buffering, but the player is currently starved
for data, and cannot play. |
public static final int | BUFFERING_STATE_COMPLETE Buffering state indicating the player is done buffering, and the remainder of the content is
available for playback. |
public static final int | BUFFERING_STATE_UNKNOWN Buffering state is unknown. |
public static final int | PLAYER_STATE_ERROR State when the player is in error state and cannot be recovered self. |
public static final int | PLAYER_STATE_IDLE State when the player is idle, and needs configuration to start playback. |
public static final int | PLAYER_STATE_PAUSED State when the player's playback is paused |
public static final int | PLAYER_STATE_PLAYING State when the player's playback is ongoing |
public static final int | REPEAT_MODE_ALL Playing media list will be repeated. |
public static final int | REPEAT_MODE_GROUP Playback of the playing media group will be repeated. |
public static final int | REPEAT_MODE_NONE Playback will be stopped at the end of the playing media list. |
public static final int | REPEAT_MODE_ONE Playback of the current playing media item will be repeated. |
public static final int | SHUFFLE_MODE_ALL Media list will be played in shuffled order. |
public static final int | SHUFFLE_MODE_GROUP Media group will be played in shuffled order. |
public static final int | SHUFFLE_MODE_NONE Media list will be played in order. |
public static final long | UNKNOWN_TIME |
Methods |
---|
public abstract <any> | addPlaylistItem(int index, MediaItem item)
Adds the media item to the playlist at position index. |
public abstract AudioAttributesCompat | getAudioAttributes()
Gets the AudioAttributesCompat that media player has. |
public abstract long | getBufferedPosition()
Gets the buffered position of current playback, or SessionPlayer.UNKNOWN_TIME if unknown. |
public abstract int | getBufferingState()
Returns the current buffering state of the player. |
protected final java.util.List<Pair> | getCallbacks()
Gets the callbacks with executors for subclasses to notify player events. |
public abstract MediaItem | getCurrentMediaItem()
Gets the current media item. |
public abstract int | getCurrentMediaItemIndex()
Gets the index of current media item in playlist. |
public abstract long | getCurrentPosition()
Gets the current playback head position. |
public abstract long | getDuration()
Gets the duration of the current media item, or SessionPlayer.UNKNOWN_TIME if unknown. |
public abstract int | getNextMediaItemIndex()
Gets the next item index in the playlist. |
public abstract float | getPlaybackSpeed()
Gets the actual playback speed to be used by the player when playing. |
public abstract int | getPlayerState()
Gets the current player state. |
public abstract java.util.List<MediaItem> | getPlaylist()
Gets the playlist. |
public abstract MediaMetadata | getPlaylistMetadata()
Gets the playlist metadata. |
public abstract int | getPreviousMediaItemIndex()
Gets the previous item index in the playlist. |
public abstract int | getRepeatMode()
Gets the repeat mode. |
public abstract int | getShuffleMode()
Gets the shuffle mode. |
public abstract <any> | pause()
Pauses playback. |
public abstract <any> | play()
Plays the playback. |
public abstract <any> | prepare()
Prepares the media items for playback. |
public final void | registerPlayerCallback(java.util.concurrent.Executor executor, SessionPlayer.PlayerCallback callback)
Register SessionPlayer.PlayerCallback to listen changes. |
public abstract <any> | removePlaylistItem(int index)
Removes the media item from the playlist |
public abstract <any> | replacePlaylistItem(int index, MediaItem item)
Replaces the media item at index in the playlist. |
public abstract <any> | seekTo(long position)
Seeks to the specified position. |
public abstract <any> | setAudioAttributes(AudioAttributesCompat attributes)
Sets the AudioAttributesCompat to be used during the playback of the media. |
public abstract <any> | setMediaItem(MediaItem item)
Sets a MediaItem for playback. |
public abstract <any> | setPlaybackSpeed(float playbackSpeed)
Sets the playback speed. |
public abstract <any> | setPlaylist(java.util.List<MediaItem> list, MediaMetadata metadata)
Sets a list of MediaItem with metadata. |
public abstract <any> | setRepeatMode(int repeatMode)
Sets the repeat mode. |
public abstract <any> | setShuffleMode(int shuffleMode)
Sets the shuffle mode. |
public abstract <any> | skipToNextPlaylistItem()
Skips to the next item in the playlist. |
public abstract <any> | skipToPlaylistItem(int index)
Skips to the the media item. |
public abstract <any> | skipToPreviousPlaylistItem()
Skips to the previous item in the playlist. |
public final void | unregisterPlayerCallback(SessionPlayer.PlayerCallback callback)
Unregister the previously registered SessionPlayer.PlayerCallback. |
public abstract <any> | updatePlaylistMetadata(MediaMetadata metadata)
Updates the playlist metadata while keeping the playlist as-is. |
from java.lang.Object | clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait |
Fields
public static final int
PLAYER_STATE_IDLEState when the player is idle, and needs configuration to start playback.
public static final int
PLAYER_STATE_PAUSEDState when the player's playback is paused
public static final int
PLAYER_STATE_PLAYINGState when the player's playback is ongoing
public static final int
PLAYER_STATE_ERRORState when the player is in error state and cannot be recovered self.
public static final int
BUFFERING_STATE_UNKNOWNBuffering state is unknown.
public static final int
BUFFERING_STATE_BUFFERING_AND_PLAYABLEBuffering state indicating the player is buffering but enough has been buffered
for this player to be able to play the content.
See SessionPlayer.getBufferedPosition() for how far is buffered already.
public static final int
BUFFERING_STATE_BUFFERING_AND_STARVEDBuffering state indicating the player is buffering, but the player is currently starved
for data, and cannot play.
public static final int
BUFFERING_STATE_COMPLETEBuffering state indicating the player is done buffering, and the remainder of the content is
available for playback.
public static final int
REPEAT_MODE_NONEPlayback will be stopped at the end of the playing media list.
public static final int
REPEAT_MODE_ONEPlayback of the current playing media item will be repeated.
public static final int
REPEAT_MODE_ALLPlaying media list will be repeated.
public static final int
REPEAT_MODE_GROUPPlayback of the playing media group will be repeated.
A group is a logical block of media items which is specified in the section 5.7 of the
Bluetooth AVRCP 1.6. An example of a group is the playlist.
public static final int
SHUFFLE_MODE_NONEMedia list will be played in order.
public static final int
SHUFFLE_MODE_ALLMedia list will be played in shuffled order.
public static final int
SHUFFLE_MODE_GROUPMedia group will be played in shuffled order.
A group is a logical block of media items which is specified in the section 5.7 of the
Bluetooth AVRCP 1.6. An example of a group is the playlist.
public static final long
UNKNOWN_TIMEConstructors
Methods
public abstract <any>
play()
Plays the playback.
public abstract <any>
pause()
Pauses playback.
public abstract <any>
prepare()
Prepares the media items for playback. During this time, the player may allocate resources
required to play, such as audio and video decoders.
public abstract <any>
seekTo(long position)
Seeks to the specified position. Moves the playback head to the specified position.
Parameters:
position: the new playback position in ms. The value should be in the range of start
and end positions defined in MediaItem.
public abstract <any>
setPlaybackSpeed(float playbackSpeed)
Sets the playback speed. A value of 1.0f is the default playback value.
After changing the playback speed, it is recommended to query the actual speed supported
by the player, see SessionPlayer.getPlaybackSpeed().
Parameters:
playbackSpeed: playback speed
Sets the AudioAttributesCompat to be used during the playback of the media.
You must call this method in SessionPlayer.PLAYER_STATE_IDLE in order for the audio attributes to
become effective thereafter.
Parameters:
attributes: non-null AudioAttributes
.
public abstract int
getPlayerState()
Gets the current player state.
Returns:
the current player state
See also: SessionPlayer.PlayerCallback.onPlayerStateChanged(SessionPlayer, int), SessionPlayer.PLAYER_STATE_IDLE, SessionPlayer.PLAYER_STATE_PAUSED, SessionPlayer.PLAYER_STATE_PLAYING, SessionPlayer.PLAYER_STATE_ERROR
public abstract long
getCurrentPosition()
Gets the current playback head position.
Returns:
the current playback position in ms, or SessionPlayer.UNKNOWN_TIME if unknown.
public abstract long
getDuration()
Gets the duration of the current media item, or SessionPlayer.UNKNOWN_TIME if unknown.
Returns:
the duration in ms, or SessionPlayer.UNKNOWN_TIME.
public abstract long
getBufferedPosition()
Gets the buffered position of current playback, or SessionPlayer.UNKNOWN_TIME if unknown.
Returns:
the buffered position in ms, or SessionPlayer.UNKNOWN_TIME.
public abstract int
getBufferingState()
Returns the current buffering state of the player.
During the buffering, see SessionPlayer.getBufferedPosition() for the quantifying the amount
already buffered.
Returns:
the buffering state.
See also: SessionPlayer.getBufferedPosition()
public abstract float
getPlaybackSpeed()
Gets the actual playback speed to be used by the player when playing.
Note that it may differ from the speed set in SessionPlayer.setPlaybackSpeed(float).
Returns:
the actual playback speed
public abstract <any>
setPlaylist(java.util.List<MediaItem> list,
MediaMetadata metadata)
Sets a list of MediaItem with metadata. Ensure uniqueness of each MediaItem
in the playlist so the session can uniquely identity individual items. All
MediaItems shouldn't be null as well.
It's recommended to fill MediaMetadata in each MediaItem especially for the
duration information with the key MediaMetadata.METADATA_KEY_DURATION. Without the
duration information in the metadata, session will do extra work to get the duration and send
it to the controller.
The implementation must notify registered callbacks with
SessionPlayer.PlayerCallback.onPlaylistChanged(SessionPlayer, List, MediaMetadata) when it's
completed.
Parameters:
list: A list of MediaItem objects to set as a play list.
Returns:
a which represents the pending completion of the command.
See also: SessionPlayer.PlayerCallback.onPlaylistChanged(SessionPlayer, List, MediaMetadata)
Gets the AudioAttributesCompat that media player has.
public abstract <any>
setMediaItem(
MediaItem item)
Sets a MediaItem for playback.
It's recommended to fill MediaMetadata in each MediaItem especially for the
duration information with the key MediaMetadata.METADATA_KEY_DURATION. Without the
duration information in the metadata, session will do extra work to get the duration and send
it to the controller.
The implementation must notify registered callbacks with
SessionPlayer.PlayerCallback.onPlaylistChanged(SessionPlayer, List, MediaMetadata) when it's
completed.
Parameters:
item: the descriptor of media item you want to play
Returns:
a which represents the pending completion of the command.
public abstract <any>
addPlaylistItem(int index,
MediaItem item)
Adds the media item to the playlist at position index. Index equals or greater than
the current playlist size (e.g. MAX_VALUE
) will add the item at the end of
the playlist.
The implementation may not change the currently playing media item.
If index is less than or equal to the current index of the playlist,
the current index of the playlist will be increased correspondingly.
The implementation must notify registered callbacks with
SessionPlayer.PlayerCallback.onPlaylistChanged(SessionPlayer, List, MediaMetadata) when it's
completed.
Parameters:
index: the index of the item you want to add in the playlist
item: the media item you want to add
See also: SessionPlayer.PlayerCallback.onPlaylistChanged(SessionPlayer, List, MediaMetadata)
public abstract <any>
removePlaylistItem(int index)
Removes the media item from the playlist
The implementation may not change the currently playing media item even when it's removed.
The implementation must notify registered callbacks with
SessionPlayer.PlayerCallback.onPlaylistChanged(SessionPlayer, List, MediaMetadata) when it's
completed.
Parameters:
index: the index of the item you want to remove in the playlist
See also: SessionPlayer.PlayerCallback.onPlaylistChanged(SessionPlayer, List, MediaMetadata)
public abstract <any>
replacePlaylistItem(int index,
MediaItem item)
Replaces the media item at index in the playlist. This can be also used to update metadata of
an item.
The implementation must notify registered callbacks with
SessionPlayer.PlayerCallback.onPlaylistChanged(SessionPlayer, List, MediaMetadata) when it's
completed.
Parameters:
index: the index of the item to replace in the playlist
item: the new item
See also: SessionPlayer.PlayerCallback.onPlaylistChanged(SessionPlayer, List, MediaMetadata)
public abstract <any>
skipToPreviousPlaylistItem()
Skips to the previous item in the playlist.
The implementation must notify registered callbacks with
SessionPlayer.PlayerCallback.onCurrentMediaItemChanged(SessionPlayer, MediaItem) when it's
completed.
See also: SessionPlayer.PlayerCallback.onCurrentMediaItemChanged(SessionPlayer, MediaItem)
public abstract <any>
skipToNextPlaylistItem()
Skips to the next item in the playlist.
The implementation must notify registered callbacks with
SessionPlayer.PlayerCallback.onCurrentMediaItemChanged(SessionPlayer, MediaItem) when it's
completed.
See also: SessionPlayer.PlayerCallback.onCurrentMediaItemChanged(SessionPlayer, MediaItem)
public abstract <any>
skipToPlaylistItem(int index)
Skips to the the media item.
The implementation must notify registered callbacks with
SessionPlayer.PlayerCallback.onCurrentMediaItemChanged(SessionPlayer, MediaItem) when it's
completed.
Parameters:
index: The index of the item you want to play in the playlist
See also: SessionPlayer.PlayerCallback.onCurrentMediaItemChanged(SessionPlayer, MediaItem)
public abstract <any>
updatePlaylistMetadata(
MediaMetadata metadata)
Updates the playlist metadata while keeping the playlist as-is.
The implementation must notify registered callbacks with
SessionPlayer.PlayerCallback.onPlaylistMetadataChanged(SessionPlayer, MediaMetadata) when it's
completed.
Parameters:
metadata: metadata of the playlist
See also: SessionPlayer.PlayerCallback.onPlaylistMetadataChanged(SessionPlayer, MediaMetadata)
public abstract <any>
setRepeatMode(int repeatMode)
Sets the repeat mode.
The implementation must notify registered callbacks with
SessionPlayer.PlayerCallback.onRepeatModeChanged(SessionPlayer, int) when it's completed.
Parameters:
repeatMode: repeat mode
See also: SessionPlayer.REPEAT_MODE_NONE, SessionPlayer.REPEAT_MODE_ONE, SessionPlayer.REPEAT_MODE_ALL, SessionPlayer.REPEAT_MODE_GROUP, SessionPlayer.PlayerCallback.onRepeatModeChanged(SessionPlayer, int)
public abstract <any>
setShuffleMode(int shuffleMode)
Sets the shuffle mode.
The implementation must notify registered callbacks with
SessionPlayer.PlayerCallback.onShuffleModeChanged(SessionPlayer, int) when it's completed.
Parameters:
shuffleMode: The shuffle mode
See also: SessionPlayer.SHUFFLE_MODE_NONE, SessionPlayer.SHUFFLE_MODE_ALL, SessionPlayer.SHUFFLE_MODE_GROUP, SessionPlayer.PlayerCallback.onShuffleModeChanged(SessionPlayer, int)
public abstract java.util.List<MediaItem>
getPlaylist()
Gets the playlist.
Returns:
playlist, or null if none is set.
See also: SessionPlayer.PlayerCallback.onPlaylistChanged(SessionPlayer, List, MediaMetadata)
Gets the playlist metadata.
Returns:
metadata metadata of the playlist, or null if none is set
See also: SessionPlayer.PlayerCallback.onPlaylistChanged(SessionPlayer, List, MediaMetadata), SessionPlayer.PlayerCallback.onPlaylistMetadataChanged(SessionPlayer, MediaMetadata)
public abstract int
getRepeatMode()
Gets the repeat mode.
Returns:
repeat mode
See also: SessionPlayer.REPEAT_MODE_NONE, SessionPlayer.REPEAT_MODE_ONE, SessionPlayer.REPEAT_MODE_ALL, SessionPlayer.REPEAT_MODE_GROUP, SessionPlayer.PlayerCallback.onRepeatModeChanged(SessionPlayer, int)
public abstract int
getShuffleMode()
Gets the shuffle mode.
Returns:
The shuffle mode
See also: SessionPlayer.SHUFFLE_MODE_NONE, SessionPlayer.SHUFFLE_MODE_ALL, SessionPlayer.SHUFFLE_MODE_GROUP, SessionPlayer.PlayerCallback.onShuffleModeChanged(SessionPlayer, int)
public abstract
MediaItem getCurrentMediaItem()
Gets the current media item.
Returns:
the current media item. Can be null only when media item or playlist hasn't
been set.
public abstract int
getCurrentMediaItemIndex()
Gets the index of current media item in playlist. This value may be updated when
SessionPlayer.PlayerCallback.onCurrentMediaItemChanged(SessionPlayer, MediaItem) or
SessionPlayer.PlayerCallback.onPlaylistChanged(SessionPlayer, List, MediaMetadata) is called.
Returns:
the index of current media item. Can be -1 only when current media item is null or
playlist hasn't been set.
public abstract int
getPreviousMediaItemIndex()
Gets the previous item index in the playlist. The returned value can be outdated after
SessionPlayer.PlayerCallback.onCurrentMediaItemChanged(SessionPlayer, MediaItem) or
SessionPlayer.PlayerCallback.onPlaylistChanged(SessionPlayer, List, MediaMetadata) is called.
Returns:
the index of previous media item. Can be -1 only when previous media item does not
exist or playlist hasn't been set.
public abstract int
getNextMediaItemIndex()
Gets the next item index in the playlist. The returned value can be outdated after
SessionPlayer.PlayerCallback.onCurrentMediaItemChanged(SessionPlayer, MediaItem) or
SessionPlayer.PlayerCallback.onPlaylistChanged(SessionPlayer, List, MediaMetadata) is called.
Returns:
the index of next media item. Can be -1 only when next media item does not exist or
playlist hasn't been set.
Register SessionPlayer.PlayerCallback to listen changes.
Parameters:
executor: a callback Executor
callback: a PlayerCallback
Unregister the previously registered SessionPlayer.PlayerCallback.
Parameters:
callback: the callback to be removed
protected final java.util.List<Pair>
getCallbacks()
Gets the callbacks with executors for subclasses to notify player events.
Returns:
map of callbacks and its executors
Source
/*
* Copyright 2018 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;
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
import android.annotation.TargetApi;
import android.os.Build;
import android.os.SystemClock;
import android.util.Log;
import androidx.annotation.GuardedBy;
import androidx.annotation.IntDef;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.concurrent.futures.ResolvableFuture;
import androidx.core.util.Pair;
import androidx.media.AudioAttributesCompat;
import com.google.common.util.concurrent.ListenableFuture;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
/**
* Base interface for all media players that want media session.
* <p>
* APIs that return {@link ListenableFuture} should be the asynchronous calls and shouldn't block
* the calling thread. This guarantees the APIs are safe to be called on the main thread.
*
* <p>Topics covered here are:
* <ol>
* <li><a href="#BestPractices">Best practices</a>
* <li><a href="#PlayerStates">Player states</a>
* <li><a href="#InvalidStates">Invalid method calls</a>
* </ol>
*
* <h3 id="BestPractices">Best practices</h3>
*
* Here are best practices when implementing/using SessionPlayer:
*
* <ul>
* <li>When updating UI, you should respond to {@link PlayerCallback} invocations instead of
* {@link PlayerResult} objects since the player can be controlled by others.
* <li>When a SessionPlayer object is no longer being used, call {@link #close()} as soon as
* possible to release the resources used by the internal player engine associated with the
* SessionPlayer. For example, if a player uses hardware decoder, other player instances may
* fallback to software decoders or fail to play. You cannot use SessionPlayer instance after
* you call {@link #close()}. There is no way to reuse the instance.
* <li>The current playback position can be retrieved with a call to {@link #getCurrentPosition()},
* which is helpful for applications such as a music player that need to keep track of the playback
* progress.
* <li>The playback position can be adjusted with a call to {@link #seekTo(long)}. Although the
* asynchronous {@link #seekTo} call returns right away, the actual seek operation may take a
* while to finish, especially for audio/video being streamed.
* <li>You can call {@link #seekTo(long)} from the {@link #PLAYER_STATE_PAUSED}. In these cases, if
* you are playing a video stream and the requested position is valid, one video frame may be
* displayed.
* </ul>
*
* <h3 id="PlayerStates">Player states</h3>
* The playback control of audio/video files is managed as a state machine. The SessionPlayer
* defines four states:
* <ol>
* <li>{@link #PLAYER_STATE_IDLE}: Initial state after the instantiation.
* <p>
* While in this state, you should call {@link #setMediaItem(MediaItem)} or
* {@link #setPlaylist(List, MediaMetadata)}. Check returned {@link ListenableFuture} for
* potential error.
* <p>
* Calling {@link #prepare()} transfers this object to {@link #PLAYER_STATE_PAUSED}.
*
* <li>{@link #PLAYER_STATE_PAUSED}: State when the audio/video playback is paused.
* <p>
* Call {@link #play()} to resume or start playback from the position where it paused.
*
* <li>{@link #PLAYER_STATE_PLAYING}: State when the player plays the media item.
* <p>
* In this state, {@link PlayerCallback#onBufferingStateChanged(
* SessionPlayer, MediaItem, int)} will be called regularly to tell the buffering status.
* <p>
* Playback state would remain {@link #PLAYER_STATE_PLAYING} when the currently playing
* media item is changed.
* <p>
* When the playback reaches the end of stream, the behavior depends on repeat mode, set by
* {@link #setRepeatMode(int)}. If the repeat mode was set to {@link #REPEAT_MODE_NONE},
* the player will transfer to the {@link #PLAYER_STATE_PAUSED}. Otherwise, the
* SessionPlayer object remains in the {@link #PLAYER_STATE_PLAYING} and playback will be
* ongoing.
*
* <li>{@link #PLAYER_STATE_ERROR}: State when the playback failed and player cannot be
* recovered by itself.
* <p>
* In general, playback might fail due to various reasons such as unsupported audio/video
* format, poorly interleaved audio/video, resolution too high, streaming timeout, and
* others. In addition, due to programming errors, a playback control operation might be
* performed from an <a href="#InvalidStates">invalid state</a>. In these cases the player
* may transition to this state.
* </ol>
* <p>
*
* <h3 id="InvalidStates">Invalid method calls</h3>
* The only method you safely call from the {@link #PLAYER_STATE_ERROR} is {@link #close()}.
* Any other methods might throw an exception or return meaningless data.
* <p>
* Subclasses of the SessionPlayer may have extra methods that are safe to be called in the error
* state and/or provide a method to recover from the error state. Take a look at documentations of
* specific class that you're interested in.
* <p>
* Most methods can be called from any non-Error state. They will either perform their work or
* silently have no effect. The following table lists the methods that aren't guaranteed to
* successfully running if they're called from the associated invalid states.
* <p>
* <table>
* <tr><th>Method Name</th> <th>Invalid States</th></tr>
* <tr><td>setMediaItem</td> <td>{Paused, Playing}</td></tr>
* <tr><td>setPlaylist</td> <td>{Paused, Playing}</td></tr>
* <tr><td>prepare</td> <td>{Paused, Playing}</td></tr>
* <tr><td>play</td> <td>{Idle}</td></tr>
* <tr><td>pause</td> <td>{Idle}</td></tr>
* <tr><td>seekTo</td> <td>{Idle}</td></tr>
* </table>
*/
// Previously MediaSessionCompat.Callback.
// Players can extend this directly (e.g. MediaPlayer) or create wrapper and control underlying
// player.
// Preferably it can be interface, but API guideline requires to use abstract class.
@TargetApi(Build.VERSION_CODES.P)
public abstract class SessionPlayer implements AutoCloseable {
private static final String TAG = "SessionPlayer";
/**
* @hide
*/
@RestrictTo(LIBRARY_GROUP)
@IntDef({
PLAYER_STATE_IDLE,
PLAYER_STATE_PAUSED,
PLAYER_STATE_PLAYING,
PLAYER_STATE_ERROR})
@Retention(RetentionPolicy.SOURCE)
public @interface PlayerState {
}
/**
* @hide
*/
@RestrictTo(LIBRARY_GROUP)
@IntDef({
BUFFERING_STATE_UNKNOWN,
BUFFERING_STATE_BUFFERING_AND_PLAYABLE,
BUFFERING_STATE_BUFFERING_AND_STARVED,
BUFFERING_STATE_COMPLETE})
@Retention(RetentionPolicy.SOURCE)
public @interface BuffState {
}
/**
* State when the player is idle, and needs configuration to start playback.
*/
public static final int PLAYER_STATE_IDLE = 0;
/**
* State when the player's playback is paused
*/
public static final int PLAYER_STATE_PAUSED = 1;
/**
* State when the player's playback is ongoing
*/
public static final int PLAYER_STATE_PLAYING = 2;
/**
* State when the player is in error state and cannot be recovered self.
*/
public static final int PLAYER_STATE_ERROR = 3;
/**
* Buffering state is unknown.
*/
public static final int BUFFERING_STATE_UNKNOWN = 0;
/**
* Buffering state indicating the player is buffering but enough has been buffered
* for this player to be able to play the content.
* See {@link #getBufferedPosition()} for how far is buffered already.
*/
public static final int BUFFERING_STATE_BUFFERING_AND_PLAYABLE = 1;
/**
* Buffering state indicating the player is buffering, but the player is currently starved
* for data, and cannot play.
*/
public static final int BUFFERING_STATE_BUFFERING_AND_STARVED = 2;
/**
* Buffering state indicating the player is done buffering, and the remainder of the content is
* available for playback.
*/
public static final int BUFFERING_STATE_COMPLETE = 3;
/**
* @hide
*/
@RestrictTo(LIBRARY_GROUP)
@IntDef({REPEAT_MODE_NONE, REPEAT_MODE_ONE, REPEAT_MODE_ALL,
REPEAT_MODE_GROUP})
@Retention(RetentionPolicy.SOURCE)
public @interface RepeatMode {
}
/**
* Playback will be stopped at the end of the playing media list.
*/
public static final int REPEAT_MODE_NONE = 0;
/**
* Playback of the current playing media item will be repeated.
*/
public static final int REPEAT_MODE_ONE = 1;
/**
* Playing media list will be repeated.
*/
public static final int REPEAT_MODE_ALL = 2;
/**
* Playback of the playing media group will be repeated.
* A group is a logical block of media items which is specified in the section 5.7 of the
* Bluetooth AVRCP 1.6. An example of a group is the playlist.
*/
public static final int REPEAT_MODE_GROUP = 3;
/**
* @hide
*/
@RestrictTo(LIBRARY_GROUP)
@IntDef({SHUFFLE_MODE_NONE, SHUFFLE_MODE_ALL, SHUFFLE_MODE_GROUP})
@Retention(RetentionPolicy.SOURCE)
public @interface ShuffleMode {
}
/**
* Media list will be played in order.
*/
public static final int SHUFFLE_MODE_NONE = 0;
/**
* Media list will be played in shuffled order.
*/
public static final int SHUFFLE_MODE_ALL = 1;
/**
* Media group will be played in shuffled order.
* A group is a logical block of media items which is specified in the section 5.7 of the
* Bluetooth AVRCP 1.6. An example of a group is the playlist.
*/
public static final int SHUFFLE_MODE_GROUP = 2;
public static final long UNKNOWN_TIME = -1;
private final Object mLock = new Object();
@GuardedBy("mLock")
private final List<Pair<PlayerCallback, Executor>> mCallbacks = new ArrayList<>();
/**
* Plays the playback.
*/
public abstract @NonNull ListenableFuture<PlayerResult> play();
/**
* Pauses playback.
*/
public abstract @NonNull ListenableFuture<PlayerResult> pause();
/**
* Prepares the media items for playback. During this time, the player may allocate resources
* required to play, such as audio and video decoders.
*/
public abstract @NonNull ListenableFuture<PlayerResult> prepare();
/**
* Seeks to the specified position. Moves the playback head to the specified position.
*
* @param position the new playback position in ms. The value should be in the range of start
* and end positions defined in {@link MediaItem}.
*/
public abstract @NonNull ListenableFuture<PlayerResult> seekTo(long position);
/**
* Sets the playback speed. A value of {@code 1.0f} is the default playback value.
* <p>
* After changing the playback speed, it is recommended to query the actual speed supported
* by the player, see {@link #getPlaybackSpeed()}.
*
* @param playbackSpeed playback speed
*/
public abstract @NonNull ListenableFuture<PlayerResult> setPlaybackSpeed(float playbackSpeed);
/**
* Sets the {@link AudioAttributesCompat} to be used during the playback of the media.
* <p>
* You must call this method in {@link #PLAYER_STATE_IDLE} in order for the audio attributes to
* become effective thereafter.
*
* @param attributes non-null <code>AudioAttributes</code>.
*/
public abstract @NonNull ListenableFuture<PlayerResult> setAudioAttributes(
@NonNull AudioAttributesCompat attributes);
/**
* Gets the current player state.
*
* @return the current player state
* @see PlayerCallback#onPlayerStateChanged(SessionPlayer, int)
* @see #PLAYER_STATE_IDLE
* @see #PLAYER_STATE_PAUSED
* @see #PLAYER_STATE_PLAYING
* @see #PLAYER_STATE_ERROR
*/
public abstract @PlayerState int getPlayerState();
/**
* Gets the current playback head position.
*
* @return the current playback position in ms, or {@link #UNKNOWN_TIME} if unknown.
*/
public abstract long getCurrentPosition();
/**
* Gets the duration of the current media item, or {@link #UNKNOWN_TIME} if unknown.
*
* @return the duration in ms, or {@link #UNKNOWN_TIME}.
*/
public abstract long getDuration();
/**
* Gets the buffered position of current playback, or {@link #UNKNOWN_TIME} if unknown.
* @return the buffered position in ms, or {@link #UNKNOWN_TIME}.
*/
public abstract long getBufferedPosition();
/**
* Returns the current buffering state of the player.
* During the buffering, see {@link #getBufferedPosition()} for the quantifying the amount
* already buffered.
*
* @return the buffering state.
* @see #getBufferedPosition()
*/
public abstract @BuffState int getBufferingState();
/**
* Gets the actual playback speed to be used by the player when playing.
* <p>
* Note that it may differ from the speed set in {@link #setPlaybackSpeed(float)}.
*
* @return the actual playback speed
*/
public abstract float getPlaybackSpeed();
/**
* Sets a list of {@link MediaItem} with metadata. Ensure uniqueness of each {@link MediaItem}
* in the playlist so the session can uniquely identity individual items. All
* {@link MediaItem}s shouldn't be {@code null} as well.
* <p>
* It's recommended to fill {@link MediaMetadata} in each {@link MediaItem} especially for the
* duration information with the key {@link MediaMetadata#METADATA_KEY_DURATION}. Without the
* duration information in the metadata, session will do extra work to get the duration and send
* it to the controller.
* <p>
* The implementation must notify registered callbacks with
* {@link PlayerCallback#onPlaylistChanged(SessionPlayer, List, MediaMetadata)} when it's
* completed.
*
* @param list A list of {@link MediaItem} objects to set as a play list.
* @throws IllegalArgumentException if the given list is {@code null} or empty, or has
* duplicated media items.
* @return a {@link ListenableFuture} which represents the pending completion of the command.
* @see PlayerCallback#onPlaylistChanged(SessionPlayer, List, MediaMetadata)
*/
public abstract @NonNull ListenableFuture<PlayerResult> setPlaylist(
@NonNull List<MediaItem> list, @Nullable MediaMetadata metadata);
/**
* Gets the {@link AudioAttributesCompat} that media player has.
*/
public abstract @Nullable AudioAttributesCompat getAudioAttributes();
/**
* Sets a {@link MediaItem} for playback.
* <p>
* It's recommended to fill {@link MediaMetadata} in each {@link MediaItem} especially for the
* duration information with the key {@link MediaMetadata#METADATA_KEY_DURATION}. Without the
* duration information in the metadata, session will do extra work to get the duration and send
* it to the controller.
* <p>
* The implementation must notify registered callbacks with
* {@link PlayerCallback#onPlaylistChanged(SessionPlayer, List, MediaMetadata)} when it's
* completed.
*
* @param item the descriptor of media item you want to play
* @return a {@link ListenableFuture} which represents the pending completion of the command.
* @throws IllegalArgumentException if the given item is {@code null}.
*/
public abstract @NonNull ListenableFuture<PlayerResult> setMediaItem(
@NonNull MediaItem item);
/**
* Adds the media item to the playlist at position index. Index equals or greater than
* the current playlist size (e.g. {@link Integer#MAX_VALUE}) will add the item at the end of
* the playlist.
* <p>
* The implementation may not change the currently playing media item.
* If index is less than or equal to the current index of the playlist,
* the current index of the playlist will be increased correspondingly.
* <p>
* The implementation must notify registered callbacks with
* {@link PlayerCallback#onPlaylistChanged(SessionPlayer, List, MediaMetadata)} when it's
* completed.
*
* @param index the index of the item you want to add in the playlist
* @param item the media item you want to add
* @see PlayerCallback#onPlaylistChanged(SessionPlayer, List, MediaMetadata)
*/
public abstract @NonNull ListenableFuture<PlayerResult> addPlaylistItem(int index,
@NonNull MediaItem item);
/**
* Removes the media item from the playlist
* <p>
* The implementation may not change the currently playing media item even when it's removed.
* <p>
* The implementation must notify registered callbacks with
* {@link PlayerCallback#onPlaylistChanged(SessionPlayer, List, MediaMetadata)} when it's
* completed.
*
* @param index the index of the item you want to remove in the playlist
* @see PlayerCallback#onPlaylistChanged(SessionPlayer, List, MediaMetadata)
*/
public abstract @NonNull ListenableFuture<PlayerResult> removePlaylistItem(
@IntRange(from = 0) int index);
/**
* Replaces the media item at index in the playlist. This can be also used to update metadata of
* an item.
* <p>
* The implementation must notify registered callbacks with
* {@link PlayerCallback#onPlaylistChanged(SessionPlayer, List, MediaMetadata)} when it's
* completed.
*
* @param index the index of the item to replace in the playlist
* @param item the new item
* @see PlayerCallback#onPlaylistChanged(SessionPlayer, List, MediaMetadata)
*/
public abstract @NonNull ListenableFuture<PlayerResult> replacePlaylistItem(int index,
@NonNull MediaItem item);
/**
* Skips to the previous item in the playlist.
* <p>
* The implementation must notify registered callbacks with
* {@link PlayerCallback#onCurrentMediaItemChanged(SessionPlayer, MediaItem)} when it's
* completed.
*
* @see PlayerCallback#onCurrentMediaItemChanged(SessionPlayer, MediaItem)
*/
public abstract @NonNull ListenableFuture<PlayerResult> skipToPreviousPlaylistItem();
/**
* Skips to the next item in the playlist.
* <p>
* The implementation must notify registered callbacks with
* {@link PlayerCallback#onCurrentMediaItemChanged(SessionPlayer, MediaItem)} when it's
* completed.
*
* @see PlayerCallback#onCurrentMediaItemChanged(SessionPlayer, MediaItem)
*/
public abstract @NonNull ListenableFuture<PlayerResult> skipToNextPlaylistItem();
/**
* Skips to the the media item.
* <p>
* The implementation must notify registered callbacks with
* {@link PlayerCallback#onCurrentMediaItemChanged(SessionPlayer, MediaItem)} when it's
* completed.
*
* @param index The index of the item you want to play in the playlist
* @see PlayerCallback#onCurrentMediaItemChanged(SessionPlayer, MediaItem)
*/
public abstract @NonNull ListenableFuture<PlayerResult> skipToPlaylistItem(
@IntRange(from = 0) int index);
/**
* Updates the playlist metadata while keeping the playlist as-is.
* <p>
* The implementation must notify registered callbacks with
* {@link PlayerCallback#onPlaylistMetadataChanged(SessionPlayer, MediaMetadata)} when it's
* completed.
*
* @param metadata metadata of the playlist
* @see PlayerCallback#onPlaylistMetadataChanged(SessionPlayer, MediaMetadata)
*/
public abstract @NonNull ListenableFuture<PlayerResult> updatePlaylistMetadata(
@Nullable MediaMetadata metadata);
/**
* Sets the repeat mode.
* <p>
* The implementation must notify registered callbacks with
* {@link PlayerCallback#onRepeatModeChanged(SessionPlayer, int)} when it's completed.
*
* @param repeatMode repeat mode
* @see #REPEAT_MODE_NONE
* @see #REPEAT_MODE_ONE
* @see #REPEAT_MODE_ALL
* @see #REPEAT_MODE_GROUP
* @see PlayerCallback#onRepeatModeChanged(SessionPlayer, int)
*/
public abstract @NonNull ListenableFuture<PlayerResult> setRepeatMode(
@RepeatMode int repeatMode);
/**
* Sets the shuffle mode.
* <p>
* The implementation must notify registered callbacks with
* {@link PlayerCallback#onShuffleModeChanged(SessionPlayer, int)} when it's completed.
*
* @param shuffleMode The shuffle mode
* @see #SHUFFLE_MODE_NONE
* @see #SHUFFLE_MODE_ALL
* @see #SHUFFLE_MODE_GROUP
* @see PlayerCallback#onShuffleModeChanged(SessionPlayer, int)
*/
public abstract @NonNull ListenableFuture<PlayerResult> setShuffleMode(
@ShuffleMode int shuffleMode);
/**
* Gets the playlist.
*
* @return playlist, or null if none is set.
* @see PlayerCallback#onPlaylistChanged(SessionPlayer, List, MediaMetadata)
*/
public abstract @Nullable List<MediaItem> getPlaylist();
/**
* Gets the playlist metadata.
*
* @return metadata metadata of the playlist, or null if none is set
* @see PlayerCallback#onPlaylistChanged(SessionPlayer, List, MediaMetadata)
* @see PlayerCallback#onPlaylistMetadataChanged(SessionPlayer, MediaMetadata)
*/
public abstract @Nullable MediaMetadata getPlaylistMetadata();
/**
* Gets the repeat mode.
*
* @return repeat mode
* @see #REPEAT_MODE_NONE
* @see #REPEAT_MODE_ONE
* @see #REPEAT_MODE_ALL
* @see #REPEAT_MODE_GROUP
* @see PlayerCallback#onRepeatModeChanged(SessionPlayer, int)
*/
public abstract @RepeatMode int getRepeatMode();
/**
* Gets the shuffle mode.
*
* @return The shuffle mode
* @see #SHUFFLE_MODE_NONE
* @see #SHUFFLE_MODE_ALL
* @see #SHUFFLE_MODE_GROUP
* @see PlayerCallback#onShuffleModeChanged(SessionPlayer, int)
*/
public abstract @ShuffleMode int getShuffleMode();
/**
* Gets the current media item.
*
* @return the current media item. Can be {@code null} only when media item or playlist hasn't
* been set.
*/
public abstract @Nullable MediaItem getCurrentMediaItem();
/**
* Gets the index of current media item in playlist. This value may be updated when
* {@link PlayerCallback#onCurrentMediaItemChanged(SessionPlayer, MediaItem)} or
* {@link PlayerCallback#onPlaylistChanged(SessionPlayer, List, MediaMetadata)} is called.
*
* @return the index of current media item. Can be -1 only when current media item is null or
* playlist hasn't been set.
*/
public abstract int getCurrentMediaItemIndex();
/**
* Gets the previous item index in the playlist. The returned value can be outdated after
* {@link PlayerCallback#onCurrentMediaItemChanged(SessionPlayer, MediaItem)} or
* {@link PlayerCallback#onPlaylistChanged(SessionPlayer, List, MediaMetadata)} is called.
*
* @return the index of previous media item. Can be -1 only when previous media item does not
* exist or playlist hasn't been set.
*/
public abstract int getPreviousMediaItemIndex();
/**
* Gets the next item index in the playlist. The returned value can be outdated after
* {@link PlayerCallback#onCurrentMediaItemChanged(SessionPlayer, MediaItem)} or
* {@link PlayerCallback#onPlaylistChanged(SessionPlayer, List, MediaMetadata)} is called.
*
* @return the index of next media item. Can be -1 only when next media item does not exist or
* playlist hasn't been set.
*/
public abstract int getNextMediaItemIndex();
// Listeners / Callback related
// Intentionally final not to allow developers to change the behavior
/**
* Register {@link PlayerCallback} to listen changes.
*
* @param executor a callback Executor
* @param callback a PlayerCallback
* @throws IllegalArgumentException if executor or callback is {@code null}.
*/
public final void registerPlayerCallback(
@NonNull /*@CallbackExecutor*/ Executor executor,
@NonNull PlayerCallback callback) {
if (executor == null) {
throw new IllegalArgumentException("executor shouldn't be null");
}
if (callback == null) {
throw new IllegalArgumentException("callback shouldn't be null");
}
synchronized (mLock) {
for (Pair<PlayerCallback, Executor> pair : mCallbacks) {
if (pair.first == callback && pair.second != null) {
Log.w(TAG, "callback is already added. Ignoring.");
return;
}
}
mCallbacks.add(new Pair<>(callback, executor));
}
}
/**
* Unregister the previously registered {@link PlayerCallback}.
*
* @param callback the callback to be removed
* @throws IllegalArgumentException if the callback is {@code null}.
*/
public final void unregisterPlayerCallback(@NonNull PlayerCallback callback) {
if (callback == null) {
throw new IllegalArgumentException("callback shouldn't be null");
}
synchronized (mLock) {
for (int i = mCallbacks.size() - 1; i >= 0; i--) {
if (mCallbacks.get(i).first == callback) {
mCallbacks.remove(i);
}
}
}
}
/**
* Gets the callbacks with executors for subclasses to notify player events.
*
* @return map of callbacks and its executors
*/
protected final @NonNull List<Pair<PlayerCallback, Executor>> getCallbacks() {
List<Pair<PlayerCallback, Executor>> list = new ArrayList<>();
synchronized (mLock) {
list.addAll(mCallbacks);
}
return list;
}
/**
* A callback class to receive notifications for events on the session player. See
* {@link #registerPlayerCallback(Executor, PlayerCallback)} to register this callback.
*/
public abstract static class PlayerCallback {
/**
* Called when the state of the player has changed.
*
* @param player the player whose state has changed.
* @param playerState the new state of the player.
* @see #getPlayerState() ()
*/
public void onPlayerStateChanged(@NonNull SessionPlayer player,
@PlayerState int playerState) {
}
/**
* Called when a buffering events for a media item happened.
*
* @param player the player that is buffering
* @param item the media item for which buffering is happening.
* @param buffState the new buffering state.
* @see #getBufferingState()
*/
public void onBufferingStateChanged(@NonNull SessionPlayer player,
@Nullable MediaItem item, @BuffState int buffState) {
}
/**
* Called when the playback speed has changed.
*
* @param player the player that has changed the playback speed.
* @param playbackSpeed the new playback speed.
* @see #getPlaybackSpeed()
*/
public void onPlaybackSpeedChanged(@NonNull SessionPlayer player,
float playbackSpeed) {
}
/**
* Called when {@link #seekTo(long)} is completed.
*
* @param player the player that has completed seeking.
* @param position the previous seeking request.
* @see #getCurrentPosition()
*/
public void onSeekCompleted(@NonNull SessionPlayer player, long position) {
}
/**
* Called when a playlist is changed.
*
* @param player the player that has changed the playlist and playlist metadata.
* @param list new playlist
* @param metadata new metadata
* @see #getPlaylist()
* @see #getPlaylistMetadata()
*/
public void onPlaylistChanged(@NonNull SessionPlayer player,
@Nullable List<MediaItem> list, @Nullable MediaMetadata metadata) {
}
/**
* Called when a playlist metadata is changed.
*
* @param player the player that has changed the playlist metadata.
* @param metadata new metadata
* @see #getPlaylistMetadata()
*/
public void onPlaylistMetadataChanged(@NonNull SessionPlayer player,
@Nullable MediaMetadata metadata) {
}
/**
* Called when the shuffle mode is changed.
*
* @param player playlist agent for this event
* @param shuffleMode shuffle mode
* @see #SHUFFLE_MODE_NONE
* @see #SHUFFLE_MODE_ALL
* @see #SHUFFLE_MODE_GROUP
* @see #getShuffleMode()
*/
public void onShuffleModeChanged(@NonNull SessionPlayer player,
@ShuffleMode int shuffleMode) {
}
/**
* Called when the repeat mode is changed.
*
* @param player player for this event
* @param repeatMode repeat mode
* @see #REPEAT_MODE_NONE
* @see #REPEAT_MODE_ONE
* @see #REPEAT_MODE_ALL
* @see #REPEAT_MODE_GROUP
* @see #getRepeatMode()
*/
public void onRepeatModeChanged(@NonNull SessionPlayer player,
@RepeatMode int repeatMode) {
}
/**
* Called when the player's current media item has changed.
*
* @param player the player whose media item changed.
* @param item the new current media item.
* @see #getCurrentMediaItem()
*/
public void onCurrentMediaItemChanged(@NonNull SessionPlayer player,
@NonNull MediaItem item) {
}
/**
* Called when the player finished playing. Playback state would be also set
* {@link #PLAYER_STATE_PAUSED} with it.
* <p>
* This will be called only when the repeat mode is set to {@link #REPEAT_MODE_NONE}.
*
* @param player the player whose playback is completed.
* @see #REPEAT_MODE_NONE
*/
public void onPlaybackCompleted(@NonNull SessionPlayer player) {
}
/**
* Called when the player's current audio attributes are changed.
*
* @param player the player whose audio attributes are changed.
* @param attributes the new current audio attributes
* @see #getAudioAttributes()
*/
public void onAudioAttributesChanged(@NonNull SessionPlayer player,
@Nullable AudioAttributesCompat attributes) {
}
}
/**
* Result class of the asynchronous APIs.
* <p>
* Subclass may extend this class for providing more result and/or custom result code. For the
* custom result code, follow the convention below to avoid potential code duplication.
* <p>
* <ul>
* <li>Predefined error code: Negative integers greater than -100. (i.e. -100 < code < 0)
* <li>Custom error code: Negative integers equal to or less than -1000. (i.e. code < -1000)
* <li>Predefined info code: Positive integers less than 100. (i.e. 0 < code < 100)
* <li>Custom Info code: Positive integers equal to or greater than 1000. (i.e. code > +1000)
* </ul>
*/
public static class PlayerResult implements BaseResult {
/**
* @hide
*/
@IntDef(flag = false, /*prefix = "RESULT_CODE",*/ value = {
RESULT_CODE_SUCCESS,
RESULT_CODE_UNKNOWN_ERROR,
RESULT_CODE_INVALID_STATE,
RESULT_CODE_BAD_VALUE,
RESULT_CODE_PERMISSION_DENIED,
RESULT_CODE_IO_ERROR,
RESULT_CODE_SKIPPED})
@Retention(RetentionPolicy.SOURCE)
@RestrictTo(LIBRARY_GROUP)
public @interface ResultCode {}
private final int mResultCode;
private final long mCompletionTime;
private final MediaItem mItem;
/**
* Constructor that uses the current system clock as the completion time.
*
* @param resultCode result code. Recommends to use the standard code defined here.
* @param item media item when the command is completed
*/
// Note: resultCode is intentionally not annotated for subclass to return extra error codes.
public PlayerResult(int resultCode, @Nullable MediaItem item) {
this(resultCode, item, SystemClock.elapsedRealtime());
}
// Note: resultCode is intentionally not annotated for subclass to return extra error codes.
private PlayerResult(int resultCode, @Nullable MediaItem item, long completionTime) {
mResultCode = resultCode;
mItem = item;
mCompletionTime = completionTime;
}
static ListenableFuture<PlayerResult> createFuture(int resultCode) {
ResolvableFuture<PlayerResult> result = ResolvableFuture.create();
result.set(new PlayerResult(resultCode, null));
return result;
}
/**
* Gets the result code.
* <p>
* Subclass of the {@link SessionPlayer} may have defined customized extra code other than
* codes defined here. Check the documentation of the class that you're interested in.
*
* @return result code.
* @see #RESULT_CODE_UNKNOWN_ERROR
* @see #RESULT_CODE_INVALID_STATE
* @see #RESULT_CODE_BAD_VALUE
* @see #RESULT_CODE_PERMISSION_DENIED
* @see #RESULT_CODE_IO_ERROR
* @see #RESULT_CODE_SKIPPED
*/
@Override
public @ResultCode int getResultCode() {
return mResultCode;
}
/**
* Gets the completion time of the command. Being more specific, it's the same as
* {@link android.os.SystemClock#elapsedRealtime()} when the command is completed.
*
* @return completion time of the command
*/
@Override
public long getCompletionTime() {
return mCompletionTime;
}
/**
* Gets the {@link MediaItem} for which the command was executed. In other words, this is
* the item sent as an argument of the command if any, otherwise the current media item when
* the command was completed.
*
* @return media item when the command is completed. Can be {@code null} for an error, or
* the current media item was {@code null}.
*/
@Override
public @Nullable MediaItem getMediaItem() {
return mItem;
}
}
}