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
17package androidx.media;
18
19import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
20
21import android.annotation.TargetApi;
22import android.app.PendingIntent;
23import android.content.Context;
24import android.media.AudioManager;
25import android.net.Uri;
26import android.os.Build;
27import android.os.Bundle;
28import android.os.ResultReceiver;
29import android.support.v4.media.MediaBrowserCompat;
30
31import androidx.annotation.IntDef;
32import androidx.annotation.NonNull;
33import androidx.annotation.Nullable;
34import androidx.annotation.RestrictTo;
35import androidx.annotation.VisibleForTesting;
36import androidx.media.MediaPlaylistAgent.RepeatMode;
37import androidx.media.MediaPlaylistAgent.ShuffleMode;
38import androidx.media.MediaSession2.CommandButton;
39import androidx.media.MediaSession2.ControllerInfo;
40import androidx.media.MediaSession2.ErrorCode;
41
42import java.lang.annotation.Retention;
43import java.lang.annotation.RetentionPolicy;
44import java.util.List;
45import 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)
70public 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