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