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      */
MediaController2(@onNull Context context, @NonNull SessionToken2 token, @NonNull Executor executor, @NonNull ControllerCallback callback)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
close()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      */
getSessionToken()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      */
isConnected()132     public boolean isConnected() {
133         return mImpl.isConnected();
134     }
135 
136     /**
137      * Requests that the player starts or resumes playback.
138      */
play()139     public void play() {
140         mImpl.play();
141     }
142 
143     /**
144      * Requests that the player pauses playback.
145      */
pause()146     public void pause() {
147         mImpl.pause();
148     }
149 
150     /**
151      * Requests that the player be reset to its uninitialized state.
152      */
reset()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      */
prepare()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      */
fastForward()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      */
rewind()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      */
seekTo(long pos)189     public void seekTo(long pos) {
190         mImpl.seekTo(pos);
191     }
192 
193     /**
194      * @hide
195      */
196     @RestrictTo(LIBRARY_GROUP)
skipForward()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)
skipBackward()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      */
playFromMediaId(@onNull String mediaId, @Nullable Bundle extras)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      */
playFromSearch(@onNull String query, @Nullable Bundle extras)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      */
playFromUri(@onNull Uri uri, @Nullable Bundle extras)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      */
prepareFromMediaId(@onNull String mediaId, @Nullable Bundle extras)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      */
prepareFromSearch(@onNull String query, @Nullable Bundle extras)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      */
prepareFromUri(@onNull Uri uri, @Nullable Bundle extras)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      */
setVolumeTo(int value, @VolumeFlags int flags)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      */
adjustVolume(@olumeDirection int direction, @VolumeFlags int flags)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      */
getSessionActivity()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      */
getPlayerState()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      */
getDuration()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      */
getCurrentPosition()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      */
getPlaybackSpeed()380     public float getPlaybackSpeed() {
381         return mImpl.getPlaybackSpeed();
382     }
383 
384     /**
385      * Set the playback speed.
386      */
setPlaybackSpeed(float speed)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      */
getBufferingState()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      */
getBufferedPosition()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      */
getPlaybackInfo()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      */
setRating(@onNull String mediaId, @NonNull Rating2 rating)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      */
sendCustomCommand(@onNull SessionCommand2 command, @Nullable Bundle args, @Nullable ResultReceiver cb)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      */
getPlaylist()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      */
setPlaylist(@onNull List<MediaItem2> list, @Nullable MediaMetadata2 metadata)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      */
updatePlaylistMetadata(@ullable MediaMetadata2 metadata)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      */
getPlaylistMetadata()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      */
addPlaylistItem(int index, @NonNull MediaItem2 item)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      */
removePlaylistItem(@onNull MediaItem2 item)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      */
replacePlaylistItem(int index, @NonNull MediaItem2 item)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      */
getCurrentMediaItem()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      */
skipToPreviousItem()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      */
skipToNextItem()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      */
skipToPlaylistItem(@onNull MediaItem2 item)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      */
getRepeatMode()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      */
setRepeatMode(@epeatMode int repeatMode)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      */
getShuffleMode()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      */
setShuffleMode(@huffleMode int shuffleMode)626     public void setShuffleMode(@ShuffleMode int shuffleMode) {
627         mImpl.setShuffleMode(shuffleMode);
628     }
629 
630     /**
631      * Queries for information about the routes currently known.
632      */
subscribeRoutesInfo()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      */
unsubscribeRoutesInfo()644     public void unsubscribeRoutesInfo() {
645         mImpl.unsubscribeRoutesInfo();
646     }
647 
648     /**
649      * Selects the specified route.
650      *
651      * @param route The route to select.
652      */
selectRoute(@onNull Bundle route)653     public void selectRoute(@NonNull Bundle route) {
654         mImpl.selectRoute(route);
655     }
656 
getContext()657     @NonNull Context getContext() {
658         return mImpl.getContext();
659     }
660 
getCallback()661     @NonNull ControllerCallback getCallback() {
662         return mImpl.getCallback();
663     }
664 
getCallbackExecutor()665     @NonNull Executor getCallbackExecutor() {
666         return mImpl.getCallbackExecutor();
667     }
668 
getBrowserCompat()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
setTimeDiff(Long timeDiff)678     void setTimeDiff(Long timeDiff) {
679         mTimeDiff = timeDiff;
680     }
681 
682     interface SupportLibraryImpl extends AutoCloseable {
setInstance(MediaController2 controller)683         void setInstance(MediaController2 controller);
getSessionToken()684         SessionToken2 getSessionToken();
isConnected()685         boolean isConnected();
play()686         void play();
pause()687         void pause();
reset()688         void reset();
prepare()689         void prepare();
fastForward()690         void fastForward();
rewind()691         void rewind();
seekTo(long pos)692         void seekTo(long pos);
skipForward()693         void skipForward();
skipBackward()694         void skipBackward();
playFromMediaId(@onNull String mediaId, @Nullable Bundle extras)695         void playFromMediaId(@NonNull String mediaId, @Nullable Bundle extras);
playFromSearch(@onNull String query, @Nullable Bundle extras)696         void playFromSearch(@NonNull String query, @Nullable Bundle extras);
playFromUri(@onNull Uri uri, @Nullable Bundle extras)697         void playFromUri(@NonNull Uri uri, @Nullable Bundle extras);
prepareFromMediaId(@onNull String mediaId, @Nullable Bundle extras)698         void prepareFromMediaId(@NonNull String mediaId, @Nullable Bundle extras);
prepareFromSearch(@onNull String query, @Nullable Bundle extras)699         void prepareFromSearch(@NonNull String query, @Nullable Bundle extras);
prepareFromUri(@onNull Uri uri, @Nullable Bundle extras)700         void prepareFromUri(@NonNull Uri uri, @Nullable Bundle extras);
setVolumeTo(int value, @VolumeFlags int flags)701         void setVolumeTo(int value, @VolumeFlags int flags);
adjustVolume(@olumeDirection int direction, @VolumeFlags int flags)702         void adjustVolume(@VolumeDirection int direction, @VolumeFlags int flags);
getSessionActivity()703         @Nullable PendingIntent getSessionActivity();
getPlayerState()704         int getPlayerState();
getDuration()705         long getDuration();
getCurrentPosition()706         long getCurrentPosition();
getPlaybackSpeed()707         float getPlaybackSpeed();
setPlaybackSpeed(float speed)708         void setPlaybackSpeed(float speed);
getBufferingState()709         @MediaPlayerInterface.BuffState int getBufferingState();
getBufferedPosition()710         long getBufferedPosition();
getPlaybackInfo()711         @Nullable PlaybackInfo getPlaybackInfo();
setRating(@onNull String mediaId, @NonNull Rating2 rating)712         void setRating(@NonNull String mediaId, @NonNull Rating2 rating);
sendCustomCommand(@onNull SessionCommand2 command, @Nullable Bundle args, @Nullable ResultReceiver cb)713         void sendCustomCommand(@NonNull SessionCommand2 command, @Nullable Bundle args,
714                 @Nullable ResultReceiver cb);
getPlaylist()715         @Nullable List<MediaItem2> getPlaylist();
setPlaylist(@onNull List<MediaItem2> list, @Nullable MediaMetadata2 metadata)716         void setPlaylist(@NonNull List<MediaItem2> list, @Nullable MediaMetadata2 metadata);
updatePlaylistMetadata(@ullable MediaMetadata2 metadata)717         void updatePlaylistMetadata(@Nullable MediaMetadata2 metadata);
getPlaylistMetadata()718         @Nullable MediaMetadata2 getPlaylistMetadata();
addPlaylistItem(int index, @NonNull MediaItem2 item)719         void addPlaylistItem(int index, @NonNull MediaItem2 item);
removePlaylistItem(@onNull MediaItem2 item)720         void removePlaylistItem(@NonNull MediaItem2 item);
replacePlaylistItem(int index, @NonNull MediaItem2 item)721         void replacePlaylistItem(int index, @NonNull MediaItem2 item);
getCurrentMediaItem()722         MediaItem2 getCurrentMediaItem();
skipToPreviousItem()723         void skipToPreviousItem();
skipToNextItem()724         void skipToNextItem();
skipToPlaylistItem(@onNull MediaItem2 item)725         void skipToPlaylistItem(@NonNull MediaItem2 item);
getRepeatMode()726         @RepeatMode int getRepeatMode();
setRepeatMode(@epeatMode int repeatMode)727         void setRepeatMode(@RepeatMode int repeatMode);
getShuffleMode()728         @ShuffleMode int getShuffleMode();
setShuffleMode(@huffleMode int shuffleMode)729         void setShuffleMode(@ShuffleMode int shuffleMode);
subscribeRoutesInfo()730         void subscribeRoutesInfo();
unsubscribeRoutesInfo()731         void unsubscribeRoutesInfo();
selectRoute(@onNull Bundle route)732         void selectRoute(@NonNull Bundle route);
733 
734         // For MediaBrowser2
getContext()735         @NonNull Context getContext();
getCallback()736         @NonNull ControllerCallback getCallback();
getCallbackExecutor()737         @NonNull Executor getCallbackExecutor();
getBrowserCompat()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          */
onConnected(@onNull MediaController2 controller, @NonNull SessionCommandGroup2 allowedCommands)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          */
onDisconnected(@onNull MediaController2 controller)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          */
onCustomLayoutChanged(@onNull MediaController2 controller, @NonNull List<CommandButton> layout)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          */
onPlaybackInfoChanged(@onNull MediaController2 controller, @NonNull PlaybackInfo info)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          */
onAllowedCommandsChanged(@onNull MediaController2 controller, @NonNull SessionCommandGroup2 commands)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          */
onCustomCommand(@onNull MediaController2 controller, @NonNull SessionCommand2 command, @Nullable Bundle args, @Nullable ResultReceiver receiver)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          */
onPlayerStateChanged(@onNull MediaController2 controller, int state)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          */
onPlaybackSpeedChanged(@onNull MediaController2 controller, float speed)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          */
onBufferingStateChanged(@onNull MediaController2 controller, @NonNull MediaItem2 item, @MediaPlayerInterface.BuffState int state)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          */
onSeekCompleted(@onNull MediaController2 controller, long position)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          */
onError(@onNull MediaController2 controller, @ErrorCode int errorCode, @Nullable Bundle extras)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          */
onCurrentMediaItemChanged(@onNull MediaController2 controller, @Nullable MediaItem2 item)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          */
onPlaylistChanged(@onNull MediaController2 controller, @NonNull List<MediaItem2> list, @Nullable MediaMetadata2 metadata)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          */
onPlaylistMetadataChanged(@onNull MediaController2 controller, @Nullable MediaMetadata2 metadata)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          */
onShuffleModeChanged(@onNull MediaController2 controller, @MediaPlaylistAgent.ShuffleMode int shuffleMode)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          */
onRepeatModeChanged(@onNull MediaController2 controller, @MediaPlaylistAgent.RepeatMode int repeatMode)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          */
onRoutesInfoChanged(@onNull MediaController2 controller, @Nullable List<Bundle> routes)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 
PlaybackInfo(int playbackType, AudioAttributesCompat attrs, int controlType, int max, int current)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          */
getPlaybackType()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          */
getAudioAttributes()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          */
getControlType()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          */
getMaxVolume()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          */
getCurrentVolume()1015         public int getCurrentVolume() {
1016             return mCurrentVolume;
1017         }
1018 
toBundle()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 
createPlaybackInfo(int playbackType, AudioAttributesCompat attrs, int controlType, int max, int current)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 
fromBundle(Bundle bundle)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