MediaControllerCompat.java revision 81dc63dae3e3f0a53f77efa3c06f5029a2f25db9
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.Activity; 20import android.app.PendingIntent; 21import android.content.Context; 22import android.media.AudioManager; 23import android.net.Uri; 24import android.os.Bundle; 25import android.os.Handler; 26import android.os.IBinder; 27import android.os.Looper; 28import android.os.Message; 29import android.os.RemoteException; 30import android.os.ResultReceiver; 31import android.support.v4.app.BundleCompat; 32import android.support.v4.app.SupportActivity; 33import android.support.v4.media.MediaDescriptionCompat; 34import android.support.v4.media.MediaMetadataCompat; 35import android.support.v4.media.RatingCompat; 36import android.support.v4.media.VolumeProviderCompat; 37import android.support.v4.media.session.MediaSessionCompat.QueueItem; 38import android.support.v4.media.session.PlaybackStateCompat.CustomAction; 39import android.text.TextUtils; 40import android.util.Log; 41import android.view.KeyEvent; 42 43import java.lang.ref.WeakReference; 44import java.util.ArrayList; 45import java.util.HashMap; 46import java.util.List; 47 48/** 49 * Allows an app to interact with an ongoing media session. Media buttons and 50 * other commands can be sent to the session. A callback may be registered to 51 * receive updates from the session, such as metadata and play state changes. 52 * <p> 53 * A MediaController can be created if you have a {@link MediaSessionCompat.Token} 54 * from the session owner. 55 * <p> 56 * MediaController objects are thread-safe. 57 * <p> 58 * This is a helper for accessing features in {@link android.media.session.MediaSession} 59 * introduced after API level 4 in a backwards compatible fashion. 60 */ 61public final class MediaControllerCompat { 62 static final String TAG = "MediaControllerCompat"; 63 64 static final String COMMAND_GET_EXTRA_BINDER = 65 "android.support.v4.media.session.command.GET_EXTRA_BINDER"; 66 static final String COMMAND_ADD_QUEUE_ITEM = 67 "android.support.v4.media.session.command.ADD_QUEUE_ITEM"; 68 static final String COMMAND_ADD_QUEUE_ITEM_AT = 69 "android.support.v4.media.session.command.ADD_QUEUE_ITEM_AT"; 70 static final String COMMAND_REMOVE_QUEUE_ITEM = 71 "android.support.v4.media.session.command.REMOVE_QUEUE_ITEM"; 72 static final String COMMAND_REMOVE_QUEUE_ITEM_AT = 73 "android.support.v4.media.session.command.REMOVE_QUEUE_ITEM_AT"; 74 75 static final String COMMAND_ARGUMENT_MEDIA_DESCRIPTION = 76 "android.support.v4.media.session.command.ARGUMENT_MEDIA_DESCRIPTION"; 77 static final String COMMAND_ARGUMENT_INDEX = 78 "android.support.v4.media.session.command.ARGUMENT_INDEX"; 79 80 private static class MediaControllerExtraData extends SupportActivity.ExtraData { 81 private final MediaControllerCompat mMediaController; 82 83 MediaControllerExtraData(MediaControllerCompat mediaController) { 84 mMediaController = mediaController; 85 } 86 87 MediaControllerCompat getMediaController() { 88 return mMediaController; 89 } 90 } 91 92 /** 93 * Sets a {@link MediaControllerCompat} for later retrieval via 94 * {@link #getMediaController()}. 95 * 96 * <p>On API 21 and later, this controller will be tied to the window of the activity and 97 * media key and volume events which are received while the Activity is in the foreground 98 * will be forwarded to the controller and used to invoke transport controls or adjust the 99 * volume. Prior to API 21, the global handling of media key and volume events through an 100 * active {@link android.support.v4.media.session.MediaSessionCompat} and media button receiver 101 * will still be respected.</p> 102 * 103 * @param mediaController The controller for the session which should receive 104 * media keys and volume changes on API 21 and later. 105 * @see #getMediaController() 106 * @see Activity#setMediaController(android.media.session.MediaController) 107 */ 108 public static void setMediaController(Activity activity, 109 MediaControllerCompat mediaController) { 110 if (activity instanceof SupportActivity) { 111 ((SupportActivity) activity).putExtraData( 112 new MediaControllerExtraData(mediaController)); 113 } 114 if (android.os.Build.VERSION.SDK_INT >= 21) { 115 Object controllerObj = null; 116 if (mediaController != null) { 117 Object sessionTokenObj = mediaController.getSessionToken().getToken(); 118 controllerObj = MediaControllerCompatApi21.fromToken(activity, sessionTokenObj); 119 } 120 MediaControllerCompatApi21.setMediaController(activity, controllerObj); 121 } 122 } 123 124 /** 125 * Retrieves the current {@link MediaControllerCompat} for sending media key and volume events. 126 * 127 * @return The controller which should receive events. 128 * @see #setMediaController(Activity,MediaControllerCompat) 129 * @see #getMediaController() 130 */ 131 public static MediaControllerCompat getMediaController(Activity activity) { 132 if (activity instanceof SupportActivity) { 133 MediaControllerExtraData extraData = 134 ((SupportActivity) activity).getExtraData(MediaControllerExtraData.class); 135 return extraData != null ? extraData.getMediaController() : null; 136 } else if (android.os.Build.VERSION.SDK_INT >= 21) { 137 Object controllerObj = MediaControllerCompatApi21.getMediaController(activity); 138 if (controllerObj == null) { 139 return null; 140 } 141 Object sessionTokenObj = MediaControllerCompatApi21.getSessionToken(controllerObj); 142 try { 143 return new MediaControllerCompat(activity, 144 MediaSessionCompat.Token.fromToken(sessionTokenObj)); 145 } catch (RemoteException e) { 146 Log.e(TAG, "Dead object in getMediaController.", e); 147 } 148 } 149 return null; 150 } 151 152 private final MediaControllerImpl mImpl; 153 private final MediaSessionCompat.Token mToken; 154 155 /** 156 * Creates a media controller from a session. 157 * 158 * @param session The session to be controlled. 159 */ 160 public MediaControllerCompat(Context context, MediaSessionCompat session) { 161 if (session == null) { 162 throw new IllegalArgumentException("session must not be null"); 163 } 164 mToken = session.getSessionToken(); 165 166 if (android.os.Build.VERSION.SDK_INT >= 24) { 167 mImpl = new MediaControllerImplApi24(context, session); 168 } else if (android.os.Build.VERSION.SDK_INT >= 23) { 169 mImpl = new MediaControllerImplApi23(context, session); 170 } else if (android.os.Build.VERSION.SDK_INT >= 21) { 171 mImpl = new MediaControllerImplApi21(context, session); 172 } else { 173 mImpl = new MediaControllerImplBase(mToken); 174 } 175 } 176 177 /** 178 * Creates a media controller from a session token which may have 179 * been obtained from another process. 180 * 181 * @param sessionToken The token of the session to be controlled. 182 * @throws RemoteException if the session is not accessible. 183 */ 184 public MediaControllerCompat(Context context, MediaSessionCompat.Token sessionToken) 185 throws RemoteException { 186 if (sessionToken == null) { 187 throw new IllegalArgumentException("sessionToken must not be null"); 188 } 189 mToken = sessionToken; 190 191 if (android.os.Build.VERSION.SDK_INT >= 24) { 192 mImpl = new MediaControllerImplApi24(context, sessionToken); 193 } else if (android.os.Build.VERSION.SDK_INT >= 23) { 194 mImpl = new MediaControllerImplApi23(context, sessionToken); 195 } else if (android.os.Build.VERSION.SDK_INT >= 21) { 196 mImpl = new MediaControllerImplApi21(context, sessionToken); 197 } else { 198 mImpl = new MediaControllerImplBase(mToken); 199 } 200 } 201 202 /** 203 * Get a {@link TransportControls} instance for this session. 204 * 205 * @return A controls instance 206 */ 207 public TransportControls getTransportControls() { 208 return mImpl.getTransportControls(); 209 } 210 211 /** 212 * Send the specified media button event to the session. Only media keys can 213 * be sent by this method, other keys will be ignored. 214 * 215 * @param keyEvent The media button event to dispatch. 216 * @return true if the event was sent to the session, false otherwise. 217 */ 218 public boolean dispatchMediaButtonEvent(KeyEvent keyEvent) { 219 if (keyEvent == null) { 220 throw new IllegalArgumentException("KeyEvent may not be null"); 221 } 222 return mImpl.dispatchMediaButtonEvent(keyEvent); 223 } 224 225 /** 226 * Get the current playback state for this session. 227 * 228 * @return The current PlaybackState or null 229 */ 230 public PlaybackStateCompat getPlaybackState() { 231 return mImpl.getPlaybackState(); 232 } 233 234 /** 235 * Get the current metadata for this session. 236 * 237 * @return The current MediaMetadata or null. 238 */ 239 public MediaMetadataCompat getMetadata() { 240 return mImpl.getMetadata(); 241 } 242 243 /** 244 * Get the current play queue for this session if one is set. If you only 245 * care about the current item {@link #getMetadata()} should be used. 246 * 247 * @return The current play queue or null. 248 */ 249 public List<MediaSessionCompat.QueueItem> getQueue() { 250 return mImpl.getQueue(); 251 } 252 253 /** 254 * Add a queue item from the given {@code description} at the end of the play queue 255 * of this session. Not all sessions may support this. To know whether the session supports 256 * this, get the session's flags with {@link #getFlags()} and check that the flag 257 * {@link MediaSessionCompat#FLAG_HANDLES_QUEUE_COMMANDS} is set. 258 * 259 * @param description The {@link MediaDescriptionCompat} for creating the 260 * {@link MediaSessionCompat.QueueItem} to be inserted. 261 * @throws UnsupportedOperationException If this session doesn't support this. 262 * @see #getFlags() 263 * @see MediaSessionCompat#FLAG_HANDLES_QUEUE_COMMANDS 264 */ 265 public void addQueueItem(MediaDescriptionCompat description) { 266 mImpl.addQueueItem(description); 267 } 268 269 /** 270 * Add a queue item from the given {@code description} at the specified position 271 * in the play queue of this session. Shifts the queue item currently at that position 272 * (if any) and any subsequent queue items to the right (adds one to their indices). 273 * Not all sessions may support this. To know whether the session supports this, 274 * get the session's flags with {@link #getFlags()} and check that the flag 275 * {@link MediaSessionCompat#FLAG_HANDLES_QUEUE_COMMANDS} is set. 276 * 277 * @param description The {@link MediaDescriptionCompat} for creating the 278 * {@link MediaSessionCompat.QueueItem} to be inserted. 279 * @param index The index at which the created {@link MediaSessionCompat.QueueItem} 280 * is to be inserted. 281 * @throws UnsupportedOperationException If this session doesn't support this. 282 * @see #getFlags() 283 * @see MediaSessionCompat#FLAG_HANDLES_QUEUE_COMMANDS 284 */ 285 public void addQueueItem(MediaDescriptionCompat description, int index) { 286 mImpl.addQueueItem(description, index); 287 } 288 289 /** 290 * Remove the first occurrence of the specified {@link MediaSessionCompat.QueueItem} 291 * with the given {@link MediaDescriptionCompat description} in the play queue of the 292 * associated session. Not all sessions may support this. To know whether the session supports 293 * this, get the session's flags with {@link #getFlags()} and check that the flag 294 * {@link MediaSessionCompat#FLAG_HANDLES_QUEUE_COMMANDS} is set. 295 * 296 * @param description The {@link MediaDescriptionCompat} for denoting the 297 * {@link MediaSessionCompat.QueueItem} to be removed. 298 * @throws UnsupportedOperationException If this session doesn't support this. 299 * @see #getFlags() 300 * @see MediaSessionCompat#FLAG_HANDLES_QUEUE_COMMANDS 301 */ 302 public void removeQueueItem(MediaDescriptionCompat description) { 303 mImpl.removeQueueItem(description); 304 } 305 306 /** 307 * Remove an queue item at the specified position in the play queue 308 * of this session. Not all sessions may support this. To know whether the session supports 309 * this, get the session's flags with {@link #getFlags()} and check that the flag 310 * {@link MediaSessionCompat#FLAG_HANDLES_QUEUE_COMMANDS} is set. 311 * 312 * @param index The index of the element to be removed. 313 * @throws UnsupportedOperationException If this session doesn't support this. 314 * @see #getFlags() 315 * @see MediaSessionCompat#FLAG_HANDLES_QUEUE_COMMANDS 316 */ 317 public void removeQueueItemAt(int index) { 318 mImpl.removeQueueItemAt(index); 319 } 320 321 /** 322 * Get the queue title for this session. 323 */ 324 public CharSequence getQueueTitle() { 325 return mImpl.getQueueTitle(); 326 } 327 328 /** 329 * Get the extras for this session. 330 */ 331 public Bundle getExtras() { 332 return mImpl.getExtras(); 333 } 334 335 /** 336 * Get the rating type supported by the session. One of: 337 * <ul> 338 * <li>{@link RatingCompat#RATING_NONE}</li> 339 * <li>{@link RatingCompat#RATING_HEART}</li> 340 * <li>{@link RatingCompat#RATING_THUMB_UP_DOWN}</li> 341 * <li>{@link RatingCompat#RATING_3_STARS}</li> 342 * <li>{@link RatingCompat#RATING_4_STARS}</li> 343 * <li>{@link RatingCompat#RATING_5_STARS}</li> 344 * <li>{@link RatingCompat#RATING_PERCENTAGE}</li> 345 * </ul> 346 * 347 * @return The supported rating type 348 */ 349 public int getRatingType() { 350 return mImpl.getRatingType(); 351 } 352 353 /** 354 * Return whether captioning is enabled for this session. 355 * 356 * @return {@code true} if captioning is enabled, {@code false} if disabled or not set. 357 */ 358 public boolean isCaptioningEnabled() { 359 return mImpl.isCaptioningEnabled(); 360 } 361 362 /** 363 * Get the repeat mode for this session. 364 * 365 * @return The latest repeat mode set to the session, or 366 * {@link PlaybackStateCompat#REPEAT_MODE_NONE} if not set. 367 */ 368 public int getRepeatMode() { 369 return mImpl.getRepeatMode(); 370 } 371 372 /** 373 * Return whether the shuffle mode is enabled for this session. 374 * 375 * @return {@code true} if the shuffle mode is enabled, {@code false} if disabled or not set. 376 */ 377 public boolean isShuffleModeEnabled() { 378 return mImpl.isShuffleModeEnabled(); 379 } 380 381 /** 382 * Get the flags for this session. Flags are defined in 383 * {@link MediaSessionCompat}. 384 * 385 * @return The current set of flags for the session. 386 */ 387 public long getFlags() { 388 return mImpl.getFlags(); 389 } 390 391 /** 392 * Get the current playback info for this session. 393 * 394 * @return The current playback info or null. 395 */ 396 public PlaybackInfo getPlaybackInfo() { 397 return mImpl.getPlaybackInfo(); 398 } 399 400 /** 401 * Get an intent for launching UI associated with this session if one 402 * exists. 403 * 404 * @return A {@link PendingIntent} to launch UI or null. 405 */ 406 public PendingIntent getSessionActivity() { 407 return mImpl.getSessionActivity(); 408 } 409 410 /** 411 * Get the token for the session this controller is connected to. 412 * 413 * @return The session's token. 414 */ 415 public MediaSessionCompat.Token getSessionToken() { 416 return mToken; 417 } 418 419 /** 420 * Set the volume of the output this session is playing on. The command will 421 * be ignored if it does not support 422 * {@link VolumeProviderCompat#VOLUME_CONTROL_ABSOLUTE}. The flags in 423 * {@link AudioManager} may be used to affect the handling. 424 * 425 * @see #getPlaybackInfo() 426 * @param value The value to set it to, between 0 and the reported max. 427 * @param flags Flags from {@link AudioManager} to include with the volume 428 * request. 429 */ 430 public void setVolumeTo(int value, int flags) { 431 mImpl.setVolumeTo(value, flags); 432 } 433 434 /** 435 * Adjust the volume of the output this session is playing on. The direction 436 * must be one of {@link AudioManager#ADJUST_LOWER}, 437 * {@link AudioManager#ADJUST_RAISE}, or {@link AudioManager#ADJUST_SAME}. 438 * The command will be ignored if the session does not support 439 * {@link VolumeProviderCompat#VOLUME_CONTROL_RELATIVE} or 440 * {@link VolumeProviderCompat#VOLUME_CONTROL_ABSOLUTE}. The flags in 441 * {@link AudioManager} may be used to affect the handling. 442 * 443 * @see #getPlaybackInfo() 444 * @param direction The direction to adjust the volume in. 445 * @param flags Any flags to pass with the command. 446 */ 447 public void adjustVolume(int direction, int flags) { 448 mImpl.adjustVolume(direction, flags); 449 } 450 451 /** 452 * Adds a callback to receive updates from the Session. Updates will be 453 * posted on the caller's thread. 454 * 455 * @param callback The callback object, must not be null. 456 */ 457 public void registerCallback(Callback callback) { 458 registerCallback(callback, null); 459 } 460 461 /** 462 * Adds a callback to receive updates from the session. Updates will be 463 * posted on the specified handler's thread. 464 * 465 * @param callback The callback object, must not be null. 466 * @param handler The handler to post updates on. If null the callers thread 467 * will be used. 468 */ 469 public void registerCallback(Callback callback, Handler handler) { 470 if (callback == null) { 471 throw new IllegalArgumentException("callback cannot be null"); 472 } 473 if (handler == null) { 474 handler = new Handler(); 475 } 476 mImpl.registerCallback(callback, handler); 477 } 478 479 /** 480 * Stop receiving updates on the specified callback. If an update has 481 * already been posted you may still receive it after calling this method. 482 * 483 * @param callback The callback to remove 484 */ 485 public void unregisterCallback(Callback callback) { 486 if (callback == null) { 487 throw new IllegalArgumentException("callback cannot be null"); 488 } 489 mImpl.unregisterCallback(callback); 490 } 491 492 /** 493 * Sends a generic command to the session. It is up to the session creator 494 * to decide what commands and parameters they will support. As such, 495 * commands should only be sent to sessions that the controller owns. 496 * 497 * @param command The command to send 498 * @param params Any parameters to include with the command 499 * @param cb The callback to receive the result on 500 */ 501 public void sendCommand(String command, Bundle params, ResultReceiver cb) { 502 if (TextUtils.isEmpty(command)) { 503 throw new IllegalArgumentException("command cannot be null or empty"); 504 } 505 mImpl.sendCommand(command, params, cb); 506 } 507 508 /** 509 * Get the session owner's package name. 510 * 511 * @return The package name of of the session owner. 512 */ 513 public String getPackageName() { 514 return mImpl.getPackageName(); 515 } 516 517 /** 518 * Gets the underlying framework 519 * {@link android.media.session.MediaController} object. 520 * <p> 521 * This method is only supported on API 21+. 522 * </p> 523 * 524 * @return The underlying {@link android.media.session.MediaController} 525 * object, or null if none. 526 */ 527 public Object getMediaController() { 528 return mImpl.getMediaController(); 529 } 530 531 /** 532 * Callback for receiving updates on from the session. A Callback can be 533 * registered using {@link #registerCallback} 534 */ 535 public static abstract class Callback implements IBinder.DeathRecipient { 536 private final Object mCallbackObj; 537 MessageHandler mHandler; 538 boolean mHasExtraCallback; 539 540 boolean mRegistered = false; 541 542 public Callback() { 543 if (android.os.Build.VERSION.SDK_INT >= 21) { 544 mCallbackObj = MediaControllerCompatApi21.createCallback(new StubApi21()); 545 } else { 546 mCallbackObj = new StubCompat(); 547 } 548 } 549 550 /** 551 * Override to handle the session being destroyed. The session is no 552 * longer valid after this call and calls to it will be ignored. 553 */ 554 public void onSessionDestroyed() { 555 } 556 557 /** 558 * Override to handle custom events sent by the session owner without a 559 * specified interface. Controllers should only handle these for 560 * sessions they own. 561 * 562 * @param event The event from the session. 563 * @param extras Optional parameters for the event. 564 */ 565 public void onSessionEvent(String event, Bundle extras) { 566 } 567 568 /** 569 * Override to handle changes in playback state. 570 * 571 * @param state The new playback state of the session 572 */ 573 public void onPlaybackStateChanged(PlaybackStateCompat state) { 574 } 575 576 /** 577 * Override to handle changes to the current metadata. 578 * 579 * @param metadata The current metadata for the session or null if none. 580 * @see MediaMetadataCompat 581 */ 582 public void onMetadataChanged(MediaMetadataCompat metadata) { 583 } 584 585 /** 586 * Override to handle changes to items in the queue. 587 * 588 * @see MediaSessionCompat.QueueItem 589 * @param queue A list of items in the current play queue. It should 590 * include the currently playing item as well as previous and 591 * upcoming items if applicable. 592 */ 593 public void onQueueChanged(List<MediaSessionCompat.QueueItem> queue) { 594 } 595 596 /** 597 * Override to handle changes to the queue title. 598 * 599 * @param title The title that should be displayed along with the play 600 * queue such as "Now Playing". May be null if there is no 601 * such title. 602 */ 603 public void onQueueTitleChanged(CharSequence title) { 604 } 605 606 /** 607 * Override to handle chagnes to the {@link MediaSessionCompat} extras. 608 * 609 * @param extras The extras that can include other information 610 * associated with the {@link MediaSessionCompat}. 611 */ 612 public void onExtrasChanged(Bundle extras) { 613 } 614 615 /** 616 * Override to handle changes to the audio info. 617 * 618 * @param info The current audio info for this session. 619 */ 620 public void onAudioInfoChanged(PlaybackInfo info) { 621 } 622 623 /** 624 * Override to handle changes to the captioning enabled status. 625 * 626 * @param enabled {@code true} if captioning is enabled, {@code false} otherwise. 627 */ 628 public void onCaptioningEnabledChanged(boolean enabled) { 629 } 630 631 /** 632 * Override to handle changes to the repeat mode. 633 * 634 * @param repeatMode The repeat mode. It should be one of followings: 635 * {@link PlaybackStateCompat#REPEAT_MODE_NONE}, 636 * {@link PlaybackStateCompat#REPEAT_MODE_ONE}, 637 * {@link PlaybackStateCompat#REPEAT_MODE_ALL} 638 */ 639 public void onRepeatModeChanged(@PlaybackStateCompat.RepeatMode int repeatMode) { 640 } 641 642 /** 643 * Override to handle changes to the shuffle mode. 644 * 645 * @param enabled {@code true} if the shuffle mode is enabled, {@code false} otherwise. 646 */ 647 public void onShuffleModeChanged(boolean enabled) { 648 } 649 650 @Override 651 public void binderDied() { 652 onSessionDestroyed(); 653 } 654 655 /** 656 * Set the handler to use for pre 21 callbacks. 657 */ 658 private void setHandler(Handler handler) { 659 mHandler = new MessageHandler(handler.getLooper()); 660 } 661 662 private class StubApi21 implements MediaControllerCompatApi21.Callback { 663 StubApi21() { 664 } 665 666 @Override 667 public void onSessionDestroyed() { 668 Callback.this.onSessionDestroyed(); 669 } 670 671 @Override 672 public void onSessionEvent(String event, Bundle extras) { 673 if (mHasExtraCallback && android.os.Build.VERSION.SDK_INT < 23) { 674 // Ignore. ExtraCallback will handle this. 675 } else { 676 Callback.this.onSessionEvent(event, extras); 677 } 678 } 679 680 @Override 681 public void onPlaybackStateChanged(Object stateObj) { 682 if (mHasExtraCallback) { 683 // Ignore. ExtraCallback will handle this. 684 } else { 685 Callback.this.onPlaybackStateChanged( 686 PlaybackStateCompat.fromPlaybackState(stateObj)); 687 } 688 } 689 690 @Override 691 public void onMetadataChanged(Object metadataObj) { 692 Callback.this.onMetadataChanged(MediaMetadataCompat.fromMediaMetadata(metadataObj)); 693 } 694 695 @Override 696 public void onQueueChanged(List<?> queue) { 697 Callback.this.onQueueChanged(QueueItem.fromQueueItemList(queue)); 698 } 699 700 @Override 701 public void onQueueTitleChanged(CharSequence title) { 702 Callback.this.onQueueTitleChanged(title); 703 } 704 705 @Override 706 public void onExtrasChanged(Bundle extras) { 707 Callback.this.onExtrasChanged(extras); 708 } 709 710 @Override 711 public void onAudioInfoChanged( 712 int type, int stream, int control, int max, int current) { 713 Callback.this.onAudioInfoChanged( 714 new PlaybackInfo(type, stream, control, max, current)); 715 } 716 } 717 718 private class StubCompat extends IMediaControllerCallback.Stub { 719 720 StubCompat() { 721 } 722 723 @Override 724 public void onEvent(String event, Bundle extras) throws RemoteException { 725 mHandler.post(MessageHandler.MSG_EVENT, event, extras); 726 } 727 728 @Override 729 public void onSessionDestroyed() throws RemoteException { 730 mHandler.post(MessageHandler.MSG_DESTROYED, null, null); 731 } 732 733 @Override 734 public void onPlaybackStateChanged(PlaybackStateCompat state) throws RemoteException { 735 mHandler.post(MessageHandler.MSG_UPDATE_PLAYBACK_STATE, state, null); 736 } 737 738 @Override 739 public void onMetadataChanged(MediaMetadataCompat metadata) throws RemoteException { 740 mHandler.post(MessageHandler.MSG_UPDATE_METADATA, metadata, null); 741 } 742 743 @Override 744 public void onQueueChanged(List<QueueItem> queue) throws RemoteException { 745 mHandler.post(MessageHandler.MSG_UPDATE_QUEUE, queue, null); 746 } 747 748 @Override 749 public void onQueueTitleChanged(CharSequence title) throws RemoteException { 750 mHandler.post(MessageHandler.MSG_UPDATE_QUEUE_TITLE, title, null); 751 } 752 753 @Override 754 public void onCaptioningEnabledChanged(boolean enabled) throws RemoteException { 755 mHandler.post(MessageHandler.MSG_UPDATE_CAPTIONING_ENABLED, enabled, null); 756 } 757 758 @Override 759 public void onRepeatModeChanged(int repeatMode) throws RemoteException { 760 mHandler.post(MessageHandler.MSG_UPDATE_REPEAT_MODE, repeatMode, null); 761 } 762 763 @Override 764 public void onShuffleModeChanged(boolean enabled) throws RemoteException { 765 mHandler.post(MessageHandler.MSG_UPDATE_SHUFFLE_MODE, enabled, null); 766 } 767 768 @Override 769 public void onExtrasChanged(Bundle extras) throws RemoteException { 770 mHandler.post(MessageHandler.MSG_UPDATE_EXTRAS, extras, null); 771 } 772 773 @Override 774 public void onVolumeInfoChanged(ParcelableVolumeInfo info) throws RemoteException { 775 PlaybackInfo pi = null; 776 if (info != null) { 777 pi = new PlaybackInfo(info.volumeType, info.audioStream, info.controlType, 778 info.maxVolume, info.currentVolume); 779 } 780 mHandler.post(MessageHandler.MSG_UPDATE_VOLUME, pi, null); 781 } 782 } 783 784 private class MessageHandler extends Handler { 785 private static final int MSG_EVENT = 1; 786 private static final int MSG_UPDATE_PLAYBACK_STATE = 2; 787 private static final int MSG_UPDATE_METADATA = 3; 788 private static final int MSG_UPDATE_VOLUME = 4; 789 private static final int MSG_UPDATE_QUEUE = 5; 790 private static final int MSG_UPDATE_QUEUE_TITLE = 6; 791 private static final int MSG_UPDATE_EXTRAS = 7; 792 private static final int MSG_DESTROYED = 8; 793 private static final int MSG_UPDATE_REPEAT_MODE = 9; 794 private static final int MSG_UPDATE_SHUFFLE_MODE = 10; 795 private static final int MSG_UPDATE_CAPTIONING_ENABLED = 11; 796 797 public MessageHandler(Looper looper) { 798 super(looper); 799 } 800 801 @Override 802 public void handleMessage(Message msg) { 803 if (!mRegistered) { 804 return; 805 } 806 switch (msg.what) { 807 case MSG_EVENT: 808 onSessionEvent((String) msg.obj, msg.getData()); 809 break; 810 case MSG_UPDATE_PLAYBACK_STATE: 811 onPlaybackStateChanged((PlaybackStateCompat) msg.obj); 812 break; 813 case MSG_UPDATE_METADATA: 814 onMetadataChanged((MediaMetadataCompat) msg.obj); 815 break; 816 case MSG_UPDATE_QUEUE: 817 onQueueChanged((List<MediaSessionCompat.QueueItem>) msg.obj); 818 break; 819 case MSG_UPDATE_QUEUE_TITLE: 820 onQueueTitleChanged((CharSequence) msg.obj); 821 break; 822 case MSG_UPDATE_CAPTIONING_ENABLED: 823 onCaptioningEnabledChanged((boolean) msg.obj); 824 break; 825 case MSG_UPDATE_REPEAT_MODE: 826 onRepeatModeChanged((int) msg.obj); 827 break; 828 case MSG_UPDATE_SHUFFLE_MODE: 829 onShuffleModeChanged((boolean) msg.obj); 830 break; 831 case MSG_UPDATE_EXTRAS: 832 onExtrasChanged((Bundle) msg.obj); 833 break; 834 case MSG_UPDATE_VOLUME: 835 onAudioInfoChanged((PlaybackInfo) msg.obj); 836 break; 837 case MSG_DESTROYED: 838 onSessionDestroyed(); 839 break; 840 } 841 } 842 843 public void post(int what, Object obj, Bundle data) { 844 Message msg = obtainMessage(what, obj); 845 msg.setData(data); 846 msg.sendToTarget(); 847 } 848 } 849 } 850 851 /** 852 * Interface for controlling media playback on a session. This allows an app 853 * to send media transport commands to the session. 854 */ 855 public static abstract class TransportControls { 856 /** 857 * Used as an integer extra field in {@link #playFromMediaId(String, Bundle)} or 858 * {@link #prepareFromMediaId(String, Bundle)} to indicate the stream type to be used by the 859 * media player when playing or preparing the specified media id. See {@link AudioManager} 860 * for a list of stream types. 861 */ 862 public static final String EXTRA_STREAM_TYPE = "android.media.session.extra.STREAM_TYPE"; 863 864 TransportControls() { 865 } 866 867 /** 868 * Request that the player prepare its playback without audio focus. In other words, other 869 * session can continue to play during the preparation of this session. This method can be 870 * used to speed up the start of the playback. Once the preparation is done, the session 871 * will change its playback state to {@link PlaybackStateCompat#STATE_PAUSED}. Afterwards, 872 * {@link #play} can be called to start playback. If the preparation is not needed, 873 * {@link #play} can be directly called without this method. 874 */ 875 public abstract void prepare(); 876 877 /** 878 * Request that the player prepare playback for a specific media id. In other words, other 879 * session can continue to play during the preparation of this session. This method can be 880 * used to speed up the start of the playback. Once the preparation is 881 * done, the session will change its playback state to 882 * {@link PlaybackStateCompat#STATE_PAUSED}. Afterwards, {@link #play} can be called to 883 * start playback. If the preparation is not needed, {@link #playFromMediaId} can 884 * be directly called without this method. 885 * 886 * @param mediaId The id of the requested media. 887 * @param extras Optional extras that can include extra information about the media item 888 * to be prepared. 889 */ 890 public abstract void prepareFromMediaId(String mediaId, Bundle extras); 891 892 /** 893 * Request that the player prepare playback for a specific search query. 894 * An empty or null query should be treated as a request to prepare any 895 * music. In other words, other session can continue to play during 896 * the preparation of this session. This method can be used to speed up the start of the 897 * playback. Once the preparation is done, the session will change its playback state to 898 * {@link PlaybackStateCompat#STATE_PAUSED}. Afterwards, {@link #play} can be called to 899 * start playback. If the preparation is not needed, {@link #playFromSearch} can be directly 900 * called without this method. 901 * 902 * @param query The search query. 903 * @param extras Optional extras that can include extra information 904 * about the query. 905 */ 906 public abstract void prepareFromSearch(String query, Bundle extras); 907 908 /** 909 * Request that the player prepare playback for a specific {@link Uri}. 910 * In other words, other session can continue to play during the preparation of this 911 * session. This method can be used to speed up the start of the playback. 912 * Once the preparation is done, the session will change its playback state to 913 * {@link PlaybackStateCompat#STATE_PAUSED}. Afterwards, {@link #play} can be called to 914 * start playback. If the preparation is not needed, {@link #playFromUri} can be directly 915 * called without this method. 916 * 917 * @param uri The URI of the requested media. 918 * @param extras Optional extras that can include extra information about the media item 919 * to be prepared. 920 */ 921 public abstract void prepareFromUri(Uri uri, Bundle extras); 922 923 /** 924 * Request that the player start its playback at its current position. 925 */ 926 public abstract void play(); 927 928 /** 929 * Request that the player start playback for a specific {@link Uri}. 930 * 931 * @param mediaId The uri of the requested media. 932 * @param extras Optional extras that can include extra information 933 * about the media item to be played. 934 */ 935 public abstract void playFromMediaId(String mediaId, Bundle extras); 936 937 /** 938 * Request that the player start playback for a specific search query. 939 * An empty or null query should be treated as a request to play any 940 * music. 941 * 942 * @param query The search query. 943 * @param extras Optional extras that can include extra information 944 * about the query. 945 */ 946 public abstract void playFromSearch(String query, Bundle extras); 947 948 /** 949 * Request that the player start playback for a specific {@link Uri}. 950 * 951 * @param uri The URI of the requested media. 952 * @param extras Optional extras that can include extra information about the media item 953 * to be played. 954 */ 955 public abstract void playFromUri(Uri uri, Bundle extras); 956 957 /** 958 * Play an item with a specific id in the play queue. If you specify an 959 * id that is not in the play queue, the behavior is undefined. 960 */ 961 public abstract void skipToQueueItem(long id); 962 963 /** 964 * Request that the player pause its playback and stay at its current 965 * position. 966 */ 967 public abstract void pause(); 968 969 /** 970 * Request that the player stop its playback; it may clear its state in 971 * whatever way is appropriate. 972 */ 973 public abstract void stop(); 974 975 /** 976 * Move to a new location in the media stream. 977 * 978 * @param pos Position to move to, in milliseconds. 979 */ 980 public abstract void seekTo(long pos); 981 982 /** 983 * Start fast forwarding. If playback is already fast forwarding this 984 * may increase the rate. 985 */ 986 public abstract void fastForward(); 987 988 /** 989 * Skip to the next item. 990 */ 991 public abstract void skipToNext(); 992 993 /** 994 * Start rewinding. If playback is already rewinding this may increase 995 * the rate. 996 */ 997 public abstract void rewind(); 998 999 /** 1000 * Skip to the previous item. 1001 */ 1002 public abstract void skipToPrevious(); 1003 1004 /** 1005 * Rate the current content. This will cause the rating to be set for 1006 * the current user. The Rating type must match the type returned by 1007 * {@link #getRatingType()}. 1008 * 1009 * @param rating The rating to set for the current content 1010 */ 1011 public abstract void setRating(RatingCompat rating); 1012 1013 /** 1014 * Enable/disable captioning for this session. 1015 * 1016 * @param enabled {@code true} to enable captioning, {@code false} to disable. 1017 */ 1018 public abstract void setCaptioningEnabled(boolean enabled); 1019 1020 /** 1021 * Set the repeat mode for this session. 1022 * 1023 * @param repeatMode The repeat mode. Must be one of the followings: 1024 * {@link PlaybackStateCompat#REPEAT_MODE_NONE}, 1025 * {@link PlaybackStateCompat#REPEAT_MODE_ONE}, 1026 * {@link PlaybackStateCompat#REPEAT_MODE_ALL} 1027 */ 1028 public abstract void setRepeatMode(@PlaybackStateCompat.RepeatMode int repeatMode); 1029 1030 /** 1031 * Set the shuffle mode for this session. 1032 * 1033 * @param enabled {@code true} to enable the shuffle mode, {@code false} to disable. 1034 */ 1035 public abstract void setShuffleModeEnabled(boolean enabled); 1036 1037 /** 1038 * Send a custom action for the {@link MediaSessionCompat} to perform. 1039 * 1040 * @param customAction The action to perform. 1041 * @param args Optional arguments to supply to the 1042 * {@link MediaSessionCompat} for this custom action. 1043 */ 1044 public abstract void sendCustomAction(PlaybackStateCompat.CustomAction customAction, 1045 Bundle args); 1046 1047 /** 1048 * Send the id and args from a custom action for the 1049 * {@link MediaSessionCompat} to perform. 1050 * 1051 * @see #sendCustomAction(PlaybackStateCompat.CustomAction action, 1052 * Bundle args) 1053 * @see MediaSessionCompat#ACTION_FLAG_AS_INAPPROPRIATE 1054 * @see MediaSessionCompat#ACTION_SKIP_AD 1055 * @param action The action identifier of the 1056 * {@link PlaybackStateCompat.CustomAction} as specified by 1057 * the {@link MediaSessionCompat}. 1058 * @param args Optional arguments to supply to the 1059 * {@link MediaSessionCompat} for this custom action. 1060 */ 1061 public abstract void sendCustomAction(String action, Bundle args); 1062 } 1063 1064 /** 1065 * Holds information about the way volume is handled for this session. 1066 */ 1067 public static final class PlaybackInfo { 1068 /** 1069 * The session uses local playback. 1070 */ 1071 public static final int PLAYBACK_TYPE_LOCAL = 1; 1072 /** 1073 * The session uses remote playback. 1074 */ 1075 public static final int PLAYBACK_TYPE_REMOTE = 2; 1076 1077 private final int mPlaybackType; 1078 // TODO update audio stream with AudioAttributes support version 1079 private final int mAudioStream; 1080 private final int mVolumeControl; 1081 private final int mMaxVolume; 1082 private final int mCurrentVolume; 1083 1084 PlaybackInfo(int type, int stream, int control, int max, int current) { 1085 mPlaybackType = type; 1086 mAudioStream = stream; 1087 mVolumeControl = control; 1088 mMaxVolume = max; 1089 mCurrentVolume = current; 1090 } 1091 1092 /** 1093 * Get the type of volume handling, either local or remote. One of: 1094 * <ul> 1095 * <li>{@link PlaybackInfo#PLAYBACK_TYPE_LOCAL}</li> 1096 * <li>{@link PlaybackInfo#PLAYBACK_TYPE_REMOTE}</li> 1097 * </ul> 1098 * 1099 * @return The type of volume handling this session is using. 1100 */ 1101 public int getPlaybackType() { 1102 return mPlaybackType; 1103 } 1104 1105 /** 1106 * Get the stream this is currently controlling volume on. When the volume 1107 * type is {@link PlaybackInfo#PLAYBACK_TYPE_REMOTE} this value does not 1108 * have meaning and should be ignored. 1109 * 1110 * @return The stream this session is playing on. 1111 */ 1112 public int getAudioStream() { 1113 // TODO switch to AudioAttributesCompat when it is added. 1114 return mAudioStream; 1115 } 1116 1117 /** 1118 * Get the type of volume control that can be used. One of: 1119 * <ul> 1120 * <li>{@link VolumeProviderCompat#VOLUME_CONTROL_ABSOLUTE}</li> 1121 * <li>{@link VolumeProviderCompat#VOLUME_CONTROL_RELATIVE}</li> 1122 * <li>{@link VolumeProviderCompat#VOLUME_CONTROL_FIXED}</li> 1123 * </ul> 1124 * 1125 * @return The type of volume control that may be used with this 1126 * session. 1127 */ 1128 public int getVolumeControl() { 1129 return mVolumeControl; 1130 } 1131 1132 /** 1133 * Get the maximum volume that may be set for this session. 1134 * 1135 * @return The maximum allowed volume where this session is playing. 1136 */ 1137 public int getMaxVolume() { 1138 return mMaxVolume; 1139 } 1140 1141 /** 1142 * Get the current volume for this session. 1143 * 1144 * @return The current volume where this session is playing. 1145 */ 1146 public int getCurrentVolume() { 1147 return mCurrentVolume; 1148 } 1149 } 1150 1151 interface MediaControllerImpl { 1152 void registerCallback(Callback callback, Handler handler); 1153 1154 void unregisterCallback(Callback callback); 1155 boolean dispatchMediaButtonEvent(KeyEvent keyEvent); 1156 TransportControls getTransportControls(); 1157 PlaybackStateCompat getPlaybackState(); 1158 MediaMetadataCompat getMetadata(); 1159 1160 List<MediaSessionCompat.QueueItem> getQueue(); 1161 void addQueueItem(MediaDescriptionCompat description); 1162 void addQueueItem(MediaDescriptionCompat description, int index); 1163 void removeQueueItem(MediaDescriptionCompat description); 1164 void removeQueueItemAt(int index); 1165 CharSequence getQueueTitle(); 1166 Bundle getExtras(); 1167 int getRatingType(); 1168 boolean isCaptioningEnabled(); 1169 int getRepeatMode(); 1170 boolean isShuffleModeEnabled(); 1171 long getFlags(); 1172 PlaybackInfo getPlaybackInfo(); 1173 PendingIntent getSessionActivity(); 1174 1175 void setVolumeTo(int value, int flags); 1176 void adjustVolume(int direction, int flags); 1177 void sendCommand(String command, Bundle params, ResultReceiver cb); 1178 1179 String getPackageName(); 1180 Object getMediaController(); 1181 } 1182 1183 static class MediaControllerImplBase implements MediaControllerImpl { 1184 private IMediaSession mBinder; 1185 private TransportControls mTransportControls; 1186 1187 public MediaControllerImplBase(MediaSessionCompat.Token token) { 1188 mBinder = IMediaSession.Stub.asInterface((IBinder) token.getToken()); 1189 } 1190 1191 @Override 1192 public void registerCallback(Callback callback, Handler handler) { 1193 if (callback == null) { 1194 throw new IllegalArgumentException("callback may not be null."); 1195 } 1196 try { 1197 mBinder.asBinder().linkToDeath(callback, 0); 1198 mBinder.registerCallbackListener((IMediaControllerCallback) callback.mCallbackObj); 1199 callback.setHandler(handler); 1200 callback.mRegistered = true; 1201 } catch (RemoteException e) { 1202 Log.e(TAG, "Dead object in registerCallback.", e); 1203 callback.onSessionDestroyed(); 1204 } 1205 } 1206 1207 @Override 1208 public void unregisterCallback(Callback callback) { 1209 if (callback == null) { 1210 throw new IllegalArgumentException("callback may not be null."); 1211 } 1212 try { 1213 mBinder.unregisterCallbackListener( 1214 (IMediaControllerCallback) callback.mCallbackObj); 1215 mBinder.asBinder().unlinkToDeath(callback, 0); 1216 callback.mRegistered = false; 1217 } catch (RemoteException e) { 1218 Log.e(TAG, "Dead object in unregisterCallback.", e); 1219 } 1220 } 1221 1222 @Override 1223 public boolean dispatchMediaButtonEvent(KeyEvent event) { 1224 if (event == null) { 1225 throw new IllegalArgumentException("event may not be null."); 1226 } 1227 try { 1228 mBinder.sendMediaButton(event); 1229 } catch (RemoteException e) { 1230 Log.e(TAG, "Dead object in dispatchMediaButtonEvent.", e); 1231 } 1232 return false; 1233 } 1234 1235 @Override 1236 public TransportControls getTransportControls() { 1237 if (mTransportControls == null) { 1238 mTransportControls = new TransportControlsBase(mBinder); 1239 } 1240 1241 return mTransportControls; 1242 } 1243 1244 @Override 1245 public PlaybackStateCompat getPlaybackState() { 1246 try { 1247 return mBinder.getPlaybackState(); 1248 } catch (RemoteException e) { 1249 Log.e(TAG, "Dead object in getPlaybackState.", e); 1250 } 1251 return null; 1252 } 1253 1254 @Override 1255 public MediaMetadataCompat getMetadata() { 1256 try { 1257 return mBinder.getMetadata(); 1258 } catch (RemoteException e) { 1259 Log.e(TAG, "Dead object in getMetadata.", e); 1260 } 1261 return null; 1262 } 1263 1264 @Override 1265 public List<MediaSessionCompat.QueueItem> getQueue() { 1266 try { 1267 return mBinder.getQueue(); 1268 } catch (RemoteException e) { 1269 Log.e(TAG, "Dead object in getQueue.", e); 1270 } 1271 return null; 1272 } 1273 1274 @Override 1275 public void addQueueItem(MediaDescriptionCompat description) { 1276 try { 1277 long flags = mBinder.getFlags(); 1278 if ((flags & MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS) == 0) { 1279 throw new UnsupportedOperationException( 1280 "This session doesn't support queue management operations"); 1281 } 1282 mBinder.addQueueItem(description); 1283 } catch (RemoteException e) { 1284 Log.e(TAG, "Dead object in addQueueItem.", e); 1285 } 1286 } 1287 1288 @Override 1289 public void addQueueItem(MediaDescriptionCompat description, int index) { 1290 try { 1291 long flags = mBinder.getFlags(); 1292 if ((flags & MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS) == 0) { 1293 throw new UnsupportedOperationException( 1294 "This session doesn't support queue management operations"); 1295 } 1296 mBinder.addQueueItemAt(description, index); 1297 } catch (RemoteException e) { 1298 Log.e(TAG, "Dead object in addQueueItemAt.", e); 1299 } 1300 } 1301 1302 @Override 1303 public void removeQueueItem(MediaDescriptionCompat description) { 1304 try { 1305 long flags = mBinder.getFlags(); 1306 if ((flags & MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS) == 0) { 1307 throw new UnsupportedOperationException( 1308 "This session doesn't support queue management operations"); 1309 } 1310 mBinder.removeQueueItem(description); 1311 } catch (RemoteException e) { 1312 Log.e(TAG, "Dead object in removeQueueItem.", e); 1313 } 1314 } 1315 1316 @Override 1317 public void removeQueueItemAt(int index) { 1318 try { 1319 long flags = mBinder.getFlags(); 1320 if ((flags & MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS) == 0) { 1321 throw new UnsupportedOperationException( 1322 "This session doesn't support queue management operations"); 1323 } 1324 mBinder.removeQueueItemAt(index); 1325 } catch (RemoteException e) { 1326 Log.e(TAG, "Dead object in removeQueueItemAt.", e); 1327 } 1328 } 1329 1330 @Override 1331 public CharSequence getQueueTitle() { 1332 try { 1333 return mBinder.getQueueTitle(); 1334 } catch (RemoteException e) { 1335 Log.e(TAG, "Dead object in getQueueTitle.", e); 1336 } 1337 return null; 1338 } 1339 1340 @Override 1341 public Bundle getExtras() { 1342 try { 1343 return mBinder.getExtras(); 1344 } catch (RemoteException e) { 1345 Log.e(TAG, "Dead object in getExtras.", e); 1346 } 1347 return null; 1348 } 1349 1350 @Override 1351 public int getRatingType() { 1352 try { 1353 return mBinder.getRatingType(); 1354 } catch (RemoteException e) { 1355 Log.e(TAG, "Dead object in getRatingType.", e); 1356 } 1357 return 0; 1358 } 1359 1360 @Override 1361 public boolean isCaptioningEnabled() { 1362 try { 1363 return mBinder.isCaptioningEnabled(); 1364 } catch (RemoteException e) { 1365 Log.e(TAG, "Dead object in isCaptioningEnabled.", e); 1366 } 1367 return false; 1368 } 1369 1370 @Override 1371 public int getRepeatMode() { 1372 try { 1373 return mBinder.getRepeatMode(); 1374 } catch (RemoteException e) { 1375 Log.e(TAG, "Dead object in getRepeatMode.", e); 1376 } 1377 return 0; 1378 } 1379 1380 @Override 1381 public boolean isShuffleModeEnabled() { 1382 try { 1383 return mBinder.isShuffleModeEnabled(); 1384 } catch (RemoteException e) { 1385 Log.e(TAG, "Dead object in isShuffleModeEnabled.", e); 1386 } 1387 return false; 1388 } 1389 1390 @Override 1391 public long getFlags() { 1392 try { 1393 return mBinder.getFlags(); 1394 } catch (RemoteException e) { 1395 Log.e(TAG, "Dead object in getFlags.", e); 1396 } 1397 return 0; 1398 } 1399 1400 @Override 1401 public PlaybackInfo getPlaybackInfo() { 1402 try { 1403 ParcelableVolumeInfo info = mBinder.getVolumeAttributes(); 1404 PlaybackInfo pi = new PlaybackInfo(info.volumeType, info.audioStream, 1405 info.controlType, info.maxVolume, info.currentVolume); 1406 return pi; 1407 } catch (RemoteException e) { 1408 Log.e(TAG, "Dead object in getPlaybackInfo.", e); 1409 } 1410 return null; 1411 } 1412 1413 @Override 1414 public PendingIntent getSessionActivity() { 1415 try { 1416 return mBinder.getLaunchPendingIntent(); 1417 } catch (RemoteException e) { 1418 Log.e(TAG, "Dead object in getSessionActivity.", e); 1419 } 1420 return null; 1421 } 1422 1423 @Override 1424 public void setVolumeTo(int value, int flags) { 1425 try { 1426 mBinder.setVolumeTo(value, flags, null); 1427 } catch (RemoteException e) { 1428 Log.e(TAG, "Dead object in setVolumeTo.", e); 1429 } 1430 } 1431 1432 @Override 1433 public void adjustVolume(int direction, int flags) { 1434 try { 1435 mBinder.adjustVolume(direction, flags, null); 1436 } catch (RemoteException e) { 1437 Log.e(TAG, "Dead object in adjustVolume.", e); 1438 } 1439 } 1440 1441 @Override 1442 public void sendCommand(String command, Bundle params, ResultReceiver cb) { 1443 try { 1444 mBinder.sendCommand(command, params, 1445 new MediaSessionCompat.ResultReceiverWrapper(cb)); 1446 } catch (RemoteException e) { 1447 Log.e(TAG, "Dead object in sendCommand.", e); 1448 } 1449 } 1450 1451 @Override 1452 public String getPackageName() { 1453 try { 1454 return mBinder.getPackageName(); 1455 } catch (RemoteException e) { 1456 Log.e(TAG, "Dead object in getPackageName.", e); 1457 } 1458 return null; 1459 } 1460 1461 @Override 1462 public Object getMediaController() { 1463 return null; 1464 } 1465 } 1466 1467 static class TransportControlsBase extends TransportControls { 1468 private IMediaSession mBinder; 1469 1470 public TransportControlsBase(IMediaSession binder) { 1471 mBinder = binder; 1472 } 1473 1474 @Override 1475 public void prepare() { 1476 try { 1477 mBinder.prepare(); 1478 } catch (RemoteException e) { 1479 Log.e(TAG, "Dead object in prepare.", e); 1480 } 1481 } 1482 1483 @Override 1484 public void prepareFromMediaId(String mediaId, Bundle extras) { 1485 try { 1486 mBinder.prepareFromMediaId(mediaId, extras); 1487 } catch (RemoteException e) { 1488 Log.e(TAG, "Dead object in prepareFromMediaId.", e); 1489 } 1490 } 1491 1492 @Override 1493 public void prepareFromSearch(String query, Bundle extras) { 1494 try { 1495 mBinder.prepareFromSearch(query, extras); 1496 } catch (RemoteException e) { 1497 Log.e(TAG, "Dead object in prepareFromSearch.", e); 1498 } 1499 } 1500 1501 @Override 1502 public void prepareFromUri(Uri uri, Bundle extras) { 1503 try { 1504 mBinder.prepareFromUri(uri, extras); 1505 } catch (RemoteException e) { 1506 Log.e(TAG, "Dead object in prepareFromUri.", e); 1507 } 1508 } 1509 1510 @Override 1511 public void play() { 1512 try { 1513 mBinder.play(); 1514 } catch (RemoteException e) { 1515 Log.e(TAG, "Dead object in play.", e); 1516 } 1517 } 1518 1519 @Override 1520 public void playFromMediaId(String mediaId, Bundle extras) { 1521 try { 1522 mBinder.playFromMediaId(mediaId, extras); 1523 } catch (RemoteException e) { 1524 Log.e(TAG, "Dead object in playFromMediaId.", e); 1525 } 1526 } 1527 1528 @Override 1529 public void playFromSearch(String query, Bundle extras) { 1530 try { 1531 mBinder.playFromSearch(query, extras); 1532 } catch (RemoteException e) { 1533 Log.e(TAG, "Dead object in playFromSearch.", e); 1534 } 1535 } 1536 1537 @Override 1538 public void playFromUri(Uri uri, Bundle extras) { 1539 try { 1540 mBinder.playFromUri(uri, extras); 1541 } catch (RemoteException e) { 1542 Log.e(TAG, "Dead object in playFromUri.", e); 1543 } 1544 } 1545 1546 @Override 1547 public void skipToQueueItem(long id) { 1548 try { 1549 mBinder.skipToQueueItem(id); 1550 } catch (RemoteException e) { 1551 Log.e(TAG, "Dead object in skipToQueueItem.", e); 1552 } 1553 } 1554 1555 @Override 1556 public void pause() { 1557 try { 1558 mBinder.pause(); 1559 } catch (RemoteException e) { 1560 Log.e(TAG, "Dead object in pause.", e); 1561 } 1562 } 1563 1564 @Override 1565 public void stop() { 1566 try { 1567 mBinder.stop(); 1568 } catch (RemoteException e) { 1569 Log.e(TAG, "Dead object in stop.", e); 1570 } 1571 } 1572 1573 @Override 1574 public void seekTo(long pos) { 1575 try { 1576 mBinder.seekTo(pos); 1577 } catch (RemoteException e) { 1578 Log.e(TAG, "Dead object in seekTo.", e); 1579 } 1580 } 1581 1582 @Override 1583 public void fastForward() { 1584 try { 1585 mBinder.fastForward(); 1586 } catch (RemoteException e) { 1587 Log.e(TAG, "Dead object in fastForward.", e); 1588 } 1589 } 1590 1591 @Override 1592 public void skipToNext() { 1593 try { 1594 mBinder.next(); 1595 } catch (RemoteException e) { 1596 Log.e(TAG, "Dead object in skipToNext.", e); 1597 } 1598 } 1599 1600 @Override 1601 public void rewind() { 1602 try { 1603 mBinder.rewind(); 1604 } catch (RemoteException e) { 1605 Log.e(TAG, "Dead object in rewind.", e); 1606 } 1607 } 1608 1609 @Override 1610 public void skipToPrevious() { 1611 try { 1612 mBinder.previous(); 1613 } catch (RemoteException e) { 1614 Log.e(TAG, "Dead object in skipToPrevious.", e); 1615 } 1616 } 1617 1618 @Override 1619 public void setRating(RatingCompat rating) { 1620 try { 1621 mBinder.rate(rating); 1622 } catch (RemoteException e) { 1623 Log.e(TAG, "Dead object in setRating.", e); 1624 } 1625 } 1626 1627 @Override 1628 public void setCaptioningEnabled(boolean enabled) { 1629 try { 1630 mBinder.setCaptioningEnabled(enabled); 1631 } catch (RemoteException e) { 1632 Log.e(TAG, "Dead object in setCaptioningEnabled.", e); 1633 } 1634 } 1635 1636 @Override 1637 public void setRepeatMode(@PlaybackStateCompat.RepeatMode int repeatMode) { 1638 try { 1639 mBinder.setRepeatMode(repeatMode); 1640 } catch (RemoteException e) { 1641 Log.e(TAG, "Dead object in setRepeatMode.", e); 1642 } 1643 } 1644 1645 @Override 1646 public void setShuffleModeEnabled(boolean enabled) { 1647 try { 1648 mBinder.setShuffleModeEnabled(enabled); 1649 } catch (RemoteException e) { 1650 Log.e(TAG, "Dead object in setShuffleModeEnabled.", e); 1651 } 1652 } 1653 1654 @Override 1655 public void sendCustomAction(CustomAction customAction, Bundle args) { 1656 sendCustomAction(customAction.getAction(), args); 1657 } 1658 1659 @Override 1660 public void sendCustomAction(String action, Bundle args) { 1661 try { 1662 mBinder.sendCustomAction(action, args); 1663 } catch (RemoteException e) { 1664 Log.e(TAG, "Dead object in sendCustomAction.", e); 1665 } 1666 } 1667 } 1668 1669 static class MediaControllerImplApi21 implements MediaControllerImpl { 1670 protected final Object mControllerObj; 1671 1672 // Extra binder is used for applying the framework change of new APIs and bug fixes 1673 // after API 21. 1674 private IMediaSession mExtraBinder; 1675 private HashMap<Callback, ExtraCallback> mCallbackMap = new HashMap<>(); 1676 private List<Callback> mPendingCallbacks = new ArrayList<>(); 1677 1678 public MediaControllerImplApi21(Context context, MediaSessionCompat session) { 1679 mControllerObj = MediaControllerCompatApi21.fromToken(context, 1680 session.getSessionToken().getToken()); 1681 mExtraBinder = session.getSessionToken().getExtraBinder(); 1682 if (mExtraBinder == null) { 1683 requestExtraBinder(); 1684 } 1685 } 1686 1687 public MediaControllerImplApi21(Context context, MediaSessionCompat.Token sessionToken) 1688 throws RemoteException { 1689 mControllerObj = MediaControllerCompatApi21.fromToken(context, 1690 sessionToken.getToken()); 1691 if (mControllerObj == null) throw new RemoteException(); 1692 mExtraBinder = sessionToken.getExtraBinder(); 1693 if (mExtraBinder == null) { 1694 requestExtraBinder(); 1695 } 1696 } 1697 1698 @Override 1699 public final void registerCallback(Callback callback, Handler handler) { 1700 MediaControllerCompatApi21.registerCallback( 1701 mControllerObj, callback.mCallbackObj, handler); 1702 if (mExtraBinder != null) { 1703 callback.setHandler(handler); 1704 ExtraCallback extraCallback = new ExtraCallback(callback); 1705 mCallbackMap.put(callback, extraCallback); 1706 callback.mHasExtraCallback = true; 1707 try { 1708 mExtraBinder.registerCallbackListener(extraCallback); 1709 } catch (RemoteException e) { 1710 Log.e(TAG, "Dead object in registerCallback.", e); 1711 } 1712 } else { 1713 callback.setHandler(handler); 1714 synchronized (mPendingCallbacks) { 1715 mPendingCallbacks.add(callback); 1716 } 1717 } 1718 } 1719 1720 @Override 1721 public final void unregisterCallback(Callback callback) { 1722 MediaControllerCompatApi21.unregisterCallback(mControllerObj, callback.mCallbackObj); 1723 if (mExtraBinder != null) { 1724 try { 1725 ExtraCallback extraCallback = mCallbackMap.remove(callback); 1726 if (extraCallback != null) { 1727 mExtraBinder.unregisterCallbackListener(extraCallback); 1728 } 1729 } catch (RemoteException e) { 1730 Log.e(TAG, "Dead object in unregisterCallback.", e); 1731 } 1732 } else { 1733 synchronized (mPendingCallbacks) { 1734 mPendingCallbacks.remove(callback); 1735 } 1736 } 1737 } 1738 1739 @Override 1740 public boolean dispatchMediaButtonEvent(KeyEvent event) { 1741 return MediaControllerCompatApi21.dispatchMediaButtonEvent(mControllerObj, event); 1742 } 1743 1744 @Override 1745 public TransportControls getTransportControls() { 1746 Object controlsObj = MediaControllerCompatApi21.getTransportControls(mControllerObj); 1747 return controlsObj != null ? new TransportControlsApi21(controlsObj) : null; 1748 } 1749 1750 @Override 1751 public PlaybackStateCompat getPlaybackState() { 1752 if (mExtraBinder != null) { 1753 try { 1754 return mExtraBinder.getPlaybackState(); 1755 } catch (RemoteException e) { 1756 Log.e(TAG, "Dead object in getPlaybackState.", e); 1757 } 1758 } 1759 Object stateObj = MediaControllerCompatApi21.getPlaybackState(mControllerObj); 1760 return stateObj != null ? PlaybackStateCompat.fromPlaybackState(stateObj) : null; 1761 } 1762 1763 @Override 1764 public MediaMetadataCompat getMetadata() { 1765 Object metadataObj = MediaControllerCompatApi21.getMetadata(mControllerObj); 1766 return metadataObj != null ? MediaMetadataCompat.fromMediaMetadata(metadataObj) : null; 1767 } 1768 1769 @Override 1770 public List<MediaSessionCompat.QueueItem> getQueue() { 1771 List<Object> queueObjs = MediaControllerCompatApi21.getQueue(mControllerObj); 1772 return queueObjs != null ? MediaSessionCompat.QueueItem.fromQueueItemList(queueObjs) 1773 : null; 1774 } 1775 1776 @Override 1777 public void addQueueItem(MediaDescriptionCompat description) { 1778 long flags = getFlags(); 1779 if ((flags & MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS) == 0) { 1780 throw new UnsupportedOperationException( 1781 "This session doesn't support queue management operations"); 1782 } 1783 Bundle params = new Bundle(); 1784 params.putParcelable(COMMAND_ARGUMENT_MEDIA_DESCRIPTION, description); 1785 sendCommand(COMMAND_ADD_QUEUE_ITEM, params, null); 1786 } 1787 1788 @Override 1789 public void addQueueItem(MediaDescriptionCompat description, int index) { 1790 long flags = getFlags(); 1791 if ((flags & MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS) == 0) { 1792 throw new UnsupportedOperationException( 1793 "This session doesn't support queue management operations"); 1794 } 1795 Bundle params = new Bundle(); 1796 params.putParcelable(COMMAND_ARGUMENT_MEDIA_DESCRIPTION, description); 1797 params.putInt(COMMAND_ARGUMENT_INDEX, index); 1798 sendCommand(COMMAND_ADD_QUEUE_ITEM_AT, params, null); 1799 } 1800 1801 @Override 1802 public void removeQueueItem(MediaDescriptionCompat description) { 1803 long flags = getFlags(); 1804 if ((flags & MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS) == 0) { 1805 throw new UnsupportedOperationException( 1806 "This session doesn't support queue management operations"); 1807 } 1808 Bundle params = new Bundle(); 1809 params.putParcelable(COMMAND_ARGUMENT_MEDIA_DESCRIPTION, description); 1810 sendCommand(COMMAND_REMOVE_QUEUE_ITEM, params, null); 1811 } 1812 1813 @Override 1814 public void removeQueueItemAt(int index) { 1815 long flags = getFlags(); 1816 if ((flags & MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS) == 0) { 1817 throw new UnsupportedOperationException( 1818 "This session doesn't support queue management operations"); 1819 } 1820 Bundle params = new Bundle(); 1821 params.putInt(COMMAND_ARGUMENT_INDEX, index); 1822 sendCommand(COMMAND_REMOVE_QUEUE_ITEM_AT, params, null); 1823 } 1824 1825 @Override 1826 public CharSequence getQueueTitle() { 1827 return MediaControllerCompatApi21.getQueueTitle(mControllerObj); 1828 } 1829 1830 @Override 1831 public Bundle getExtras() { 1832 return MediaControllerCompatApi21.getExtras(mControllerObj); 1833 } 1834 1835 @Override 1836 public int getRatingType() { 1837 if (android.os.Build.VERSION.SDK_INT < 22 && mExtraBinder != null) { 1838 try { 1839 return mExtraBinder.getRatingType(); 1840 } catch (RemoteException e) { 1841 Log.e(TAG, "Dead object in getRatingType.", e); 1842 } 1843 } 1844 return MediaControllerCompatApi21.getRatingType(mControllerObj); 1845 } 1846 1847 @Override 1848 public boolean isCaptioningEnabled() { 1849 if (mExtraBinder != null) { 1850 try { 1851 return mExtraBinder.isCaptioningEnabled(); 1852 } catch (RemoteException e) { 1853 Log.e(TAG, "Dead object in isCaptioningEnabled.", e); 1854 } 1855 } 1856 return false; 1857 } 1858 1859 @Override 1860 public int getRepeatMode() { 1861 if (mExtraBinder != null) { 1862 try { 1863 return mExtraBinder.getRepeatMode(); 1864 } catch (RemoteException e) { 1865 Log.e(TAG, "Dead object in getRepeatMode.", e); 1866 } 1867 } 1868 return PlaybackStateCompat.REPEAT_MODE_NONE; 1869 } 1870 1871 @Override 1872 public boolean isShuffleModeEnabled() { 1873 if (mExtraBinder != null) { 1874 try { 1875 return mExtraBinder.isShuffleModeEnabled(); 1876 } catch (RemoteException e) { 1877 Log.e(TAG, "Dead object in isShuffleModeEnabled.", e); 1878 } 1879 } 1880 return false; 1881 } 1882 1883 @Override 1884 public long getFlags() { 1885 return MediaControllerCompatApi21.getFlags(mControllerObj); 1886 } 1887 1888 @Override 1889 public PlaybackInfo getPlaybackInfo() { 1890 Object volumeInfoObj = MediaControllerCompatApi21.getPlaybackInfo(mControllerObj); 1891 return volumeInfoObj != null ? new PlaybackInfo( 1892 MediaControllerCompatApi21.PlaybackInfo.getPlaybackType(volumeInfoObj), 1893 MediaControllerCompatApi21.PlaybackInfo.getLegacyAudioStream(volumeInfoObj), 1894 MediaControllerCompatApi21.PlaybackInfo.getVolumeControl(volumeInfoObj), 1895 MediaControllerCompatApi21.PlaybackInfo.getMaxVolume(volumeInfoObj), 1896 MediaControllerCompatApi21.PlaybackInfo.getCurrentVolume(volumeInfoObj)) : null; 1897 } 1898 1899 @Override 1900 public PendingIntent getSessionActivity() { 1901 return MediaControllerCompatApi21.getSessionActivity(mControllerObj); 1902 } 1903 1904 @Override 1905 public void setVolumeTo(int value, int flags) { 1906 MediaControllerCompatApi21.setVolumeTo(mControllerObj, value, flags); 1907 } 1908 1909 @Override 1910 public void adjustVolume(int direction, int flags) { 1911 MediaControllerCompatApi21.adjustVolume(mControllerObj, direction, flags); 1912 } 1913 1914 @Override 1915 public void sendCommand(String command, Bundle params, ResultReceiver cb) { 1916 MediaControllerCompatApi21.sendCommand(mControllerObj, command, params, cb); 1917 } 1918 1919 @Override 1920 public String getPackageName() { 1921 return MediaControllerCompatApi21.getPackageName(mControllerObj); 1922 } 1923 1924 @Override 1925 public Object getMediaController() { 1926 return mControllerObj; 1927 } 1928 1929 private void requestExtraBinder() { 1930 sendCommand(COMMAND_GET_EXTRA_BINDER, null, 1931 new ExtraBinderRequestResultReceiver(this, new Handler())); 1932 } 1933 1934 private void processPendingCallbacks() { 1935 if (mExtraBinder == null) { 1936 return; 1937 } 1938 synchronized (mPendingCallbacks) { 1939 for (Callback callback : mPendingCallbacks) { 1940 ExtraCallback extraCallback = new ExtraCallback(callback); 1941 mCallbackMap.put(callback, extraCallback); 1942 callback.mHasExtraCallback = true; 1943 try { 1944 mExtraBinder.registerCallbackListener(extraCallback); 1945 } catch (RemoteException e) { 1946 Log.e(TAG, "Dead object in registerCallback.", e); 1947 break; 1948 } 1949 } 1950 mPendingCallbacks.clear(); 1951 } 1952 } 1953 1954 private static class ExtraBinderRequestResultReceiver extends ResultReceiver { 1955 private WeakReference<MediaControllerImplApi21> mMediaControllerImpl; 1956 1957 public ExtraBinderRequestResultReceiver(MediaControllerImplApi21 mediaControllerImpl, 1958 Handler handler) { 1959 super(handler); 1960 mMediaControllerImpl = new WeakReference<>(mediaControllerImpl); 1961 } 1962 1963 @Override 1964 protected void onReceiveResult(int resultCode, Bundle resultData) { 1965 MediaControllerImplApi21 mediaControllerImpl = mMediaControllerImpl.get(); 1966 if (mediaControllerImpl == null || resultData == null) { 1967 return; 1968 } 1969 mediaControllerImpl.mExtraBinder = IMediaSession.Stub.asInterface( 1970 BundleCompat.getBinder(resultData, MediaSessionCompat.EXTRA_BINDER)); 1971 mediaControllerImpl.processPendingCallbacks(); 1972 } 1973 } 1974 1975 private class ExtraCallback extends IMediaControllerCallback.Stub { 1976 private Callback mCallback; 1977 1978 ExtraCallback(Callback callback) { 1979 mCallback = callback; 1980 } 1981 1982 @Override 1983 public void onEvent(final String event, final Bundle extras) throws RemoteException { 1984 mCallback.mHandler.post(new Runnable() { 1985 @Override 1986 public void run() { 1987 mCallback.onSessionEvent(event, extras); 1988 } 1989 }); 1990 } 1991 1992 @Override 1993 public void onSessionDestroyed() throws RemoteException { 1994 // Will not be called. 1995 throw new AssertionError(); 1996 } 1997 1998 @Override 1999 public void onPlaybackStateChanged(final PlaybackStateCompat state) 2000 throws RemoteException { 2001 mCallback.mHandler.post(new Runnable() { 2002 @Override 2003 public void run() { 2004 mCallback.onPlaybackStateChanged(state); 2005 } 2006 }); 2007 } 2008 2009 @Override 2010 public void onMetadataChanged(MediaMetadataCompat metadata) throws RemoteException { 2011 // Will not be called. 2012 throw new AssertionError(); 2013 } 2014 2015 @Override 2016 public void onQueueChanged(List<QueueItem> queue) throws RemoteException { 2017 // Will not be called. 2018 throw new AssertionError(); 2019 } 2020 2021 @Override 2022 public void onQueueTitleChanged(CharSequence title) throws RemoteException { 2023 // Will not be called. 2024 throw new AssertionError(); 2025 } 2026 2027 @Override 2028 public void onCaptioningEnabledChanged(final boolean enabled) throws RemoteException { 2029 mCallback.mHandler.post(new Runnable() { 2030 @Override 2031 public void run() { 2032 mCallback.onCaptioningEnabledChanged(enabled); 2033 } 2034 }); 2035 } 2036 2037 @Override 2038 public void onRepeatModeChanged(final int repeatMode) throws RemoteException { 2039 mCallback.mHandler.post(new Runnable() { 2040 @Override 2041 public void run() { 2042 mCallback.onRepeatModeChanged(repeatMode); 2043 } 2044 }); 2045 } 2046 2047 @Override 2048 public void onShuffleModeChanged(final boolean enabled) throws RemoteException { 2049 mCallback.mHandler.post(new Runnable() { 2050 @Override 2051 public void run() { 2052 mCallback.onShuffleModeChanged(enabled); 2053 } 2054 }); 2055 } 2056 2057 @Override 2058 public void onExtrasChanged(Bundle extras) throws RemoteException { 2059 // Will not be called. 2060 throw new AssertionError(); 2061 } 2062 2063 @Override 2064 public void onVolumeInfoChanged(ParcelableVolumeInfo info) throws RemoteException { 2065 // Will not be called. 2066 throw new AssertionError(); 2067 } 2068 } 2069 } 2070 2071 static class TransportControlsApi21 extends TransportControls { 2072 protected final Object mControlsObj; 2073 2074 public TransportControlsApi21(Object controlsObj) { 2075 mControlsObj = controlsObj; 2076 } 2077 2078 @Override 2079 public void prepare() { 2080 sendCustomAction(MediaSessionCompat.ACTION_PREPARE, null); 2081 } 2082 2083 @Override 2084 public void prepareFromMediaId(String mediaId, Bundle extras) { 2085 Bundle bundle = new Bundle(); 2086 bundle.putString(MediaSessionCompat.ACTION_ARGUMENT_MEDIA_ID, mediaId); 2087 bundle.putBundle(MediaSessionCompat.ACTION_ARGUMENT_EXTRAS, extras); 2088 sendCustomAction(MediaSessionCompat.ACTION_PREPARE_FROM_MEDIA_ID, bundle); 2089 } 2090 2091 @Override 2092 public void prepareFromSearch(String query, Bundle extras) { 2093 Bundle bundle = new Bundle(); 2094 bundle.putString(MediaSessionCompat.ACTION_ARGUMENT_QUERY, query); 2095 bundle.putBundle(MediaSessionCompat.ACTION_ARGUMENT_EXTRAS, extras); 2096 sendCustomAction(MediaSessionCompat.ACTION_PREPARE_FROM_SEARCH, bundle); 2097 } 2098 2099 @Override 2100 public void prepareFromUri(Uri uri, Bundle extras) { 2101 Bundle bundle = new Bundle(); 2102 bundle.putParcelable(MediaSessionCompat.ACTION_ARGUMENT_URI, uri); 2103 bundle.putBundle(MediaSessionCompat.ACTION_ARGUMENT_EXTRAS, extras); 2104 sendCustomAction(MediaSessionCompat.ACTION_PREPARE_FROM_URI, bundle); 2105 } 2106 2107 @Override 2108 public void play() { 2109 MediaControllerCompatApi21.TransportControls.play(mControlsObj); 2110 } 2111 2112 @Override 2113 public void pause() { 2114 MediaControllerCompatApi21.TransportControls.pause(mControlsObj); 2115 } 2116 2117 @Override 2118 public void stop() { 2119 MediaControllerCompatApi21.TransportControls.stop(mControlsObj); 2120 } 2121 2122 @Override 2123 public void seekTo(long pos) { 2124 MediaControllerCompatApi21.TransportControls.seekTo(mControlsObj, pos); 2125 } 2126 2127 @Override 2128 public void fastForward() { 2129 MediaControllerCompatApi21.TransportControls.fastForward(mControlsObj); 2130 } 2131 2132 @Override 2133 public void rewind() { 2134 MediaControllerCompatApi21.TransportControls.rewind(mControlsObj); 2135 } 2136 2137 @Override 2138 public void skipToNext() { 2139 MediaControllerCompatApi21.TransportControls.skipToNext(mControlsObj); 2140 } 2141 2142 @Override 2143 public void skipToPrevious() { 2144 MediaControllerCompatApi21.TransportControls.skipToPrevious(mControlsObj); 2145 } 2146 2147 @Override 2148 public void setRating(RatingCompat rating) { 2149 MediaControllerCompatApi21.TransportControls.setRating(mControlsObj, 2150 rating != null ? rating.getRating() : null); 2151 } 2152 2153 @Override 2154 public void setCaptioningEnabled(boolean enabled) { 2155 Bundle bundle = new Bundle(); 2156 bundle.putBoolean(MediaSessionCompat.ACTION_ARGUMENT_CAPTIONING_ENABLED, enabled); 2157 sendCustomAction(MediaSessionCompat.ACTION_SET_CAPTIONING_ENABLED, bundle); 2158 } 2159 2160 @Override 2161 public void setRepeatMode(@PlaybackStateCompat.RepeatMode int repeatMode) { 2162 Bundle bundle = new Bundle(); 2163 bundle.putInt(MediaSessionCompat.ACTION_ARGUMENT_REPEAT_MODE, repeatMode); 2164 sendCustomAction(MediaSessionCompat.ACTION_SET_REPEAT_MODE, bundle); 2165 } 2166 2167 @Override 2168 public void setShuffleModeEnabled(boolean enabled) { 2169 Bundle bundle = new Bundle(); 2170 bundle.putBoolean(MediaSessionCompat.ACTION_ARGUMENT_SHUFFLE_MODE_ENABLED, enabled); 2171 sendCustomAction(MediaSessionCompat.ACTION_SET_SHUFFLE_MODE_ENABLED, bundle); 2172 } 2173 2174 @Override 2175 public void playFromMediaId(String mediaId, Bundle extras) { 2176 MediaControllerCompatApi21.TransportControls.playFromMediaId(mControlsObj, mediaId, 2177 extras); 2178 } 2179 2180 @Override 2181 public void playFromSearch(String query, Bundle extras) { 2182 MediaControllerCompatApi21.TransportControls.playFromSearch(mControlsObj, query, 2183 extras); 2184 } 2185 2186 @Override 2187 public void playFromUri(Uri uri, Bundle extras) { 2188 if (uri == null || Uri.EMPTY.equals(uri)) { 2189 throw new IllegalArgumentException( 2190 "You must specify a non-empty Uri for playFromUri."); 2191 } 2192 Bundle bundle = new Bundle(); 2193 bundle.putParcelable(MediaSessionCompat.ACTION_ARGUMENT_URI, uri); 2194 bundle.putParcelable(MediaSessionCompat.ACTION_ARGUMENT_EXTRAS, extras); 2195 sendCustomAction(MediaSessionCompat.ACTION_PLAY_FROM_URI, bundle); 2196 } 2197 2198 @Override 2199 public void skipToQueueItem(long id) { 2200 MediaControllerCompatApi21.TransportControls.skipToQueueItem(mControlsObj, id); 2201 } 2202 2203 @Override 2204 public void sendCustomAction(CustomAction customAction, Bundle args) { 2205 MediaControllerCompatApi21.TransportControls.sendCustomAction(mControlsObj, 2206 customAction.getAction(), args); 2207 } 2208 2209 @Override 2210 public void sendCustomAction(String action, Bundle args) { 2211 MediaControllerCompatApi21.TransportControls.sendCustomAction(mControlsObj, action, 2212 args); 2213 } 2214 } 2215 2216 static class MediaControllerImplApi23 extends MediaControllerImplApi21 { 2217 2218 public MediaControllerImplApi23(Context context, MediaSessionCompat session) { 2219 super(context, session); 2220 } 2221 2222 public MediaControllerImplApi23(Context context, MediaSessionCompat.Token sessionToken) 2223 throws RemoteException { 2224 super(context, sessionToken); 2225 } 2226 2227 @Override 2228 public TransportControls getTransportControls() { 2229 Object controlsObj = MediaControllerCompatApi21.getTransportControls(mControllerObj); 2230 return controlsObj != null ? new TransportControlsApi23(controlsObj) : null; 2231 } 2232 } 2233 2234 static class TransportControlsApi23 extends TransportControlsApi21 { 2235 2236 public TransportControlsApi23(Object controlsObj) { 2237 super(controlsObj); 2238 } 2239 2240 @Override 2241 public void playFromUri(Uri uri, Bundle extras) { 2242 MediaControllerCompatApi23.TransportControls.playFromUri(mControlsObj, uri, 2243 extras); 2244 } 2245 } 2246 2247 static class MediaControllerImplApi24 extends MediaControllerImplApi23 { 2248 2249 public MediaControllerImplApi24(Context context, MediaSessionCompat session) { 2250 super(context, session); 2251 } 2252 2253 public MediaControllerImplApi24(Context context, MediaSessionCompat.Token sessionToken) 2254 throws RemoteException { 2255 super(context, sessionToken); 2256 } 2257 2258 @Override 2259 public TransportControls getTransportControls() { 2260 Object controlsObj = MediaControllerCompatApi21.getTransportControls(mControllerObj); 2261 return controlsObj != null ? new TransportControlsApi24(controlsObj) : null; 2262 } 2263 } 2264 2265 static class TransportControlsApi24 extends TransportControlsApi23 { 2266 2267 public TransportControlsApi24(Object controlsObj) { 2268 super(controlsObj); 2269 } 2270 2271 @Override 2272 public void prepare() { 2273 MediaControllerCompatApi24.TransportControls.prepare(mControlsObj); 2274 } 2275 2276 @Override 2277 public void prepareFromMediaId(String mediaId, Bundle extras) { 2278 MediaControllerCompatApi24.TransportControls.prepareFromMediaId( 2279 mControlsObj, mediaId, extras); 2280 } 2281 2282 @Override 2283 public void prepareFromSearch(String query, Bundle extras) { 2284 MediaControllerCompatApi24.TransportControls.prepareFromSearch( 2285 mControlsObj, query, extras); 2286 } 2287 2288 @Override 2289 public void prepareFromUri(Uri uri, Bundle extras) { 2290 MediaControllerCompatApi24.TransportControls.prepareFromUri(mControlsObj, uri, extras); 2291 } 2292 } 2293} 2294