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