Home | History | Annotate | Download | only in media
      1 /*
      2  * Copyright 2018 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package androidx.media;
     18 
     19 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
     20 
     21 import android.annotation.TargetApi;
     22 import android.app.PendingIntent;
     23 import android.content.Context;
     24 import android.media.AudioManager;
     25 import android.net.Uri;
     26 import android.os.Build;
     27 import android.os.Bundle;
     28 import android.os.ResultReceiver;
     29 import android.support.v4.media.MediaBrowserCompat;
     30 
     31 import androidx.annotation.IntDef;
     32 import androidx.annotation.NonNull;
     33 import androidx.annotation.Nullable;
     34 import androidx.annotation.RestrictTo;
     35 import androidx.annotation.VisibleForTesting;
     36 import androidx.media.MediaPlaylistAgent.RepeatMode;
     37 import androidx.media.MediaPlaylistAgent.ShuffleMode;
     38 import androidx.media.MediaSession2.CommandButton;
     39 import androidx.media.MediaSession2.ControllerInfo;
     40 import androidx.media.MediaSession2.ErrorCode;
     41 
     42 import java.lang.annotation.Retention;
     43 import java.lang.annotation.RetentionPolicy;
     44 import java.util.List;
     45 import java.util.concurrent.Executor;
     46 
     47 /**
     48  * Allows an app to interact with an active {@link MediaSession2} or a
     49  * {@link MediaSessionService2} in any status. Media buttons and other commands can be sent to
     50  * the session.
     51  * <p>
     52  * When you're done, use {@link #close()} to clean up resources. This also helps session service
     53  * to be destroyed when there's no controller associated with it.
     54  * <p>
     55  * When controlling {@link MediaSession2}, the controller will be available immediately after
     56  * the creation.
     57  * <p>
     58  * When controlling {@link MediaSessionService2}, the {@link MediaController2} would be
     59  * available only if the session service allows this controller by
     60  * {@link MediaSession2.SessionCallback#onConnect(MediaSession2, ControllerInfo)} for the service.
     61  * Wait {@link ControllerCallback#onConnected(MediaController2, SessionCommandGroup2)} or
     62  * {@link ControllerCallback#onDisconnected(MediaController2)} for the result.
     63  * <p>
     64  * MediaController2 objects are thread-safe.
     65  * <p>
     66  * @see MediaSession2
     67  * @see MediaSessionService2
     68  */
     69 @TargetApi(Build.VERSION_CODES.KITKAT)
     70 public class MediaController2 implements AutoCloseable {
     71     /**
     72      * @hide
     73      */
     74     @RestrictTo(LIBRARY_GROUP)
     75     @IntDef({AudioManager.ADJUST_LOWER, AudioManager.ADJUST_RAISE, AudioManager.ADJUST_SAME,
     76             AudioManager.ADJUST_MUTE, AudioManager.ADJUST_UNMUTE, AudioManager.ADJUST_TOGGLE_MUTE})
     77     @Retention(RetentionPolicy.SOURCE)
     78     public @interface VolumeDirection {}
     79 
     80     /**
     81      * @hide
     82      */
     83     @RestrictTo(LIBRARY_GROUP)
     84     @IntDef(value = {AudioManager.FLAG_SHOW_UI, AudioManager.FLAG_ALLOW_RINGER_MODES,
     85             AudioManager.FLAG_PLAY_SOUND, AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE,
     86             AudioManager.FLAG_VIBRATE}, flag = true)
     87     @Retention(RetentionPolicy.SOURCE)
     88     public @interface VolumeFlags {}
     89 
     90     private final SupportLibraryImpl mImpl;
     91     // For testing.
     92     Long mTimeDiff;
     93 
     94     /**
     95      * Create a {@link MediaController2} from the {@link SessionToken2}.
     96      * This connects to the session and may wake up the service if it's not available.
     97      *
     98      * @param context Context
     99      * @param token token to connect to
    100      * @param executor executor to run callbacks on.
    101      * @param callback controller callback to receive changes in
    102      */
    103     public MediaController2(@NonNull Context context, @NonNull SessionToken2 token,
    104             @NonNull Executor executor, @NonNull ControllerCallback callback) {
    105         mImpl = new MediaController2ImplBase(context, token, executor, callback);
    106         mImpl.setInstance(this);
    107     }
    108 
    109     /**
    110      * Release this object, and disconnect from the session. After this, callbacks wouldn't be
    111      * received.
    112      */
    113     @Override
    114     public void close() {
    115         try {
    116             mImpl.close();
    117         } catch (Exception e) {
    118             // Should not be here.
    119         }
    120     }
    121 
    122     /**
    123      * @return token
    124      */
    125     public @NonNull SessionToken2 getSessionToken() {
    126         return mImpl.getSessionToken();
    127     }
    128 
    129     /**
    130      * Returns whether this class is connected to active {@link MediaSession2} or not.
    131      */
    132     public boolean isConnected() {
    133         return mImpl.isConnected();
    134     }
    135 
    136     /**
    137      * Requests that the player starts or resumes playback.
    138      */
    139     public void play() {
    140         mImpl.play();
    141     }
    142 
    143     /**
    144      * Requests that the player pauses playback.
    145      */
    146     public void pause() {
    147         mImpl.pause();
    148     }
    149 
    150     /**
    151      * Requests that the player be reset to its uninitialized state.
    152      */
    153     public void reset() {
    154         mImpl.reset();
    155     }
    156 
    157     /**
    158      * Request that the player prepare its playback. In other words, other sessions can continue
    159      * to play during the preparation of this session. This method can be used to speed up the
    160      * start of the playback. Once the preparation is done, the session will change its playback
    161      * state to {@link MediaPlayerInterface#PLAYER_STATE_PAUSED}. Afterwards, {@link #play} can be
    162      * called to start playback.
    163      */
    164     public void prepare() {
    165         mImpl.prepare();
    166     }
    167 
    168     /**
    169      * Start fast forwarding. If playback is already fast forwarding this
    170      * may increase the rate.
    171      */
    172     public void fastForward() {
    173         mImpl.fastForward();
    174     }
    175 
    176     /**
    177      * Start rewinding. If playback is already rewinding this may increase
    178      * the rate.
    179      */
    180     public void rewind() {
    181         mImpl.rewind();
    182     }
    183 
    184     /**
    185      * Move to a new location in the media stream.
    186      *
    187      * @param pos Position to move to, in milliseconds.
    188      */
    189     public void seekTo(long pos) {
    190         mImpl.seekTo(pos);
    191     }
    192 
    193     /**
    194      * @hide
    195      */
    196     @RestrictTo(LIBRARY_GROUP)
    197     public void skipForward() {
    198         // To match with KEYCODE_MEDIA_SKIP_FORWARD
    199         mImpl.skipForward();
    200     }
    201 
    202     /**
    203      * @hide
    204      */
    205     @RestrictTo(LIBRARY_GROUP)
    206     public void skipBackward() {
    207         // To match with KEYCODE_MEDIA_SKIP_BACKWARD
    208         mImpl.skipBackward();
    209     }
    210 
    211     /**
    212      * Request that the player start playback for a specific media id.
    213      *
    214      * @param mediaId The id of the requested media.
    215      * @param extras Optional extras that can include extra information about the media item
    216      *               to be played.
    217      */
    218     public void playFromMediaId(@NonNull String mediaId, @Nullable Bundle extras) {
    219         mImpl.playFromMediaId(mediaId, extras);
    220     }
    221 
    222     /**
    223      * Request that the player start playback for a specific search query.
    224      *
    225      * @param query The search query. Should not be an empty string.
    226      * @param extras Optional extras that can include extra information about the query.
    227      */
    228     public void playFromSearch(@NonNull String query, @Nullable Bundle extras) {
    229         mImpl.playFromSearch(query, extras);
    230     }
    231 
    232     /**
    233      * Request that the player start playback for a specific {@link Uri}.
    234      *
    235      * @param uri The URI of the requested media.
    236      * @param extras Optional extras that can include extra information about the media item
    237      *               to be played.
    238      */
    239     public void playFromUri(@NonNull Uri uri, @Nullable Bundle extras) {
    240         mImpl.playFromUri(uri, extras);
    241     }
    242 
    243     /**
    244      * Request that the player prepare playback for a specific media id. In other words, other
    245      * sessions can continue to play during the preparation of this session. This method can be
    246      * used to speed up the start of the playback. Once the preparation is done, the session
    247      * will change its playback state to {@link MediaPlayerInterface#PLAYER_STATE_PAUSED}.
    248      * Afterwards, {@link #play} can be called to start playback. If the preparation is not needed,
    249      * {@link #playFromMediaId} can be directly called without this method.
    250      *
    251      * @param mediaId The id of the requested media.
    252      * @param extras Optional extras that can include extra information about the media item
    253      *               to be prepared.
    254      */
    255     public void prepareFromMediaId(@NonNull String mediaId, @Nullable Bundle extras) {
    256         mImpl.prepareFromMediaId(mediaId, extras);
    257     }
    258 
    259     /**
    260      * Request that the player prepare playback for a specific search query.
    261      * In other words, other sessions can continue to play during the preparation of this session.
    262      * This method can be used to speed up the start of the playback.
    263      * Once the preparation is done, the session will change its playback state to
    264      * {@link MediaPlayerInterface#PLAYER_STATE_PAUSED}. Afterwards,
    265      * {@link #play} can be called to start playback. If the preparation is not needed,
    266      * {@link #playFromSearch} can be directly called without this method.
    267      *
    268      * @param query The search query. Should not be an empty string.
    269      * @param extras Optional extras that can include extra information about the query.
    270      */
    271     public void prepareFromSearch(@NonNull String query, @Nullable Bundle extras) {
    272         mImpl.prepareFromSearch(query, extras);
    273     }
    274 
    275     /**
    276      * Request that the player prepare playback for a specific {@link Uri}. In other words,
    277      * other sessions can continue to play during the preparation of this session. This method
    278      * can be used to speed up the start of the playback. Once the preparation is done, the
    279      * session will change its playback state to {@link MediaPlayerInterface#PLAYER_STATE_PAUSED}.
    280      * Afterwards, {@link #play} can be called to start playback. If the preparation is not needed,
    281      * {@link #playFromUri} can be directly called without this method.
    282      *
    283      * @param uri The URI of the requested media.
    284      * @param extras Optional extras that can include extra information about the media item
    285      *               to be prepared.
    286      */
    287     public void prepareFromUri(@NonNull Uri uri, @Nullable Bundle extras) {
    288         mImpl.prepareFromUri(uri, extras);
    289     }
    290 
    291     /**
    292      * Set the volume of the output this session is playing on. The command will be ignored if it
    293      * does not support {@link VolumeProviderCompat#VOLUME_CONTROL_ABSOLUTE}.
    294      * <p>
    295      * If the session is local playback, this changes the device's volume with the stream that
    296      * session's player is using. Flags will be specified for the {@link AudioManager}.
    297      * <p>
    298      * If the session is remote player (i.e. session has set volume provider), its volume provider
    299      * will receive this request instead.
    300      *
    301      * @see #getPlaybackInfo()
    302      * @param value The value to set it to, between 0 and the reported max.
    303      * @param flags flags from {@link AudioManager} to include with the volume request for local
    304      *              playback
    305      */
    306     public void setVolumeTo(int value, @VolumeFlags int flags) {
    307         mImpl.setVolumeTo(value, flags);
    308     }
    309 
    310     /**
    311      * Adjust the volume of the output this session is playing on. The direction
    312      * must be one of {@link AudioManager#ADJUST_LOWER},
    313      * {@link AudioManager#ADJUST_RAISE}, or {@link AudioManager#ADJUST_SAME}.
    314      * <p>
    315      * The command will be ignored if the session does not support
    316      * {@link VolumeProviderCompat#VOLUME_CONTROL_RELATIVE} or
    317      * {@link VolumeProviderCompat#VOLUME_CONTROL_ABSOLUTE}.
    318      * <p>
    319      * If the session is local playback, this changes the device's volume with the stream that
    320      * session's player is using. Flags will be specified for the {@link AudioManager}.
    321      * <p>
    322      * If the session is remote player (i.e. session has set volume provider), its volume provider
    323      * will receive this request instead.
    324      *
    325      * @see #getPlaybackInfo()
    326      * @param direction The direction to adjust the volume in.
    327      * @param flags flags from {@link AudioManager} to include with the volume request for local
    328      *              playback
    329      */
    330     public void adjustVolume(@VolumeDirection int direction, @VolumeFlags int flags) {
    331         mImpl.adjustVolume(direction, flags);
    332     }
    333 
    334     /**
    335      * Get an intent for launching UI associated with this session if one exists.
    336      *
    337      * @return A {@link PendingIntent} to launch UI or null.
    338      */
    339     public @Nullable PendingIntent getSessionActivity() {
    340         return mImpl.getSessionActivity();
    341     }
    342 
    343     /**
    344      * Get the lastly cached player state from
    345      * {@link ControllerCallback#onPlayerStateChanged(MediaController2, int)}.
    346      *
    347      * @return player state
    348      */
    349     public int getPlayerState() {
    350         return mImpl.getPlayerState();
    351     }
    352 
    353     /**
    354      * Gets the duration of the current media item, or {@link MediaPlayerInterface#UNKNOWN_TIME} if
    355      * unknown.
    356      * @return the duration in ms, or {@link MediaPlayerInterface#UNKNOWN_TIME}.
    357      */
    358     public long getDuration() {
    359         return mImpl.getDuration();
    360     }
    361 
    362     /**
    363      * Gets the current playback position.
    364      * <p>
    365      * This returns the calculated value of the position, based on the difference between the
    366      * update time and current time.
    367      *
    368      * @return position
    369      */
    370     public long getCurrentPosition() {
    371         return mImpl.getCurrentPosition();
    372     }
    373 
    374     /**
    375      * Get the lastly cached playback speed from
    376      * {@link ControllerCallback#onPlaybackSpeedChanged(MediaController2, float)}.
    377      *
    378      * @return speed the lastly cached playback speed, or 0.0f if unknown.
    379      */
    380     public float getPlaybackSpeed() {
    381         return mImpl.getPlaybackSpeed();
    382     }
    383 
    384     /**
    385      * Set the playback speed.
    386      */
    387     public void setPlaybackSpeed(float speed) {
    388         mImpl.setPlaybackSpeed(speed);
    389     }
    390 
    391     /**
    392      * Gets the current buffering state of the player.
    393      * During buffering, see {@link #getBufferedPosition()} for the quantifying the amount already
    394      * buffered.
    395      * @return the buffering state.
    396      */
    397     public @MediaPlayerInterface.BuffState int getBufferingState() {
    398         return mImpl.getBufferingState();
    399     }
    400 
    401     /**
    402      * Gets the lastly cached buffered position from the session when
    403      * {@link ControllerCallback#onBufferingStateChanged(MediaController2, MediaItem2, int)} is
    404      * called.
    405      *
    406      * @return buffering position in millis, or {@link MediaPlayerInterface#UNKNOWN_TIME} if
    407      * unknown.
    408      */
    409     public long getBufferedPosition() {
    410         return mImpl.getBufferedPosition();
    411     }
    412 
    413     /**
    414      * Get the current playback info for this session.
    415      *
    416      * @return The current playback info or null.
    417      */
    418     public @Nullable PlaybackInfo getPlaybackInfo() {
    419         return mImpl.getPlaybackInfo();
    420     }
    421 
    422     /**
    423      * Rate the media. This will cause the rating to be set for the current user.
    424      * The rating style must follow the user rating style from the session.
    425      * You can get the rating style from the session through the
    426      * {@link MediaMetadata2#getRating(String)} with the key
    427      * {@link MediaMetadata2#METADATA_KEY_USER_RATING}.
    428      * <p>
    429      * If the user rating was {@code null}, the media item does not accept setting user rating.
    430      *
    431      * @param mediaId The id of the media
    432      * @param rating The rating to set
    433      */
    434     public void setRating(@NonNull String mediaId, @NonNull Rating2 rating) {
    435         mImpl.setRating(mediaId, rating);
    436     }
    437 
    438     /**
    439      * Send custom command to the session
    440      *
    441      * @param command custom command
    442      * @param args optional argument
    443      * @param cb optional result receiver
    444      */
    445     public void sendCustomCommand(@NonNull SessionCommand2 command, @Nullable Bundle args,
    446             @Nullable ResultReceiver cb) {
    447         mImpl.sendCustomCommand(command, args, cb);
    448     }
    449 
    450     /**
    451      * Returns the cached playlist from {@link ControllerCallback#onPlaylistChanged}.
    452      * <p>
    453      * This list may differ with the list that was specified with
    454      * {@link #setPlaylist(List, MediaMetadata2)} depending on the {@link MediaPlaylistAgent}
    455      * implementation. Use media items returned here for other playlist agent APIs such as
    456      * {@link MediaPlaylistAgent#skipToPlaylistItem(MediaItem2)}.
    457      *
    458      * @return playlist. Can be {@code null} if the playlist hasn't set nor controller doesn't have
    459      *      enough permission.
    460      * @see SessionCommand2#COMMAND_CODE_PLAYLIST_GET_LIST
    461      */
    462     public @Nullable List<MediaItem2> getPlaylist() {
    463         return mImpl.getPlaylist();
    464     }
    465 
    466     /**
    467      * Sets the playlist.
    468      * <p>
    469      * Even when the playlist is successfully set, use the playlist returned from
    470      * {@link #getPlaylist()} for playlist APIs such as {@link #skipToPlaylistItem(MediaItem2)}.
    471      * Otherwise the session in the remote process can't distinguish between media items.
    472      *
    473      * @param list playlist
    474      * @param metadata metadata of the playlist
    475      * @see #getPlaylist()
    476      * @see ControllerCallback#onPlaylistChanged
    477      */
    478     public void setPlaylist(@NonNull List<MediaItem2> list, @Nullable MediaMetadata2 metadata) {
    479         mImpl.setPlaylist(list, metadata);
    480     }
    481 
    482     /**
    483      * Updates the playlist metadata
    484      *
    485      * @param metadata metadata of the playlist
    486      */
    487     public void updatePlaylistMetadata(@Nullable MediaMetadata2 metadata) {
    488         mImpl.updatePlaylistMetadata(metadata);
    489     }
    490 
    491     /**
    492      * Gets the lastly cached playlist playlist metadata either from
    493      * {@link ControllerCallback#onPlaylistMetadataChanged or
    494      * {@link ControllerCallback#onPlaylistChanged}.
    495      *
    496      * @return metadata metadata of the playlist, or null if none is set
    497      */
    498     public @Nullable MediaMetadata2 getPlaylistMetadata() {
    499         return mImpl.getPlaylistMetadata();
    500     }
    501 
    502     /**
    503      * Adds the media item to the playlist at position index. Index equals or greater than
    504      * the current playlist size (e.g. {@link Integer#MAX_VALUE}) will add the item at the end of
    505      * the playlist.
    506      * <p>
    507      * This will not change the currently playing media item.
    508      * If index is less than or equal to the current index of the playlist,
    509      * the current index of the playlist will be incremented correspondingly.
    510      *
    511      * @param index the index you want to add
    512      * @param item the media item you want to add
    513      */
    514     public void addPlaylistItem(int index, @NonNull MediaItem2 item) {
    515         mImpl.addPlaylistItem(index, item);
    516     }
    517 
    518     /**
    519      * Removes the media item at index in the playlist.
    520      *<p>
    521      * If the item is the currently playing item of the playlist, current playback
    522      * will be stopped and playback moves to next source in the list.
    523      *
    524      * @param item the media item you want to add
    525      */
    526     public void removePlaylistItem(@NonNull MediaItem2 item) {
    527         mImpl.removePlaylistItem(item);
    528     }
    529 
    530     /**
    531      * Replace the media item at index in the playlist. This can be also used to update metadata of
    532      * an item.
    533      *
    534      * @param index the index of the item to replace
    535      * @param item the new item
    536      */
    537     public void replacePlaylistItem(int index, @NonNull MediaItem2 item) {
    538         mImpl.replacePlaylistItem(index, item);
    539     }
    540 
    541     /**
    542      * Get the lastly cached current item from
    543      * {@link ControllerCallback#onCurrentMediaItemChanged(MediaController2, MediaItem2)}.
    544      *
    545      * @return the currently playing item, or null if unknown.
    546      */
    547     public MediaItem2 getCurrentMediaItem() {
    548         return mImpl.getCurrentMediaItem();
    549     }
    550 
    551     /**
    552      * Skips to the previous item in the playlist.
    553      * <p>
    554      * This calls {@link MediaPlaylistAgent#skipToPreviousItem()}.
    555      */
    556     public void skipToPreviousItem() {
    557         mImpl.skipToPreviousItem();
    558     }
    559 
    560     /**
    561      * Skips to the next item in the playlist.
    562      * <p>
    563      * This calls {@link MediaPlaylistAgent#skipToNextItem()}.
    564      */
    565     public void skipToNextItem() {
    566         mImpl.skipToNextItem();
    567     }
    568 
    569     /**
    570      * Skips to the item in the playlist.
    571      * <p>
    572      * This calls {@link MediaPlaylistAgent#skipToPlaylistItem(MediaItem2)}.
    573      *
    574      * @param item The item in the playlist you want to play
    575      */
    576     public void skipToPlaylistItem(@NonNull MediaItem2 item) {
    577         mImpl.skipToPlaylistItem(item);
    578     }
    579 
    580     /**
    581      * Gets the cached repeat mode from the {@link ControllerCallback#onRepeatModeChanged}.
    582      *
    583      * @return repeat mode
    584      * @see MediaPlaylistAgent#REPEAT_MODE_NONE
    585      * @see MediaPlaylistAgent#REPEAT_MODE_ONE
    586      * @see MediaPlaylistAgent#REPEAT_MODE_ALL
    587      * @see MediaPlaylistAgent#REPEAT_MODE_GROUP
    588      */
    589     public @RepeatMode int getRepeatMode() {
    590         return mImpl.getRepeatMode();
    591     }
    592 
    593     /**
    594      * Sets the repeat mode.
    595      *
    596      * @param repeatMode repeat mode
    597      * @see MediaPlaylistAgent#REPEAT_MODE_NONE
    598      * @see MediaPlaylistAgent#REPEAT_MODE_ONE
    599      * @see MediaPlaylistAgent#REPEAT_MODE_ALL
    600      * @see MediaPlaylistAgent#REPEAT_MODE_GROUP
    601      */
    602     public void setRepeatMode(@RepeatMode int repeatMode) {
    603         mImpl.setRepeatMode(repeatMode);
    604     }
    605 
    606     /**
    607      * Gets the cached shuffle mode from the {@link ControllerCallback#onShuffleModeChanged}.
    608      *
    609      * @return The shuffle mode
    610      * @see MediaPlaylistAgent#SHUFFLE_MODE_NONE
    611      * @see MediaPlaylistAgent#SHUFFLE_MODE_ALL
    612      * @see MediaPlaylistAgent#SHUFFLE_MODE_GROUP
    613      */
    614     public @ShuffleMode int getShuffleMode() {
    615         return mImpl.getShuffleMode();
    616     }
    617 
    618     /**
    619      * Sets the shuffle mode.
    620      *
    621      * @param shuffleMode The shuffle mode
    622      * @see MediaPlaylistAgent#SHUFFLE_MODE_NONE
    623      * @see MediaPlaylistAgent#SHUFFLE_MODE_ALL
    624      * @see MediaPlaylistAgent#SHUFFLE_MODE_GROUP
    625      */
    626     public void setShuffleMode(@ShuffleMode int shuffleMode) {
    627         mImpl.setShuffleMode(shuffleMode);
    628     }
    629 
    630     /**
    631      * Queries for information about the routes currently known.
    632      */
    633     public void subscribeRoutesInfo() {
    634         mImpl.subscribeRoutesInfo();
    635     }
    636 
    637     /**
    638      * Unsubscribes for changes to the routes.
    639      * <p>
    640      * The {@link ControllerCallback#onRoutesInfoChanged callback} will no longer be invoked for
    641      * the routes once this method returns.
    642      * </p>
    643      */
    644     public void unsubscribeRoutesInfo() {
    645         mImpl.unsubscribeRoutesInfo();
    646     }
    647 
    648     /**
    649      * Selects the specified route.
    650      *
    651      * @param route The route to select.
    652      */
    653     public void selectRoute(@NonNull Bundle route) {
    654         mImpl.selectRoute(route);
    655     }
    656 
    657     @NonNull Context getContext() {
    658         return mImpl.getContext();
    659     }
    660 
    661     @NonNull ControllerCallback getCallback() {
    662         return mImpl.getCallback();
    663     }
    664 
    665     @NonNull Executor getCallbackExecutor() {
    666         return mImpl.getCallbackExecutor();
    667     }
    668 
    669     @Nullable MediaBrowserCompat getBrowserCompat() {
    670         return mImpl.getBrowserCompat();
    671     }
    672 
    673     /**
    674      * Sets the time diff forcefully when calculating current position.
    675      * @param timeDiff {@code null} for reset.
    676      */
    677     @VisibleForTesting
    678     void setTimeDiff(Long timeDiff) {
    679         mTimeDiff = timeDiff;
    680     }
    681 
    682     interface SupportLibraryImpl extends AutoCloseable {
    683         void setInstance(MediaController2 controller);
    684         SessionToken2 getSessionToken();
    685         boolean isConnected();
    686         void play();
    687         void pause();
    688         void reset();
    689         void prepare();
    690         void fastForward();
    691         void rewind();
    692         void seekTo(long pos);
    693         void skipForward();
    694         void skipBackward();
    695         void playFromMediaId(@NonNull String mediaId, @Nullable Bundle extras);
    696         void playFromSearch(@NonNull String query, @Nullable Bundle extras);
    697         void playFromUri(@NonNull Uri uri, @Nullable Bundle extras);
    698         void prepareFromMediaId(@NonNull String mediaId, @Nullable Bundle extras);
    699         void prepareFromSearch(@NonNull String query, @Nullable Bundle extras);
    700         void prepareFromUri(@NonNull Uri uri, @Nullable Bundle extras);
    701         void setVolumeTo(int value, @VolumeFlags int flags);
    702         void adjustVolume(@VolumeDirection int direction, @VolumeFlags int flags);
    703         @Nullable PendingIntent getSessionActivity();
    704         int getPlayerState();
    705         long getDuration();
    706         long getCurrentPosition();
    707         float getPlaybackSpeed();
    708         void setPlaybackSpeed(float speed);
    709         @MediaPlayerInterface.BuffState int getBufferingState();
    710         long getBufferedPosition();
    711         @Nullable PlaybackInfo getPlaybackInfo();
    712         void setRating(@NonNull String mediaId, @NonNull Rating2 rating);
    713         void sendCustomCommand(@NonNull SessionCommand2 command, @Nullable Bundle args,
    714                 @Nullable ResultReceiver cb);
    715         @Nullable List<MediaItem2> getPlaylist();
    716         void setPlaylist(@NonNull List<MediaItem2> list, @Nullable MediaMetadata2 metadata);
    717         void updatePlaylistMetadata(@Nullable MediaMetadata2 metadata);
    718         @Nullable MediaMetadata2 getPlaylistMetadata();
    719         void addPlaylistItem(int index, @NonNull MediaItem2 item);
    720         void removePlaylistItem(@NonNull MediaItem2 item);
    721         void replacePlaylistItem(int index, @NonNull MediaItem2 item);
    722         MediaItem2 getCurrentMediaItem();
    723         void skipToPreviousItem();
    724         void skipToNextItem();
    725         void skipToPlaylistItem(@NonNull MediaItem2 item);
    726         @RepeatMode int getRepeatMode();
    727         void setRepeatMode(@RepeatMode int repeatMode);
    728         @ShuffleMode int getShuffleMode();
    729         void setShuffleMode(@ShuffleMode int shuffleMode);
    730         void subscribeRoutesInfo();
    731         void unsubscribeRoutesInfo();
    732         void selectRoute(@NonNull Bundle route);
    733 
    734         // For MediaBrowser2
    735         @NonNull Context getContext();
    736         @NonNull ControllerCallback getCallback();
    737         @NonNull Executor getCallbackExecutor();
    738         @Nullable MediaBrowserCompat getBrowserCompat();
    739     }
    740 
    741     /**
    742      * Interface for listening to change in activeness of the {@link MediaSession2}.  It's
    743      * active if and only if it has set a player.
    744      */
    745     public abstract static class ControllerCallback {
    746         /**
    747          * Called when the controller is successfully connected to the session. The controller
    748          * becomes available afterwards.
    749          *
    750          * @param controller the controller for this event
    751          * @param allowedCommands commands that's allowed by the session.
    752          */
    753         public void onConnected(@NonNull MediaController2 controller,
    754                 @NonNull SessionCommandGroup2 allowedCommands) { }
    755 
    756         /**
    757          * Called when the session refuses the controller or the controller is disconnected from
    758          * the session. The controller becomes unavailable afterwards and the callback wouldn't
    759          * be called.
    760          * <p>
    761          * It will be also called after the {@link #close()}, so you can put clean up code here.
    762          * You don't need to call {@link #close()} after this.
    763          *
    764          * @param controller the controller for this event
    765          */
    766         public void onDisconnected(@NonNull MediaController2 controller) { }
    767 
    768         /**
    769          * Called when the session set the custom layout through the
    770          * {@link MediaSession2#setCustomLayout(ControllerInfo, List)}.
    771          * <p>
    772          * Can be called before {@link #onConnected(MediaController2, SessionCommandGroup2)}
    773          * is called.
    774          *
    775          * @param controller the controller for this event
    776          * @param layout
    777          */
    778         public void onCustomLayoutChanged(@NonNull MediaController2 controller,
    779                 @NonNull List<CommandButton> layout) { }
    780 
    781         /**
    782          * Called when the session has changed anything related with the {@link PlaybackInfo}.
    783          *
    784          * @param controller the controller for this event
    785          * @param info new playback info
    786          */
    787         public void onPlaybackInfoChanged(@NonNull MediaController2 controller,
    788                 @NonNull PlaybackInfo info) { }
    789 
    790         /**
    791          * Called when the allowed commands are changed by session.
    792          *
    793          * @param controller the controller for this event
    794          * @param commands newly allowed commands
    795          */
    796         public void onAllowedCommandsChanged(@NonNull MediaController2 controller,
    797                 @NonNull SessionCommandGroup2 commands) { }
    798 
    799         /**
    800          * Called when the session sent a custom command.
    801          *
    802          * @param controller the controller for this event
    803          * @param command
    804          * @param args
    805          * @param receiver
    806          */
    807         public void onCustomCommand(@NonNull MediaController2 controller,
    808                 @NonNull SessionCommand2 command, @Nullable Bundle args,
    809                 @Nullable ResultReceiver receiver) { }
    810 
    811         /**
    812          * Called when the player state is changed.
    813          *
    814          * @param controller the controller for this event
    815          * @param state
    816          */
    817         public void onPlayerStateChanged(@NonNull MediaController2 controller, int state) { }
    818 
    819         /**
    820          * Called when playback speed is changed.
    821          *
    822          * @param controller the controller for this event
    823          * @param speed speed
    824          */
    825         public void onPlaybackSpeedChanged(@NonNull MediaController2 controller,
    826                 float speed) { }
    827 
    828         /**
    829          * Called to report buffering events for a data source.
    830          * <p>
    831          * Use {@link #getBufferedPosition()} for current buffering position.
    832          *
    833          * @param controller the controller for this event
    834          * @param item the media item for which buffering is happening.
    835          * @param state the new buffering state.
    836          */
    837         public void onBufferingStateChanged(@NonNull MediaController2 controller,
    838                 @NonNull MediaItem2 item, @MediaPlayerInterface.BuffState int state) { }
    839 
    840         /**
    841          * Called to indicate that seeking is completed.
    842          *
    843          * @param controller the controller for this event.
    844          * @param position the previous seeking request.
    845          */
    846         public void onSeekCompleted(@NonNull MediaController2 controller, long position) { }
    847 
    848         /**
    849          * Called when a error from
    850          *
    851          * @param controller the controller for this event
    852          * @param errorCode error code
    853          * @param extras extra information
    854          */
    855         public void onError(@NonNull MediaController2 controller, @ErrorCode int errorCode,
    856                 @Nullable Bundle extras) { }
    857 
    858         /**
    859          * Called when the player's currently playing item is changed
    860          * <p>
    861          * When it's called, you should invalidate previous playback information and wait for later
    862          * callbacks.
    863          *
    864          * @param controller the controller for this event
    865          * @param item new item
    866          * @see #onBufferingStateChanged(MediaController2, MediaItem2, int)
    867          */
    868         public void onCurrentMediaItemChanged(@NonNull MediaController2 controller,
    869                 @Nullable MediaItem2 item) { }
    870 
    871         /**
    872          * Called when a playlist is changed.
    873          *
    874          * @param controller the controller for this event
    875          * @param list new playlist
    876          * @param metadata new metadata
    877          */
    878         public void onPlaylistChanged(@NonNull MediaController2 controller,
    879                 @NonNull List<MediaItem2> list, @Nullable MediaMetadata2 metadata) { }
    880 
    881         /**
    882          * Called when a playlist metadata is changed.
    883          *
    884          * @param controller the controller for this event
    885          * @param metadata new metadata
    886          */
    887         public void onPlaylistMetadataChanged(@NonNull MediaController2 controller,
    888                 @Nullable MediaMetadata2 metadata) { }
    889 
    890         /**
    891          * Called when the shuffle mode is changed.
    892          *
    893          * @param controller the controller for this event
    894          * @param shuffleMode repeat mode
    895          * @see MediaPlaylistAgent#SHUFFLE_MODE_NONE
    896          * @see MediaPlaylistAgent#SHUFFLE_MODE_ALL
    897          * @see MediaPlaylistAgent#SHUFFLE_MODE_GROUP
    898          */
    899         public void onShuffleModeChanged(@NonNull MediaController2 controller,
    900                 @MediaPlaylistAgent.ShuffleMode int shuffleMode) { }
    901 
    902         /**
    903          * Called when the repeat mode is changed.
    904          *
    905          * @param controller the controller for this event
    906          * @param repeatMode repeat mode
    907          * @see MediaPlaylistAgent#REPEAT_MODE_NONE
    908          * @see MediaPlaylistAgent#REPEAT_MODE_ONE
    909          * @see MediaPlaylistAgent#REPEAT_MODE_ALL
    910          * @see MediaPlaylistAgent#REPEAT_MODE_GROUP
    911          */
    912         public void onRepeatModeChanged(@NonNull MediaController2 controller,
    913                 @MediaPlaylistAgent.RepeatMode int repeatMode) { }
    914 
    915         /**
    916          * Called when a property of the indicated media route has changed.
    917          *
    918          * @param controller the controller for this event
    919          * @param routes The list of Bundle from MediaRouteDescriptor.asBundle().
    920          *              See MediaRouteDescriptor.fromBundle(Bundle bundle) to get
    921          *              MediaRouteDescriptor object from the {@code routes}
    922          */
    923         public void onRoutesInfoChanged(@NonNull MediaController2 controller,
    924                 @Nullable List<Bundle> routes) { }
    925     }
    926 
    927     /**
    928      * Holds information about the the way volume is handled for this session.
    929      */
    930     // The same as MediaController.PlaybackInfo
    931     public static final class PlaybackInfo {
    932         private static final String KEY_PLAYBACK_TYPE = "android.media.audio_info.playback_type";
    933         private static final String KEY_CONTROL_TYPE = "android.media.audio_info.control_type";
    934         private static final String KEY_MAX_VOLUME = "android.media.audio_info.max_volume";
    935         private static final String KEY_CURRENT_VOLUME = "android.media.audio_info.current_volume";
    936         private static final String KEY_AUDIO_ATTRIBUTES = "android.media.audio_info.audio_attrs";
    937 
    938         private final int mPlaybackType;
    939         private final int mControlType;
    940         private final int mMaxVolume;
    941         private final int mCurrentVolume;
    942         private final AudioAttributesCompat mAudioAttrsCompat;
    943 
    944         /**
    945          * The session uses remote playback.
    946          */
    947         public static final int PLAYBACK_TYPE_REMOTE = 2;
    948         /**
    949          * The session uses local playback.
    950          */
    951         public static final int PLAYBACK_TYPE_LOCAL = 1;
    952 
    953         PlaybackInfo(int playbackType, AudioAttributesCompat attrs, int controlType, int max,
    954                 int current) {
    955             mPlaybackType = playbackType;
    956             mAudioAttrsCompat = attrs;
    957             mControlType = controlType;
    958             mMaxVolume = max;
    959             mCurrentVolume = current;
    960         }
    961 
    962         /**
    963          * Get the type of playback which affects volume handling. One of:
    964          * <ul>
    965          * <li>{@link #PLAYBACK_TYPE_LOCAL}</li>
    966          * <li>{@link #PLAYBACK_TYPE_REMOTE}</li>
    967          * </ul>
    968          *
    969          * @return The type of playback this session is using.
    970          */
    971         public int getPlaybackType() {
    972             return mPlaybackType;
    973         }
    974 
    975         /**
    976          * Get the audio attributes for this session. The attributes will affect
    977          * volume handling for the session. When the volume type is
    978          * {@link #PLAYBACK_TYPE_REMOTE} these may be ignored by the
    979          * remote volume handler.
    980          *
    981          * @return The attributes for this session.
    982          */
    983         public AudioAttributesCompat getAudioAttributes() {
    984             return mAudioAttrsCompat;
    985         }
    986 
    987         /**
    988          * Get the type of volume control that can be used. One of:
    989          * <ul>
    990          * <li>{@link VolumeProviderCompat#VOLUME_CONTROL_ABSOLUTE}</li>
    991          * <li>{@link VolumeProviderCompat#VOLUME_CONTROL_RELATIVE}</li>
    992          * <li>{@link VolumeProviderCompat#VOLUME_CONTROL_FIXED}</li>
    993          * </ul>
    994          *
    995          * @return The type of volume control that may be used with this session.
    996          */
    997         public int getControlType() {
    998             return mControlType;
    999         }
   1000 
   1001         /**
   1002          * Get the maximum volume that may be set for this session.
   1003          *
   1004          * @return The maximum allowed volume where this session is playing.
   1005          */
   1006         public int getMaxVolume() {
   1007             return mMaxVolume;
   1008         }
   1009 
   1010         /**
   1011          * Get the current volume for this session.
   1012          *
   1013          * @return The current volume where this session is playing.
   1014          */
   1015         public int getCurrentVolume() {
   1016             return mCurrentVolume;
   1017         }
   1018 
   1019         Bundle toBundle() {
   1020             Bundle bundle = new Bundle();
   1021             bundle.putInt(KEY_PLAYBACK_TYPE, mPlaybackType);
   1022             bundle.putInt(KEY_CONTROL_TYPE, mControlType);
   1023             bundle.putInt(KEY_MAX_VOLUME, mMaxVolume);
   1024             bundle.putInt(KEY_CURRENT_VOLUME, mCurrentVolume);
   1025             if (mAudioAttrsCompat != null) {
   1026                 bundle.putBundle(KEY_AUDIO_ATTRIBUTES, mAudioAttrsCompat.toBundle());
   1027             }
   1028             return bundle;
   1029         }
   1030 
   1031         static PlaybackInfo createPlaybackInfo(int playbackType, AudioAttributesCompat attrs,
   1032                 int controlType, int max, int current) {
   1033             return new PlaybackInfo(playbackType, attrs, controlType, max, current);
   1034         }
   1035 
   1036         static PlaybackInfo fromBundle(Bundle bundle) {
   1037             if (bundle == null) {
   1038                 return null;
   1039             }
   1040             final int volumeType = bundle.getInt(KEY_PLAYBACK_TYPE);
   1041             final int volumeControl = bundle.getInt(KEY_CONTROL_TYPE);
   1042             final int maxVolume = bundle.getInt(KEY_MAX_VOLUME);
   1043             final int currentVolume = bundle.getInt(KEY_CURRENT_VOLUME);
   1044             final AudioAttributesCompat attrs = AudioAttributesCompat.fromBundle(
   1045                     bundle.getBundle(KEY_AUDIO_ATTRIBUTES));
   1046             return createPlaybackInfo(volumeType, attrs, volumeControl, maxVolume,
   1047                     currentVolume);
   1048         }
   1049     }
   1050 }
   1051