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