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