1/* 2 * Copyright (C) 2014 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 android.support.v4.media.session; 18 19import android.app.PendingIntent; 20import android.content.Context; 21import android.media.AudioManager; 22import android.net.Uri; 23import android.os.Bundle; 24import android.os.Handler; 25import android.os.IBinder; 26import android.os.Looper; 27import android.os.Message; 28import android.os.RemoteException; 29import android.os.ResultReceiver; 30import android.support.v4.media.MediaMetadataCompat; 31import android.support.v4.media.RatingCompat; 32import android.support.v4.media.VolumeProviderCompat; 33import android.support.v4.media.session.MediaSessionCompat.QueueItem; 34import android.support.v4.media.session.PlaybackStateCompat.CustomAction; 35import android.text.TextUtils; 36import android.util.Log; 37import android.view.KeyEvent; 38 39import java.util.List; 40 41/** 42 * Allows an app to interact with an ongoing media session. Media buttons and 43 * other commands can be sent to the session. A callback may be registered to 44 * receive updates from the session, such as metadata and play state changes. 45 * <p> 46 * A MediaController can be created if you have a {@link MediaSessionCompat.Token} 47 * from the session owner. 48 * <p> 49 * MediaController objects are thread-safe. 50 * <p> 51 * This is a helper for accessing features in {@link android.media.session.MediaSession} 52 * introduced after API level 4 in a backwards compatible fashion. 53 */ 54public final class MediaControllerCompat { 55 static final String TAG = "MediaControllerCompat"; 56 57 private final MediaControllerImpl mImpl; 58 private final MediaSessionCompat.Token mToken; 59 60 /** 61 * Creates a media controller from a session. 62 * 63 * @param session The session to be controlled. 64 */ 65 public MediaControllerCompat(Context context, MediaSessionCompat session) { 66 if (session == null) { 67 throw new IllegalArgumentException("session must not be null"); 68 } 69 mToken = session.getSessionToken(); 70 71 if (android.os.Build.VERSION.SDK_INT >= 24) { 72 mImpl = new MediaControllerImplApi24(context, session); 73 } else if (android.os.Build.VERSION.SDK_INT >= 23) { 74 mImpl = new MediaControllerImplApi23(context, session); 75 } else if (android.os.Build.VERSION.SDK_INT >= 21) { 76 mImpl = new MediaControllerImplApi21(context, session); 77 } else { 78 mImpl = new MediaControllerImplBase(mToken); 79 } 80 } 81 82 /** 83 * Creates a media controller from a session token which may have 84 * been obtained from another process. 85 * 86 * @param sessionToken The token of the session to be controlled. 87 * @throws RemoteException if the session is not accessible. 88 */ 89 public MediaControllerCompat(Context context, MediaSessionCompat.Token sessionToken) 90 throws RemoteException { 91 if (sessionToken == null) { 92 throw new IllegalArgumentException("sessionToken must not be null"); 93 } 94 mToken = sessionToken; 95 96 if (android.os.Build.VERSION.SDK_INT >= 24) { 97 mImpl = new MediaControllerImplApi24(context, sessionToken); 98 } else if (android.os.Build.VERSION.SDK_INT >= 23) { 99 mImpl = new MediaControllerImplApi23(context, sessionToken); 100 } else if (android.os.Build.VERSION.SDK_INT >= 21) { 101 mImpl = new MediaControllerImplApi21(context, sessionToken); 102 } else { 103 mImpl = new MediaControllerImplBase(mToken); 104 } 105 } 106 107 /** 108 * Get a {@link TransportControls} instance for this session. 109 * 110 * @return A controls instance 111 */ 112 public TransportControls getTransportControls() { 113 return mImpl.getTransportControls(); 114 } 115 116 /** 117 * Send the specified media button event to the session. Only media keys can 118 * be sent by this method, other keys will be ignored. 119 * 120 * @param keyEvent The media button event to dispatch. 121 * @return true if the event was sent to the session, false otherwise. 122 */ 123 public boolean dispatchMediaButtonEvent(KeyEvent keyEvent) { 124 if (keyEvent == null) { 125 throw new IllegalArgumentException("KeyEvent may not be null"); 126 } 127 return mImpl.dispatchMediaButtonEvent(keyEvent); 128 } 129 130 /** 131 * Get the current playback state for this session. 132 * 133 * @return The current PlaybackState or null 134 */ 135 public PlaybackStateCompat getPlaybackState() { 136 return mImpl.getPlaybackState(); 137 } 138 139 /** 140 * Get the current metadata for this session. 141 * 142 * @return The current MediaMetadata or null. 143 */ 144 public MediaMetadataCompat getMetadata() { 145 return mImpl.getMetadata(); 146 } 147 148 /** 149 * Get the current play queue for this session if one is set. If you only 150 * care about the current item {@link #getMetadata()} should be used. 151 * 152 * @return The current play queue or null. 153 */ 154 public List<MediaSessionCompat.QueueItem> getQueue() { 155 return mImpl.getQueue(); 156 } 157 158 /** 159 * Get the queue title for this session. 160 */ 161 public CharSequence getQueueTitle() { 162 return mImpl.getQueueTitle(); 163 } 164 165 /** 166 * Get the extras for this session. 167 */ 168 public Bundle getExtras() { 169 return mImpl.getExtras(); 170 } 171 172 /** 173 * Get the rating type supported by the session. One of: 174 * <ul> 175 * <li>{@link RatingCompat#RATING_NONE}</li> 176 * <li>{@link RatingCompat#RATING_HEART}</li> 177 * <li>{@link RatingCompat#RATING_THUMB_UP_DOWN}</li> 178 * <li>{@link RatingCompat#RATING_3_STARS}</li> 179 * <li>{@link RatingCompat#RATING_4_STARS}</li> 180 * <li>{@link RatingCompat#RATING_5_STARS}</li> 181 * <li>{@link RatingCompat#RATING_PERCENTAGE}</li> 182 * </ul> 183 * 184 * @return The supported rating type 185 */ 186 public int getRatingType() { 187 return mImpl.getRatingType(); 188 } 189 190 /** 191 * Get the flags for this session. Flags are defined in 192 * {@link MediaSessionCompat}. 193 * 194 * @return The current set of flags for the session. 195 */ 196 public long getFlags() { 197 return mImpl.getFlags(); 198 } 199 200 /** 201 * Get the current playback info for this session. 202 * 203 * @return The current playback info or null. 204 */ 205 public PlaybackInfo getPlaybackInfo() { 206 return mImpl.getPlaybackInfo(); 207 } 208 209 /** 210 * Get an intent for launching UI associated with this session if one 211 * exists. 212 * 213 * @return A {@link PendingIntent} to launch UI or null. 214 */ 215 public PendingIntent getSessionActivity() { 216 return mImpl.getSessionActivity(); 217 } 218 219 /** 220 * Get the token for the session this controller is connected to. 221 * 222 * @return The session's token. 223 */ 224 public MediaSessionCompat.Token getSessionToken() { 225 return mToken; 226 } 227 228 /** 229 * Set the volume of the output this session is playing on. The command will 230 * be ignored if it does not support 231 * {@link VolumeProviderCompat#VOLUME_CONTROL_ABSOLUTE}. The flags in 232 * {@link AudioManager} may be used to affect the handling. 233 * 234 * @see #getPlaybackInfo() 235 * @param value The value to set it to, between 0 and the reported max. 236 * @param flags Flags from {@link AudioManager} to include with the volume 237 * request. 238 */ 239 public void setVolumeTo(int value, int flags) { 240 mImpl.setVolumeTo(value, flags); 241 } 242 243 /** 244 * Adjust the volume of the output this session is playing on. The direction 245 * must be one of {@link AudioManager#ADJUST_LOWER}, 246 * {@link AudioManager#ADJUST_RAISE}, or {@link AudioManager#ADJUST_SAME}. 247 * The command will be ignored if the session does not support 248 * {@link VolumeProviderCompat#VOLUME_CONTROL_RELATIVE} or 249 * {@link VolumeProviderCompat#VOLUME_CONTROL_ABSOLUTE}. The flags in 250 * {@link AudioManager} may be used to affect the handling. 251 * 252 * @see #getPlaybackInfo() 253 * @param direction The direction to adjust the volume in. 254 * @param flags Any flags to pass with the command. 255 */ 256 public void adjustVolume(int direction, int flags) { 257 mImpl.adjustVolume(direction, flags); 258 } 259 260 /** 261 * Adds a callback to receive updates from the Session. Updates will be 262 * posted on the caller's thread. 263 * 264 * @param callback The callback object, must not be null. 265 */ 266 public void registerCallback(Callback callback) { 267 registerCallback(callback, null); 268 } 269 270 /** 271 * Adds a callback to receive updates from the session. Updates will be 272 * posted on the specified handler's thread. 273 * 274 * @param callback The callback object, must not be null. 275 * @param handler The handler to post updates on. If null the callers thread 276 * will be used. 277 */ 278 public void registerCallback(Callback callback, Handler handler) { 279 if (callback == null) { 280 throw new IllegalArgumentException("callback cannot be null"); 281 } 282 if (handler == null) { 283 handler = new Handler(); 284 } 285 mImpl.registerCallback(callback, handler); 286 } 287 288 /** 289 * Stop receiving updates on the specified callback. If an update has 290 * already been posted you may still receive it after calling this method. 291 * 292 * @param callback The callback to remove 293 */ 294 public void unregisterCallback(Callback callback) { 295 if (callback == null) { 296 throw new IllegalArgumentException("callback cannot be null"); 297 } 298 mImpl.unregisterCallback(callback); 299 } 300 301 /** 302 * Sends a generic command to the session. It is up to the session creator 303 * to decide what commands and parameters they will support. As such, 304 * commands should only be sent to sessions that the controller owns. 305 * 306 * @param command The command to send 307 * @param params Any parameters to include with the command 308 * @param cb The callback to receive the result on 309 */ 310 public void sendCommand(String command, Bundle params, ResultReceiver cb) { 311 if (TextUtils.isEmpty(command)) { 312 throw new IllegalArgumentException("command cannot be null or empty"); 313 } 314 mImpl.sendCommand(command, params, cb); 315 } 316 317 /** 318 * Get the session owner's package name. 319 * 320 * @return The package name of of the session owner. 321 */ 322 public String getPackageName() { 323 return mImpl.getPackageName(); 324 } 325 326 /** 327 * Gets the underlying framework 328 * {@link android.media.session.MediaController} object. 329 * <p> 330 * This method is only supported on API 21+. 331 * </p> 332 * 333 * @return The underlying {@link android.media.session.MediaController} 334 * object, or null if none. 335 */ 336 public Object getMediaController() { 337 return mImpl.getMediaController(); 338 } 339 340 /** 341 * Callback for receiving updates on from the session. A Callback can be 342 * registered using {@link #registerCallback} 343 */ 344 public static abstract class Callback implements IBinder.DeathRecipient { 345 private final Object mCallbackObj; 346 MessageHandler mHandler; 347 348 boolean mRegistered = false; 349 350 public Callback() { 351 if (android.os.Build.VERSION.SDK_INT >= 21) { 352 mCallbackObj = MediaControllerCompatApi21.createCallback(new StubApi21()); 353 } else { 354 mCallbackObj = new StubCompat(); 355 } 356 } 357 358 /** 359 * Override to handle the session being destroyed. The session is no 360 * longer valid after this call and calls to it will be ignored. 361 */ 362 public void onSessionDestroyed() { 363 } 364 365 /** 366 * Override to handle custom events sent by the session owner without a 367 * specified interface. Controllers should only handle these for 368 * sessions they own. 369 * 370 * @param event The event from the session. 371 * @param extras Optional parameters for the event. 372 */ 373 public void onSessionEvent(String event, Bundle extras) { 374 } 375 376 /** 377 * Override to handle changes in playback state. 378 * 379 * @param state The new playback state of the session 380 */ 381 public void onPlaybackStateChanged(PlaybackStateCompat state) { 382 } 383 384 /** 385 * Override to handle changes to the current metadata. 386 * 387 * @param metadata The current metadata for the session or null if none. 388 * @see MediaMetadataCompat 389 */ 390 public void onMetadataChanged(MediaMetadataCompat metadata) { 391 } 392 393 /** 394 * Override to handle changes to items in the queue. 395 * 396 * @see MediaSessionCompat.QueueItem 397 * @param queue A list of items in the current play queue. It should 398 * include the currently playing item as well as previous and 399 * upcoming items if applicable. 400 */ 401 public void onQueueChanged(List<MediaSessionCompat.QueueItem> queue) { 402 } 403 404 /** 405 * Override to handle changes to the queue title. 406 * 407 * @param title The title that should be displayed along with the play 408 * queue such as "Now Playing". May be null if there is no 409 * such title. 410 */ 411 public void onQueueTitleChanged(CharSequence title) { 412 } 413 414 /** 415 * Override to handle chagnes to the {@link MediaSessionCompat} extras. 416 * 417 * @param extras The extras that can include other information 418 * associated with the {@link MediaSessionCompat}. 419 */ 420 public void onExtrasChanged(Bundle extras) { 421 } 422 423 /** 424 * Override to handle changes to the audio info. 425 * 426 * @param info The current audio info for this session. 427 */ 428 public void onAudioInfoChanged(PlaybackInfo info) { 429 } 430 431 @Override 432 public void binderDied() { 433 onSessionDestroyed(); 434 } 435 436 /** 437 * Set the handler to use for pre 21 callbacks. 438 */ 439 private void setHandler(Handler handler) { 440 mHandler = new MessageHandler(handler.getLooper()); 441 } 442 443 private class StubApi21 implements MediaControllerCompatApi21.Callback { 444 StubApi21() { 445 } 446 447 @Override 448 public void onSessionDestroyed() { 449 Callback.this.onSessionDestroyed(); 450 } 451 452 @Override 453 public void onSessionEvent(String event, Bundle extras) { 454 Callback.this.onSessionEvent(event, extras); 455 } 456 457 @Override 458 public void onPlaybackStateChanged(Object stateObj) { 459 Callback.this.onPlaybackStateChanged( 460 PlaybackStateCompat.fromPlaybackState(stateObj)); 461 } 462 463 @Override 464 public void onMetadataChanged(Object metadataObj) { 465 Callback.this.onMetadataChanged(MediaMetadataCompat.fromMediaMetadata(metadataObj)); 466 } 467 468 @Override 469 public void onQueueChanged(List<?> queue) { 470 Callback.this.onQueueChanged(QueueItem.fromQueueItemList(queue)); 471 } 472 473 @Override 474 public void onQueueTitleChanged(CharSequence title) { 475 Callback.this.onQueueTitleChanged(title); 476 } 477 478 @Override 479 public void onExtrasChanged(Bundle extras) { 480 Callback.this.onExtrasChanged(extras); 481 } 482 483 @Override 484 public void onAudioInfoChanged( 485 int type, int stream, int control, int max, int current) { 486 Callback.this.onAudioInfoChanged( 487 new PlaybackInfo(type, stream, control, max, current)); 488 } 489 } 490 491 private class StubCompat extends IMediaControllerCallback.Stub { 492 493 StubCompat() { 494 } 495 496 @Override 497 public void onEvent(String event, Bundle extras) throws RemoteException { 498 mHandler.post(MessageHandler.MSG_EVENT, event, extras); 499 } 500 501 @Override 502 public void onSessionDestroyed() throws RemoteException { 503 mHandler.post(MessageHandler.MSG_DESTROYED, null, null); 504 } 505 506 @Override 507 public void onPlaybackStateChanged(PlaybackStateCompat state) throws RemoteException { 508 mHandler.post(MessageHandler.MSG_UPDATE_PLAYBACK_STATE, state, null); 509 } 510 511 @Override 512 public void onMetadataChanged(MediaMetadataCompat metadata) throws RemoteException { 513 mHandler.post(MessageHandler.MSG_UPDATE_METADATA, metadata, null); 514 } 515 516 @Override 517 public void onQueueChanged(List<QueueItem> queue) throws RemoteException { 518 mHandler.post(MessageHandler.MSG_UPDATE_QUEUE, queue, null); 519 } 520 521 @Override 522 public void onQueueTitleChanged(CharSequence title) throws RemoteException { 523 mHandler.post(MessageHandler.MSG_UPDATE_QUEUE_TITLE, title, null); 524 } 525 526 @Override 527 public void onExtrasChanged(Bundle extras) throws RemoteException { 528 mHandler.post(MessageHandler.MSG_UPDATE_EXTRAS, extras, null); 529 } 530 531 @Override 532 public void onVolumeInfoChanged(ParcelableVolumeInfo info) throws RemoteException { 533 PlaybackInfo pi = null; 534 if (info != null) { 535 pi = new PlaybackInfo(info.volumeType, info.audioStream, info.controlType, 536 info.maxVolume, info.currentVolume); 537 } 538 mHandler.post(MessageHandler.MSG_UPDATE_VOLUME, pi, null); 539 } 540 } 541 542 private class MessageHandler extends Handler { 543 private static final int MSG_EVENT = 1; 544 private static final int MSG_UPDATE_PLAYBACK_STATE = 2; 545 private static final int MSG_UPDATE_METADATA = 3; 546 private static final int MSG_UPDATE_VOLUME = 4; 547 private static final int MSG_UPDATE_QUEUE = 5; 548 private static final int MSG_UPDATE_QUEUE_TITLE = 6; 549 private static final int MSG_UPDATE_EXTRAS = 7; 550 private static final int MSG_DESTROYED = 8; 551 552 public MessageHandler(Looper looper) { 553 super(looper); 554 } 555 556 @Override 557 public void handleMessage(Message msg) { 558 if (!mRegistered) { 559 return; 560 } 561 switch (msg.what) { 562 case MSG_EVENT: 563 onSessionEvent((String) msg.obj, msg.getData()); 564 break; 565 case MSG_UPDATE_PLAYBACK_STATE: 566 onPlaybackStateChanged((PlaybackStateCompat) msg.obj); 567 break; 568 case MSG_UPDATE_METADATA: 569 onMetadataChanged((MediaMetadataCompat) msg.obj); 570 break; 571 case MSG_UPDATE_QUEUE: 572 onQueueChanged((List<MediaSessionCompat.QueueItem>) msg.obj); 573 break; 574 case MSG_UPDATE_QUEUE_TITLE: 575 onQueueTitleChanged((CharSequence) msg.obj); 576 break; 577 case MSG_UPDATE_EXTRAS: 578 onExtrasChanged((Bundle) msg.obj); 579 break; 580 case MSG_UPDATE_VOLUME: 581 onAudioInfoChanged((PlaybackInfo) msg.obj); 582 break; 583 case MSG_DESTROYED: 584 onSessionDestroyed(); 585 break; 586 } 587 } 588 589 public void post(int what, Object obj, Bundle data) { 590 Message msg = obtainMessage(what, obj); 591 msg.setData(data); 592 msg.sendToTarget(); 593 } 594 } 595 } 596 597 /** 598 * Interface for controlling media playback on a session. This allows an app 599 * to send media transport commands to the session. 600 */ 601 public static abstract class TransportControls { 602 TransportControls() { 603 } 604 605 /** 606 * Request that the player prepare its playback without audio focus. In other words, other 607 * session can continue to play during the preparation of this session. This method can be 608 * used to speed up the start of the playback. Once the preparation is done, the session 609 * will change its playback state to {@link PlaybackStateCompat#STATE_PAUSED}. Afterwards, 610 * {@link #play} can be called to start playback. If the preparation is not needed, 611 * {@link #play} can be directly called without this method. 612 */ 613 public abstract void prepare(); 614 615 /** 616 * Request that the player prepare playback for a specific media id. In other words, other 617 * session can continue to play during the preparation of this session. This method can be 618 * used to speed up the start of the playback. Once the preparation is 619 * done, the session will change its playback state to 620 * {@link PlaybackStateCompat#STATE_PAUSED}. Afterwards, {@link #play} can be called to 621 * start playback. If the preparation is not needed, {@link #playFromMediaId} can 622 * be directly called without this method. 623 * 624 * @param mediaId The id of the requested media. 625 * @param extras Optional extras that can include extra information about the media item 626 * to be prepared. 627 */ 628 public abstract void prepareFromMediaId(String mediaId, Bundle extras); 629 630 /** 631 * Request that the player prepare playback for a specific search query. 632 * An empty or null query should be treated as a request to prepare any 633 * music. In other words, other session can continue to play during 634 * the preparation of this session. This method can be used to speed up the start of the 635 * playback. Once the preparation is done, the session will change its playback state to 636 * {@link PlaybackStateCompat#STATE_PAUSED}. Afterwards, {@link #play} can be called to 637 * start playback. If the preparation is not needed, {@link #playFromSearch} can be directly 638 * called without this method. 639 * 640 * @param query The search query. 641 * @param extras Optional extras that can include extra information 642 * about the query. 643 */ 644 public abstract void prepareFromSearch(String query, Bundle extras); 645 646 /** 647 * Request that the player prepare playback for a specific {@link Uri}. 648 * In other words, other session can continue to play during the preparation of this 649 * session. This method can be used to speed up the start of the playback. 650 * Once the preparation is done, the session will change its playback state to 651 * {@link PlaybackStateCompat#STATE_PAUSED}. Afterwards, {@link #play} can be called to 652 * start playback. If the preparation is not needed, {@link #playFromUri} can be directly 653 * called without this method. 654 * 655 * @param uri The URI of the requested media. 656 * @param extras Optional extras that can include extra information about the media item 657 * to be prepared. 658 */ 659 public abstract void prepareFromUri(Uri uri, Bundle extras); 660 661 /** 662 * Request that the player start its playback at its current position. 663 */ 664 public abstract void play(); 665 666 /** 667 * Request that the player start playback for a specific {@link Uri}. 668 * 669 * @param mediaId The uri of the requested media. 670 * @param extras Optional extras that can include extra information 671 * about the media item to be played. 672 */ 673 public abstract void playFromMediaId(String mediaId, Bundle extras); 674 675 /** 676 * Request that the player start playback for a specific search query. 677 * An empty or null query should be treated as a request to play any 678 * music. 679 * 680 * @param query The search query. 681 * @param extras Optional extras that can include extra information 682 * about the query. 683 */ 684 public abstract void playFromSearch(String query, Bundle extras); 685 686 /** 687 * Request that the player start playback for a specific {@link Uri}. 688 * 689 * @param uri The URI of the requested media. 690 * @param extras Optional extras that can include extra information about the media item 691 * to be played. 692 */ 693 public abstract void playFromUri(Uri uri, Bundle extras); 694 695 /** 696 * Play an item with a specific id in the play queue. If you specify an 697 * id that is not in the play queue, the behavior is undefined. 698 */ 699 public abstract void skipToQueueItem(long id); 700 701 /** 702 * Request that the player pause its playback and stay at its current 703 * position. 704 */ 705 public abstract void pause(); 706 707 /** 708 * Request that the player stop its playback; it may clear its state in 709 * whatever way is appropriate. 710 */ 711 public abstract void stop(); 712 713 /** 714 * Move to a new location in the media stream. 715 * 716 * @param pos Position to move to, in milliseconds. 717 */ 718 public abstract void seekTo(long pos); 719 720 /** 721 * Start fast forwarding. If playback is already fast forwarding this 722 * may increase the rate. 723 */ 724 public abstract void fastForward(); 725 726 /** 727 * Skip to the next item. 728 */ 729 public abstract void skipToNext(); 730 731 /** 732 * Start rewinding. If playback is already rewinding this may increase 733 * the rate. 734 */ 735 public abstract void rewind(); 736 737 /** 738 * Skip to the previous item. 739 */ 740 public abstract void skipToPrevious(); 741 742 /** 743 * Rate the current content. This will cause the rating to be set for 744 * the current user. The Rating type must match the type returned by 745 * {@link #getRatingType()}. 746 * 747 * @param rating The rating to set for the current content 748 */ 749 public abstract void setRating(RatingCompat rating); 750 751 /** 752 * Send a custom action for the {@link MediaSessionCompat} to perform. 753 * 754 * @param customAction The action to perform. 755 * @param args Optional arguments to supply to the 756 * {@link MediaSessionCompat} for this custom action. 757 */ 758 public abstract void sendCustomAction(PlaybackStateCompat.CustomAction customAction, 759 Bundle args); 760 761 /** 762 * Send the id and args from a custom action for the 763 * {@link MediaSessionCompat} to perform. 764 * 765 * @see #sendCustomAction(PlaybackStateCompat.CustomAction action, 766 * Bundle args) 767 * @param action The action identifier of the 768 * {@link PlaybackStateCompat.CustomAction} as specified by 769 * the {@link MediaSessionCompat}. 770 * @param args Optional arguments to supply to the 771 * {@link MediaSessionCompat} for this custom action. 772 */ 773 public abstract void sendCustomAction(String action, Bundle args); 774 } 775 776 /** 777 * Holds information about the way volume is handled for this session. 778 */ 779 public static final class PlaybackInfo { 780 /** 781 * The session uses local playback. 782 */ 783 public static final int PLAYBACK_TYPE_LOCAL = 1; 784 /** 785 * The session uses remote playback. 786 */ 787 public static final int PLAYBACK_TYPE_REMOTE = 2; 788 789 private final int mPlaybackType; 790 // TODO update audio stream with AudioAttributes support version 791 private final int mAudioStream; 792 private final int mVolumeControl; 793 private final int mMaxVolume; 794 private final int mCurrentVolume; 795 796 PlaybackInfo(int type, int stream, int control, int max, int current) { 797 mPlaybackType = type; 798 mAudioStream = stream; 799 mVolumeControl = control; 800 mMaxVolume = max; 801 mCurrentVolume = current; 802 } 803 804 /** 805 * Get the type of volume handling, either local or remote. One of: 806 * <ul> 807 * <li>{@link PlaybackInfo#PLAYBACK_TYPE_LOCAL}</li> 808 * <li>{@link PlaybackInfo#PLAYBACK_TYPE_REMOTE}</li> 809 * </ul> 810 * 811 * @return The type of volume handling this session is using. 812 */ 813 public int getPlaybackType() { 814 return mPlaybackType; 815 } 816 817 /** 818 * Get the stream this is currently controlling volume on. When the volume 819 * type is {@link PlaybackInfo#PLAYBACK_TYPE_REMOTE} this value does not 820 * have meaning and should be ignored. 821 * 822 * @return The stream this session is playing on. 823 */ 824 public int getAudioStream() { 825 // TODO switch to AudioAttributesCompat when it is added. 826 return mAudioStream; 827 } 828 829 /** 830 * Get the type of volume control that can be used. One of: 831 * <ul> 832 * <li>{@link VolumeProviderCompat#VOLUME_CONTROL_ABSOLUTE}</li> 833 * <li>{@link VolumeProviderCompat#VOLUME_CONTROL_RELATIVE}</li> 834 * <li>{@link VolumeProviderCompat#VOLUME_CONTROL_FIXED}</li> 835 * </ul> 836 * 837 * @return The type of volume control that may be used with this 838 * session. 839 */ 840 public int getVolumeControl() { 841 return mVolumeControl; 842 } 843 844 /** 845 * Get the maximum volume that may be set for this session. 846 * 847 * @return The maximum allowed volume where this session is playing. 848 */ 849 public int getMaxVolume() { 850 return mMaxVolume; 851 } 852 853 /** 854 * Get the current volume for this session. 855 * 856 * @return The current volume where this session is playing. 857 */ 858 public int getCurrentVolume() { 859 return mCurrentVolume; 860 } 861 } 862 863 interface MediaControllerImpl { 864 void registerCallback(Callback callback, Handler handler); 865 866 void unregisterCallback(Callback callback); 867 boolean dispatchMediaButtonEvent(KeyEvent keyEvent); 868 TransportControls getTransportControls(); 869 PlaybackStateCompat getPlaybackState(); 870 MediaMetadataCompat getMetadata(); 871 872 List<MediaSessionCompat.QueueItem> getQueue(); 873 CharSequence getQueueTitle(); 874 Bundle getExtras(); 875 int getRatingType(); 876 long getFlags(); 877 PlaybackInfo getPlaybackInfo(); 878 PendingIntent getSessionActivity(); 879 880 void setVolumeTo(int value, int flags); 881 void adjustVolume(int direction, int flags); 882 void sendCommand(String command, Bundle params, ResultReceiver cb); 883 884 String getPackageName(); 885 Object getMediaController(); 886 } 887 888 static class MediaControllerImplBase implements MediaControllerImpl { 889 private MediaSessionCompat.Token mToken; 890 private IMediaSession mBinder; 891 private TransportControls mTransportControls; 892 893 public MediaControllerImplBase(MediaSessionCompat.Token token) { 894 mToken = token; 895 mBinder = IMediaSession.Stub.asInterface((IBinder) token.getToken()); 896 } 897 898 @Override 899 public void registerCallback(Callback callback, Handler handler) { 900 if (callback == null) { 901 throw new IllegalArgumentException("callback may not be null."); 902 } 903 try { 904 mBinder.asBinder().linkToDeath(callback, 0); 905 mBinder.registerCallbackListener((IMediaControllerCallback) callback.mCallbackObj); 906 callback.setHandler(handler); 907 callback.mRegistered = true; 908 } catch (RemoteException e) { 909 Log.e(TAG, "Dead object in registerCallback. " + e); 910 callback.onSessionDestroyed(); 911 } 912 } 913 914 @Override 915 public void unregisterCallback(Callback callback) { 916 if (callback == null) { 917 throw new IllegalArgumentException("callback may not be null."); 918 } 919 try { 920 mBinder.unregisterCallbackListener( 921 (IMediaControllerCallback) callback.mCallbackObj); 922 mBinder.asBinder().unlinkToDeath(callback, 0); 923 callback.mRegistered = false; 924 } catch (RemoteException e) { 925 Log.e(TAG, "Dead object in unregisterCallback. " + e); 926 } 927 } 928 929 @Override 930 public boolean dispatchMediaButtonEvent(KeyEvent event) { 931 if (event == null) { 932 throw new IllegalArgumentException("event may not be null."); 933 } 934 try { 935 mBinder.sendMediaButton(event); 936 } catch (RemoteException e) { 937 Log.e(TAG, "Dead object in dispatchMediaButtonEvent. " + e); 938 } 939 return false; 940 } 941 942 @Override 943 public TransportControls getTransportControls() { 944 if (mTransportControls == null) { 945 mTransportControls = new TransportControlsBase(mBinder); 946 } 947 948 return mTransportControls; 949 } 950 951 @Override 952 public PlaybackStateCompat getPlaybackState() { 953 try { 954 return mBinder.getPlaybackState(); 955 } catch (RemoteException e) { 956 Log.e(TAG, "Dead object in getPlaybackState. " + e); 957 } 958 return null; 959 } 960 961 @Override 962 public MediaMetadataCompat getMetadata() { 963 try { 964 return mBinder.getMetadata(); 965 } catch (RemoteException e) { 966 Log.e(TAG, "Dead object in getMetadata. " + e); 967 } 968 return null; 969 } 970 971 @Override 972 public List<MediaSessionCompat.QueueItem> getQueue() { 973 try { 974 return mBinder.getQueue(); 975 } catch (RemoteException e) { 976 Log.e(TAG, "Dead object in getQueue. " + e); 977 } 978 return null; 979 } 980 981 @Override 982 public CharSequence getQueueTitle() { 983 try { 984 return mBinder.getQueueTitle(); 985 } catch (RemoteException e) { 986 Log.e(TAG, "Dead object in getQueueTitle. " + e); 987 } 988 return null; 989 } 990 991 @Override 992 public Bundle getExtras() { 993 try { 994 return mBinder.getExtras(); 995 } catch (RemoteException e) { 996 Log.e(TAG, "Dead object in getExtras. " + e); 997 } 998 return null; 999 } 1000 1001 @Override 1002 public int getRatingType() { 1003 try { 1004 return mBinder.getRatingType(); 1005 } catch (RemoteException e) { 1006 Log.e(TAG, "Dead object in getRatingType. " + e); 1007 } 1008 return 0; 1009 } 1010 1011 @Override 1012 public long getFlags() { 1013 try { 1014 return mBinder.getFlags(); 1015 } catch (RemoteException e) { 1016 Log.e(TAG, "Dead object in getFlags. " + e); 1017 } 1018 return 0; 1019 } 1020 1021 @Override 1022 public PlaybackInfo getPlaybackInfo() { 1023 try { 1024 ParcelableVolumeInfo info = mBinder.getVolumeAttributes(); 1025 PlaybackInfo pi = new PlaybackInfo(info.volumeType, info.audioStream, 1026 info.controlType, info.maxVolume, info.currentVolume); 1027 return pi; 1028 } catch (RemoteException e) { 1029 Log.e(TAG, "Dead object in getPlaybackInfo. " + e); 1030 } 1031 return null; 1032 } 1033 1034 @Override 1035 public PendingIntent getSessionActivity() { 1036 try { 1037 return mBinder.getLaunchPendingIntent(); 1038 } catch (RemoteException e) { 1039 Log.e(TAG, "Dead object in getSessionActivity. " + e); 1040 } 1041 return null; 1042 } 1043 1044 @Override 1045 public void setVolumeTo(int value, int flags) { 1046 try { 1047 mBinder.setVolumeTo(value, flags, null); 1048 } catch (RemoteException e) { 1049 Log.e(TAG, "Dead object in setVolumeTo. " + e); 1050 } 1051 } 1052 1053 @Override 1054 public void adjustVolume(int direction, int flags) { 1055 try { 1056 mBinder.adjustVolume(direction, flags, null); 1057 } catch (RemoteException e) { 1058 Log.e(TAG, "Dead object in adjustVolume. " + e); 1059 } 1060 } 1061 1062 @Override 1063 public void sendCommand(String command, Bundle params, ResultReceiver cb) { 1064 try { 1065 mBinder.sendCommand(command, params, 1066 new MediaSessionCompat.ResultReceiverWrapper(cb)); 1067 } catch (RemoteException e) { 1068 Log.e(TAG, "Dead object in sendCommand. " + e); 1069 } 1070 } 1071 1072 @Override 1073 public String getPackageName() { 1074 try { 1075 return mBinder.getPackageName(); 1076 } catch (RemoteException e) { 1077 Log.e(TAG, "Dead object in getPackageName. " + e); 1078 } 1079 return null; 1080 } 1081 1082 @Override 1083 public Object getMediaController() { 1084 return null; 1085 } 1086 } 1087 1088 static class TransportControlsBase extends TransportControls { 1089 private IMediaSession mBinder; 1090 1091 public TransportControlsBase(IMediaSession binder) { 1092 mBinder = binder; 1093 } 1094 1095 @Override 1096 public void prepare() { 1097 try { 1098 mBinder.prepare(); 1099 } catch (RemoteException e) { 1100 Log.e(TAG, "Dead object in prepare. " + e); 1101 } 1102 } 1103 1104 @Override 1105 public void prepareFromMediaId(String mediaId, Bundle extras) { 1106 try { 1107 mBinder.prepareFromMediaId(mediaId, extras); 1108 } catch (RemoteException e) { 1109 Log.e(TAG, "Dead object in prepareFromMediaId. " + e); 1110 } 1111 } 1112 1113 @Override 1114 public void prepareFromSearch(String query, Bundle extras) { 1115 try { 1116 mBinder.prepareFromSearch(query, extras); 1117 } catch (RemoteException e) { 1118 Log.e(TAG, "Dead object in prepareFromSearch. " + e); 1119 } 1120 } 1121 1122 @Override 1123 public void prepareFromUri(Uri uri, Bundle extras) { 1124 try { 1125 mBinder.prepareFromUri(uri, extras); 1126 } catch (RemoteException e) { 1127 Log.e(TAG, "Dead object in prepareFromUri. " + e); 1128 } 1129 } 1130 1131 @Override 1132 public void play() { 1133 try { 1134 mBinder.play(); 1135 } catch (RemoteException e) { 1136 Log.e(TAG, "Dead object in play. " + e); 1137 } 1138 } 1139 1140 @Override 1141 public void playFromMediaId(String mediaId, Bundle extras) { 1142 try { 1143 mBinder.playFromMediaId(mediaId, extras); 1144 } catch (RemoteException e) { 1145 Log.e(TAG, "Dead object in playFromMediaId. " + e); 1146 } 1147 } 1148 1149 @Override 1150 public void playFromSearch(String query, Bundle extras) { 1151 try { 1152 mBinder.playFromSearch(query, extras); 1153 } catch (RemoteException e) { 1154 Log.e(TAG, "Dead object in playFromSearch. " + e); 1155 } 1156 } 1157 1158 @Override 1159 public void playFromUri(Uri uri, Bundle extras) { 1160 try { 1161 mBinder.playFromUri(uri, extras); 1162 } catch (RemoteException e) { 1163 Log.e(TAG, "Dead object in playFromUri. " + e); 1164 } 1165 } 1166 1167 @Override 1168 public void skipToQueueItem(long id) { 1169 try { 1170 mBinder.skipToQueueItem(id); 1171 } catch (RemoteException e) { 1172 Log.e(TAG, "Dead object in skipToQueueItem. " + e); 1173 } 1174 } 1175 1176 @Override 1177 public void pause() { 1178 try { 1179 mBinder.pause(); 1180 } catch (RemoteException e) { 1181 Log.e(TAG, "Dead object in pause. " + e); 1182 } 1183 } 1184 1185 @Override 1186 public void stop() { 1187 try { 1188 mBinder.stop(); 1189 } catch (RemoteException e) { 1190 Log.e(TAG, "Dead object in stop. " + e); 1191 } 1192 } 1193 1194 @Override 1195 public void seekTo(long pos) { 1196 try { 1197 mBinder.seekTo(pos); 1198 } catch (RemoteException e) { 1199 Log.e(TAG, "Dead object in seekTo. " + e); 1200 } 1201 } 1202 1203 @Override 1204 public void fastForward() { 1205 try { 1206 mBinder.fastForward(); 1207 } catch (RemoteException e) { 1208 Log.e(TAG, "Dead object in fastForward. " + e); 1209 } 1210 } 1211 1212 @Override 1213 public void skipToNext() { 1214 try { 1215 mBinder.next(); 1216 } catch (RemoteException e) { 1217 Log.e(TAG, "Dead object in skipToNext. " + e); 1218 } 1219 } 1220 1221 @Override 1222 public void rewind() { 1223 try { 1224 mBinder.rewind(); 1225 } catch (RemoteException e) { 1226 Log.e(TAG, "Dead object in rewind. " + e); 1227 } 1228 } 1229 1230 @Override 1231 public void skipToPrevious() { 1232 try { 1233 mBinder.previous(); 1234 } catch (RemoteException e) { 1235 Log.e(TAG, "Dead object in skipToPrevious. " + e); 1236 } 1237 } 1238 1239 @Override 1240 public void setRating(RatingCompat rating) { 1241 try { 1242 mBinder.rate(rating); 1243 } catch (RemoteException e) { 1244 Log.e(TAG, "Dead object in setRating. " + e); 1245 } 1246 } 1247 1248 @Override 1249 public void sendCustomAction(CustomAction customAction, Bundle args) { 1250 sendCustomAction(customAction.getAction(), args); 1251 } 1252 1253 @Override 1254 public void sendCustomAction(String action, Bundle args) { 1255 try { 1256 mBinder.sendCustomAction(action, args); 1257 } catch (RemoteException e) { 1258 Log.e(TAG, "Dead object in sendCustomAction. " + e); 1259 } 1260 } 1261 } 1262 1263 static class MediaControllerImplApi21 implements MediaControllerImpl { 1264 protected final Object mControllerObj; 1265 1266 public MediaControllerImplApi21(Context context, MediaSessionCompat session) { 1267 mControllerObj = MediaControllerCompatApi21.fromToken(context, 1268 session.getSessionToken().getToken()); 1269 } 1270 1271 public MediaControllerImplApi21(Context context, MediaSessionCompat.Token sessionToken) 1272 throws RemoteException { 1273 mControllerObj = MediaControllerCompatApi21.fromToken(context, 1274 sessionToken.getToken()); 1275 if (mControllerObj == null) throw new RemoteException(); 1276 } 1277 1278 @Override 1279 public void registerCallback(Callback callback, Handler handler) { 1280 MediaControllerCompatApi21.registerCallback(mControllerObj, callback.mCallbackObj, handler); 1281 } 1282 1283 @Override 1284 public void unregisterCallback(Callback callback) { 1285 MediaControllerCompatApi21.unregisterCallback(mControllerObj, callback.mCallbackObj); 1286 } 1287 1288 @Override 1289 public boolean dispatchMediaButtonEvent(KeyEvent event) { 1290 return MediaControllerCompatApi21.dispatchMediaButtonEvent(mControllerObj, event); 1291 } 1292 1293 @Override 1294 public TransportControls getTransportControls() { 1295 Object controlsObj = MediaControllerCompatApi21.getTransportControls(mControllerObj); 1296 return controlsObj != null ? new TransportControlsApi21(controlsObj) : null; 1297 } 1298 1299 @Override 1300 public PlaybackStateCompat getPlaybackState() { 1301 Object stateObj = MediaControllerCompatApi21.getPlaybackState(mControllerObj); 1302 return stateObj != null ? PlaybackStateCompat.fromPlaybackState(stateObj) : null; 1303 } 1304 1305 @Override 1306 public MediaMetadataCompat getMetadata() { 1307 Object metadataObj = MediaControllerCompatApi21.getMetadata(mControllerObj); 1308 return metadataObj != null ? MediaMetadataCompat.fromMediaMetadata(metadataObj) : null; 1309 } 1310 1311 @Override 1312 public List<MediaSessionCompat.QueueItem> getQueue() { 1313 List<Object> queueObjs = MediaControllerCompatApi21.getQueue(mControllerObj); 1314 return queueObjs != null ? MediaSessionCompat.QueueItem.fromQueueItemList(queueObjs) 1315 : null; 1316 } 1317 1318 @Override 1319 public CharSequence getQueueTitle() { 1320 return MediaControllerCompatApi21.getQueueTitle(mControllerObj); 1321 } 1322 1323 @Override 1324 public Bundle getExtras() { 1325 return MediaControllerCompatApi21.getExtras(mControllerObj); 1326 } 1327 1328 @Override 1329 public int getRatingType() { 1330 return MediaControllerCompatApi21.getRatingType(mControllerObj); 1331 } 1332 1333 @Override 1334 public long getFlags() { 1335 return MediaControllerCompatApi21.getFlags(mControllerObj); 1336 } 1337 1338 @Override 1339 public PlaybackInfo getPlaybackInfo() { 1340 Object volumeInfoObj = MediaControllerCompatApi21.getPlaybackInfo(mControllerObj); 1341 return volumeInfoObj != null ? new PlaybackInfo( 1342 MediaControllerCompatApi21.PlaybackInfo.getPlaybackType(volumeInfoObj), 1343 MediaControllerCompatApi21.PlaybackInfo.getLegacyAudioStream(volumeInfoObj), 1344 MediaControllerCompatApi21.PlaybackInfo.getVolumeControl(volumeInfoObj), 1345 MediaControllerCompatApi21.PlaybackInfo.getMaxVolume(volumeInfoObj), 1346 MediaControllerCompatApi21.PlaybackInfo.getCurrentVolume(volumeInfoObj)) : null; 1347 } 1348 1349 @Override 1350 public PendingIntent getSessionActivity() { 1351 return MediaControllerCompatApi21.getSessionActivity(mControllerObj); 1352 } 1353 1354 @Override 1355 public void setVolumeTo(int value, int flags) { 1356 MediaControllerCompatApi21.setVolumeTo(mControllerObj, value, flags); 1357 } 1358 1359 @Override 1360 public void adjustVolume(int direction, int flags) { 1361 MediaControllerCompatApi21.adjustVolume(mControllerObj, direction, flags); 1362 } 1363 1364 @Override 1365 public void sendCommand(String command, Bundle params, ResultReceiver cb) { 1366 MediaControllerCompatApi21.sendCommand(mControllerObj, command, params, cb); 1367 } 1368 1369 @Override 1370 public String getPackageName() { 1371 return MediaControllerCompatApi21.getPackageName(mControllerObj); 1372 } 1373 1374 @Override 1375 public Object getMediaController() { 1376 return mControllerObj; 1377 } 1378 } 1379 1380 static class TransportControlsApi21 extends TransportControls { 1381 protected final Object mControlsObj; 1382 1383 public TransportControlsApi21(Object controlsObj) { 1384 mControlsObj = controlsObj; 1385 } 1386 1387 @Override 1388 public void prepare() { 1389 sendCustomAction(MediaSessionCompat.ACTION_PREPARE, null); 1390 } 1391 1392 @Override 1393 public void prepareFromMediaId(String mediaId, Bundle extras) { 1394 Bundle bundle = new Bundle(); 1395 bundle.putString(MediaSessionCompat.ACTION_ARGUMENT_MEDIA_ID, mediaId); 1396 bundle.putBundle(MediaSessionCompat.ACTION_ARGUMENT_EXTRAS, extras); 1397 sendCustomAction(MediaSessionCompat.ACTION_PREPARE_FROM_MEDIA_ID, bundle); 1398 } 1399 1400 @Override 1401 public void prepareFromSearch(String query, Bundle extras) { 1402 Bundle bundle = new Bundle(); 1403 bundle.putString(MediaSessionCompat.ACTION_ARGUMENT_QUERY, query); 1404 bundle.putBundle(MediaSessionCompat.ACTION_ARGUMENT_EXTRAS, extras); 1405 sendCustomAction(MediaSessionCompat.ACTION_PREPARE_FROM_SEARCH, bundle); 1406 } 1407 1408 @Override 1409 public void prepareFromUri(Uri uri, Bundle extras) { 1410 Bundle bundle = new Bundle(); 1411 bundle.putParcelable(MediaSessionCompat.ACTION_ARGUMENT_URI, uri); 1412 bundle.putBundle(MediaSessionCompat.ACTION_ARGUMENT_EXTRAS, extras); 1413 sendCustomAction(MediaSessionCompat.ACTION_PREPARE_FROM_URI, bundle); 1414 } 1415 1416 @Override 1417 public void play() { 1418 MediaControllerCompatApi21.TransportControls.play(mControlsObj); 1419 } 1420 1421 @Override 1422 public void pause() { 1423 MediaControllerCompatApi21.TransportControls.pause(mControlsObj); 1424 } 1425 1426 @Override 1427 public void stop() { 1428 MediaControllerCompatApi21.TransportControls.stop(mControlsObj); 1429 } 1430 1431 @Override 1432 public void seekTo(long pos) { 1433 MediaControllerCompatApi21.TransportControls.seekTo(mControlsObj, pos); 1434 } 1435 1436 @Override 1437 public void fastForward() { 1438 MediaControllerCompatApi21.TransportControls.fastForward(mControlsObj); 1439 } 1440 1441 @Override 1442 public void rewind() { 1443 MediaControllerCompatApi21.TransportControls.rewind(mControlsObj); 1444 } 1445 1446 @Override 1447 public void skipToNext() { 1448 MediaControllerCompatApi21.TransportControls.skipToNext(mControlsObj); 1449 } 1450 1451 @Override 1452 public void skipToPrevious() { 1453 MediaControllerCompatApi21.TransportControls.skipToPrevious(mControlsObj); 1454 } 1455 1456 @Override 1457 public void setRating(RatingCompat rating) { 1458 MediaControllerCompatApi21.TransportControls.setRating(mControlsObj, 1459 rating != null ? rating.getRating() : null); 1460 } 1461 1462 @Override 1463 public void playFromMediaId(String mediaId, Bundle extras) { 1464 MediaControllerCompatApi21.TransportControls.playFromMediaId(mControlsObj, mediaId, 1465 extras); 1466 } 1467 1468 @Override 1469 public void playFromSearch(String query, Bundle extras) { 1470 MediaControllerCompatApi21.TransportControls.playFromSearch(mControlsObj, query, 1471 extras); 1472 } 1473 1474 @Override 1475 public void playFromUri(Uri uri, Bundle extras) { 1476 if (uri == null || Uri.EMPTY.equals(uri)) { 1477 throw new IllegalArgumentException( 1478 "You must specify a non-empty Uri for playFromUri."); 1479 } 1480 Bundle bundle = new Bundle(); 1481 bundle.putParcelable(MediaSessionCompat.ACTION_ARGUMENT_URI, uri); 1482 bundle.putParcelable(MediaSessionCompat.ACTION_ARGUMENT_EXTRAS, extras); 1483 sendCustomAction(MediaSessionCompat.ACTION_PLAY_FROM_URI, bundle); 1484 } 1485 1486 @Override 1487 public void skipToQueueItem(long id) { 1488 MediaControllerCompatApi21.TransportControls.skipToQueueItem(mControlsObj, id); 1489 } 1490 1491 @Override 1492 public void sendCustomAction(CustomAction customAction, Bundle args) { 1493 MediaControllerCompatApi21.TransportControls.sendCustomAction(mControlsObj, 1494 customAction.getAction(), args); 1495 } 1496 1497 @Override 1498 public void sendCustomAction(String action, Bundle args) { 1499 MediaControllerCompatApi21.TransportControls.sendCustomAction(mControlsObj, action, 1500 args); 1501 } 1502 } 1503 1504 static class MediaControllerImplApi23 extends MediaControllerImplApi21 { 1505 1506 public MediaControllerImplApi23(Context context, MediaSessionCompat session) { 1507 super(context, session); 1508 } 1509 1510 public MediaControllerImplApi23(Context context, MediaSessionCompat.Token sessionToken) 1511 throws RemoteException { 1512 super(context, sessionToken); 1513 } 1514 1515 @Override 1516 public TransportControls getTransportControls() { 1517 Object controlsObj = MediaControllerCompatApi21.getTransportControls(mControllerObj); 1518 return controlsObj != null ? new TransportControlsApi23(controlsObj) : null; 1519 } 1520 } 1521 1522 static class TransportControlsApi23 extends TransportControlsApi21 { 1523 1524 public TransportControlsApi23(Object controlsObj) { 1525 super(controlsObj); 1526 } 1527 1528 @Override 1529 public void playFromUri(Uri uri, Bundle extras) { 1530 MediaControllerCompatApi23.TransportControls.playFromUri(mControlsObj, uri, 1531 extras); 1532 } 1533 } 1534 1535 static class MediaControllerImplApi24 extends MediaControllerImplApi23 { 1536 1537 public MediaControllerImplApi24(Context context, MediaSessionCompat session) { 1538 super(context, session); 1539 } 1540 1541 public MediaControllerImplApi24(Context context, MediaSessionCompat.Token sessionToken) 1542 throws RemoteException { 1543 super(context, sessionToken); 1544 } 1545 1546 @Override 1547 public TransportControls getTransportControls() { 1548 Object controlsObj = MediaControllerCompatApi21.getTransportControls(mControllerObj); 1549 return controlsObj != null ? new TransportControlsApi24(controlsObj) : null; 1550 } 1551 } 1552 1553 static class TransportControlsApi24 extends TransportControlsApi23 { 1554 1555 public TransportControlsApi24(Object controlsObj) { 1556 super(controlsObj); 1557 } 1558 1559 @Override 1560 public void prepare() { 1561 MediaControllerCompatApi24.TransportControls.prepare(mControlsObj); 1562 } 1563 1564 @Override 1565 public void prepareFromMediaId(String mediaId, Bundle extras) { 1566 MediaControllerCompatApi24.TransportControls.prepareFromMediaId( 1567 mControlsObj, mediaId, extras); 1568 } 1569 1570 @Override 1571 public void prepareFromSearch(String query, Bundle extras) { 1572 MediaControllerCompatApi24.TransportControls.prepareFromSearch( 1573 mControlsObj, query, extras); 1574 } 1575 1576 @Override 1577 public void prepareFromUri(Uri uri, Bundle extras) { 1578 MediaControllerCompatApi24.TransportControls.prepareFromUri(mControlsObj, uri, extras); 1579 } 1580 } 1581 1582} 1583