MediaSessionCompat.java revision 2e2aff8306def4a486c7fe47dcd0c411aaf8699d
1 2/* 3 * Copyright (C) 2014 The Android Open Source Project 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18package android.support.v4.media.session; 19 20import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP; 21 22import android.annotation.SuppressLint; 23import android.app.Activity; 24import android.app.PendingIntent; 25import android.content.BroadcastReceiver; 26import android.content.ComponentName; 27import android.content.Context; 28import android.content.Intent; 29import android.graphics.Bitmap; 30import android.media.AudioManager; 31import android.media.MediaMetadataEditor; 32import android.media.MediaMetadataRetriever; 33import android.media.Rating; 34import android.media.RemoteControlClient; 35import android.net.Uri; 36import android.os.Build; 37import android.os.Bundle; 38import android.os.Handler; 39import android.os.IBinder; 40import android.os.Looper; 41import android.os.Message; 42import android.os.Parcel; 43import android.os.Parcelable; 44import android.os.RemoteCallbackList; 45import android.os.RemoteException; 46import android.os.ResultReceiver; 47import android.os.SystemClock; 48import android.support.annotation.IntDef; 49import android.support.annotation.RequiresApi; 50import android.support.annotation.RestrictTo; 51import android.support.v4.app.BundleCompat; 52import android.support.v4.media.MediaDescriptionCompat; 53import android.support.v4.media.MediaMetadataCompat; 54import android.support.v4.media.RatingCompat; 55import android.support.v4.media.VolumeProviderCompat; 56import android.support.v4.os.BuildCompat; 57import android.text.TextUtils; 58import android.util.Log; 59import android.util.TypedValue; 60import android.view.KeyEvent; 61 62import java.lang.annotation.Retention; 63import java.lang.annotation.RetentionPolicy; 64import java.lang.ref.WeakReference; 65import java.util.ArrayList; 66import java.util.List; 67 68/** 69 * Allows interaction with media controllers, volume keys, media buttons, and 70 * transport controls. 71 * <p> 72 * A MediaSession should be created when an app wants to publish media playback 73 * information or handle media keys. In general an app only needs one session 74 * for all playback, though multiple sessions can be created to provide finer 75 * grain controls of media. 76 * <p> 77 * Once a session is created the owner of the session may pass its 78 * {@link #getSessionToken() session token} to other processes to allow them to 79 * create a {@link MediaControllerCompat} to interact with the session. 80 * <p> 81 * To receive commands, media keys, and other events a {@link Callback} must be 82 * set with {@link #setCallback(Callback)}. 83 * <p> 84 * When an app is finished performing playback it must call {@link #release()} 85 * to clean up the session and notify any controllers. 86 * <p> 87 * MediaSessionCompat objects are not thread safe and all calls should be made 88 * from the same thread. 89 * <p> 90 * This is a helper for accessing features in 91 * {@link android.media.session.MediaSession} introduced after API level 4 in a 92 * backwards compatible fashion. 93 */ 94public class MediaSessionCompat { 95 static final String TAG = "MediaSessionCompat"; 96 97 private final MediaSessionImpl mImpl; 98 private final MediaControllerCompat mController; 99 private final ArrayList<OnActiveChangeListener> mActiveListeners = new ArrayList<>(); 100 101 /** 102 * @hide 103 */ 104 @RestrictTo(LIBRARY_GROUP) 105 @IntDef(flag=true, value={ 106 FLAG_HANDLES_MEDIA_BUTTONS, 107 FLAG_HANDLES_TRANSPORT_CONTROLS, 108 FLAG_HANDLES_QUEUE_COMMANDS }) 109 @Retention(RetentionPolicy.SOURCE) 110 public @interface SessionFlags {} 111 112 /** 113 * Set this flag on the session to indicate that it can handle media button 114 * events. 115 */ 116 public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1 << 0; 117 118 /** 119 * Set this flag on the session to indicate that it handles transport 120 * control commands through its {@link Callback}. 121 */ 122 public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 1 << 1; 123 124 /** 125 * Set this flag on the session to indicate that it handles queue 126 * management commands through its {@link Callback}. 127 */ 128 public static final int FLAG_HANDLES_QUEUE_COMMANDS = 1 << 2; 129 130 /** 131 * Predefined custom action to flag the media that is currently playing as inappropriate. 132 * 133 * @see Callback#onCustomAction 134 */ 135 public static final String ACTION_FLAG_AS_INAPPROPRIATE = 136 "android.support.v4.media.session.action.FLAG_AS_INAPPROPRIATE"; 137 138 /** 139 * Predefined custom action to skip the advertisement that is currently playing. 140 * 141 * @see Callback#onCustomAction 142 */ 143 public static final String ACTION_SKIP_AD = 144 "android.support.v4.media.session.action.SKIP_AD"; 145 146 /** 147 * Custom action to invoke playFromUri() for the forward compatibility. 148 */ 149 static final String ACTION_PLAY_FROM_URI = 150 "android.support.v4.media.session.action.PLAY_FROM_URI"; 151 152 /** 153 * Custom action to invoke prepare() for the forward compatibility. 154 */ 155 static final String ACTION_PREPARE = "android.support.v4.media.session.action.PREPARE"; 156 157 /** 158 * Custom action to invoke prepareFromMediaId() for the forward compatibility. 159 */ 160 static final String ACTION_PREPARE_FROM_MEDIA_ID = 161 "android.support.v4.media.session.action.PREPARE_FROM_MEDIA_ID"; 162 163 /** 164 * Custom action to invoke prepareFromSearch() for the forward compatibility. 165 */ 166 static final String ACTION_PREPARE_FROM_SEARCH = 167 "android.support.v4.media.session.action.PREPARE_FROM_SEARCH"; 168 169 /** 170 * Custom action to invoke prepareFromUri() for the forward compatibility. 171 */ 172 static final String ACTION_PREPARE_FROM_URI = 173 "android.support.v4.media.session.action.PREPARE_FROM_URI"; 174 175 /** 176 * Custom action to invoke setRepeatMode() for the forward compatibility. 177 */ 178 static final String ACTION_SET_REPEAT_MODE = 179 "android.support.v4.media.session.action.SET_REPEAT_MODE"; 180 181 /** 182 * Custom action to invoke setShuffleModeEnabled() for the forward compatibility. 183 */ 184 static final String ACTION_SET_SHUFFLE_MODE_ENABLED = 185 "android.support.v4.media.session.action.SET_SHUFFLE_MODE_ENABLED"; 186 187 /** 188 * Argument for use with {@link #ACTION_PREPARE_FROM_MEDIA_ID} indicating media id to play. 189 */ 190 static final String ACTION_ARGUMENT_MEDIA_ID = 191 "android.support.v4.media.session.action.ARGUMENT_MEDIA_ID"; 192 193 /** 194 * Argument for use with {@link #ACTION_PREPARE_FROM_SEARCH} indicating search query. 195 */ 196 static final String ACTION_ARGUMENT_QUERY = 197 "android.support.v4.media.session.action.ARGUMENT_QUERY"; 198 199 /** 200 * Argument for use with {@link #ACTION_PREPARE_FROM_URI} and {@link #ACTION_PLAY_FROM_URI} 201 * indicating URI to play. 202 */ 203 static final String ACTION_ARGUMENT_URI = 204 "android.support.v4.media.session.action.ARGUMENT_URI"; 205 206 /** 207 * Argument for use with various actions indicating extra bundle. 208 */ 209 static final String ACTION_ARGUMENT_EXTRAS = 210 "android.support.v4.media.session.action.ARGUMENT_EXTRAS"; 211 212 /** 213 * Argument for use with {@link #ACTION_SET_REPEAT_MODE} indicating repeat mode. 214 */ 215 static final String ACTION_ARGUMENT_REPEAT_MODE = 216 "android.support.v4.media.session.action.ARGUMENT_REPEAT_MODE"; 217 218 /** 219 * Argument for use with {@link #ACTION_SET_SHUFFLE_MODE_ENABLED} indicating that shuffle mode 220 * is enabled. 221 */ 222 static final String ACTION_ARGUMENT_SHUFFLE_MODE_ENABLED = 223 "android.support.v4.media.session.action.ARGUMENT_SHUFFLE_MODE_ENABLED"; 224 225 static final String EXTRA_BINDER = "android.support.v4.media.session.EXTRA_BINDER"; 226 227 // Maximum size of the bitmap in dp. 228 private static final int MAX_BITMAP_SIZE_IN_DP = 320; 229 230 // Maximum size of the bitmap in px. It shouldn't be changed. 231 static int sMaxBitmapSize; 232 233 /** 234 * Creates a new session. You must call {@link #release()} when finished with the session. 235 * <p> 236 * The session will automatically be registered with the system but will not be published 237 * until {@link #setActive(boolean) setActive(true)} is called. 238 * </p><p> 239 * For API 20 or earlier, note that a media button receiver is required for handling 240 * {@link Intent#ACTION_MEDIA_BUTTON}. This constructor will attempt to find an appropriate 241 * {@link BroadcastReceiver} from your manifest. See {@link MediaButtonReceiver} for more 242 * details. 243 * </p> 244 * @param context The context to use to create the session. 245 * @param tag A short name for debugging purposes. 246 */ 247 public MediaSessionCompat(Context context, String tag) { 248 this(context, tag, null, null); 249 } 250 251 /** 252 * Creates a new session with a specified media button receiver (a component name and/or 253 * a pending intent). You must call {@link #release()} when finished with the session. 254 * <p> 255 * The session will automatically be registered with the system but will not be published 256 * until {@link #setActive(boolean) setActive(true)} is called. 257 * </p><p> 258 * For API 20 or earlier, note that a media button receiver is required for handling 259 * {@link Intent#ACTION_MEDIA_BUTTON}. This constructor will attempt to find an appropriate 260 * {@link BroadcastReceiver} from your manifest if it's not specified. See 261 * {@link MediaButtonReceiver} for more details. 262 * </p> 263 * @param context The context to use to create the session. 264 * @param tag A short name for debugging purposes. 265 * @param mbrComponent The component name for your media button receiver. 266 * @param mbrIntent The PendingIntent for your receiver component that handles 267 * media button events. This is optional and will be used on between 268 * {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2} and 269 * {@link android.os.Build.VERSION_CODES#KITKAT_WATCH} instead of the 270 * component name. 271 */ 272 public MediaSessionCompat(Context context, String tag, ComponentName mbrComponent, 273 PendingIntent mbrIntent) { 274 if (context == null) { 275 throw new IllegalArgumentException("context must not be null"); 276 } 277 if (TextUtils.isEmpty(tag)) { 278 throw new IllegalArgumentException("tag must not be null or empty"); 279 } 280 281 if (mbrComponent == null) { 282 mbrComponent = MediaButtonReceiver.getMediaButtonReceiverComponent(context); 283 if (mbrComponent == null) { 284 Log.w(TAG, "Couldn't find a unique registered media button receiver in the " 285 + "given context."); 286 } 287 } 288 if (mbrComponent != null && mbrIntent == null) { 289 // construct a PendingIntent for the media button 290 Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); 291 // the associated intent will be handled by the component being registered 292 mediaButtonIntent.setComponent(mbrComponent); 293 mbrIntent = PendingIntent.getBroadcast(context, 294 0/* requestCode, ignored */, mediaButtonIntent, 0/* flags */); 295 } 296 if (android.os.Build.VERSION.SDK_INT >= 21) { 297 mImpl = new MediaSessionImplApi21(context, tag); 298 if (android.os.Build.VERSION.SDK_INT < 26) { 299 // Set default callback to respond to controllers' extra binder requests. 300 setCallback(new Callback() {}); 301 } 302 mImpl.setMediaButtonReceiver(mbrIntent); 303 } else if (android.os.Build.VERSION.SDK_INT >= 19) { 304 mImpl = new MediaSessionImplApi19(context, tag, mbrComponent, mbrIntent); 305 } else if (android.os.Build.VERSION.SDK_INT >= 18) { 306 mImpl = new MediaSessionImplApi18(context, tag, mbrComponent, mbrIntent); 307 } else { 308 mImpl = new MediaSessionImplBase(context, tag, mbrComponent, mbrIntent); 309 } 310 mController = new MediaControllerCompat(context, this); 311 312 if (sMaxBitmapSize == 0) { 313 sMaxBitmapSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 314 MAX_BITMAP_SIZE_IN_DP, context.getResources().getDisplayMetrics()); 315 } 316 } 317 318 private MediaSessionCompat(Context context, MediaSessionImpl impl) { 319 mImpl = impl; 320 if (android.os.Build.VERSION.SDK_INT >= 21 && android.os.Build.VERSION.SDK_INT < 26) { 321 // Set default callback to respond to controllers' extra binder requests. 322 setCallback(new Callback() {}); 323 } 324 mController = new MediaControllerCompat(context, this); 325 } 326 327 /** 328 * Add a callback to receive updates on for the MediaSession. This includes 329 * media button and volume events. The caller's thread will be used to post 330 * events. 331 * 332 * @param callback The callback object 333 */ 334 public void setCallback(Callback callback) { 335 setCallback(callback, null); 336 } 337 338 /** 339 * Set the callback to receive updates for the MediaSession. This includes 340 * media button and volume events. Set the callback to null to stop 341 * receiving events. 342 * 343 * @param callback The callback to receive updates on. 344 * @param handler The handler that events should be posted on. 345 */ 346 public void setCallback(Callback callback, Handler handler) { 347 mImpl.setCallback(callback, handler != null ? handler : new Handler()); 348 } 349 350 /** 351 * Set an intent for launching UI for this Session. This can be used as a 352 * quick link to an ongoing media screen. The intent should be for an 353 * activity that may be started using 354 * {@link Activity#startActivity(Intent)}. 355 * 356 * @param pi The intent to launch to show UI for this Session. 357 */ 358 public void setSessionActivity(PendingIntent pi) { 359 mImpl.setSessionActivity(pi); 360 } 361 362 /** 363 * Set a pending intent for your media button receiver to allow restarting 364 * playback after the session has been stopped. If your app is started in 365 * this way an {@link Intent#ACTION_MEDIA_BUTTON} intent will be sent via 366 * the pending intent. 367 * <p> 368 * This method will only work on 369 * {@link android.os.Build.VERSION_CODES#LOLLIPOP} and later. Earlier 370 * platform versions must include the media button receiver in the 371 * constructor. 372 * 373 * @param mbr The {@link PendingIntent} to send the media button event to. 374 */ 375 public void setMediaButtonReceiver(PendingIntent mbr) { 376 mImpl.setMediaButtonReceiver(mbr); 377 } 378 379 /** 380 * Set any flags for the session. 381 * 382 * @param flags The flags to set for this session. 383 */ 384 public void setFlags(@SessionFlags int flags) { 385 mImpl.setFlags(flags); 386 } 387 388 /** 389 * Set the stream this session is playing on. This will affect the system's 390 * volume handling for this session. If {@link #setPlaybackToRemote} was 391 * previously called it will stop receiving volume commands and the system 392 * will begin sending volume changes to the appropriate stream. 393 * <p> 394 * By default sessions are on {@link AudioManager#STREAM_MUSIC}. 395 * 396 * @param stream The {@link AudioManager} stream this session is playing on. 397 */ 398 public void setPlaybackToLocal(int stream) { 399 mImpl.setPlaybackToLocal(stream); 400 } 401 402 /** 403 * Configure this session to use remote volume handling. This must be called 404 * to receive volume button events, otherwise the system will adjust the 405 * current stream volume for this session. If {@link #setPlaybackToLocal} 406 * was previously called that stream will stop receiving volume changes for 407 * this session. 408 * <p> 409 * On platforms earlier than {@link android.os.Build.VERSION_CODES#LOLLIPOP} 410 * this will only allow an app to handle volume commands sent directly to 411 * the session by a {@link MediaControllerCompat}. System routing of volume 412 * keys will not use the volume provider. 413 * 414 * @param volumeProvider The provider that will handle volume changes. May 415 * not be null. 416 */ 417 public void setPlaybackToRemote(VolumeProviderCompat volumeProvider) { 418 if (volumeProvider == null) { 419 throw new IllegalArgumentException("volumeProvider may not be null!"); 420 } 421 mImpl.setPlaybackToRemote(volumeProvider); 422 } 423 424 /** 425 * Set if this session is currently active and ready to receive commands. If 426 * set to false your session's controller may not be discoverable. You must 427 * set the session to active before it can start receiving media button 428 * events or transport commands. 429 * <p> 430 * On platforms earlier than 431 * {@link android.os.Build.VERSION_CODES#LOLLIPOP}, 432 * a media button event receiver should be set via the constructor to 433 * receive media button events. 434 * 435 * @param active Whether this session is active or not. 436 */ 437 public void setActive(boolean active) { 438 mImpl.setActive(active); 439 for (OnActiveChangeListener listener : mActiveListeners) { 440 listener.onActiveChanged(); 441 } 442 } 443 444 /** 445 * Get the current active state of this session. 446 * 447 * @return True if the session is active, false otherwise. 448 */ 449 public boolean isActive() { 450 return mImpl.isActive(); 451 } 452 453 /** 454 * Send a proprietary event to all MediaControllers listening to this 455 * Session. It's up to the Controller/Session owner to determine the meaning 456 * of any events. 457 * 458 * @param event The name of the event to send 459 * @param extras Any extras included with the event 460 */ 461 public void sendSessionEvent(String event, Bundle extras) { 462 if (TextUtils.isEmpty(event)) { 463 throw new IllegalArgumentException("event cannot be null or empty"); 464 } 465 mImpl.sendSessionEvent(event, extras); 466 } 467 468 /** 469 * This must be called when an app has finished performing playback. If 470 * playback is expected to start again shortly the session can be left open, 471 * but it must be released if your activity or service is being destroyed. 472 */ 473 public void release() { 474 mImpl.release(); 475 } 476 477 /** 478 * Retrieve a token object that can be used by apps to create a 479 * {@link MediaControllerCompat} for interacting with this session. The 480 * owner of the session is responsible for deciding how to distribute these 481 * tokens. 482 * <p> 483 * On platform versions before 484 * {@link android.os.Build.VERSION_CODES#LOLLIPOP} this token may only be 485 * used within your app as there is no way to guarantee other apps are using 486 * the same version of the support library. 487 * 488 * @return A token that can be used to create a media controller for this 489 * session. 490 */ 491 public Token getSessionToken() { 492 return mImpl.getSessionToken(); 493 } 494 495 /** 496 * Get a controller for this session. This is a convenience method to avoid 497 * having to cache your own controller in process. 498 * 499 * @return A controller for this session. 500 */ 501 public MediaControllerCompat getController() { 502 return mController; 503 } 504 505 /** 506 * Update the current playback state. 507 * 508 * @param state The current state of playback 509 */ 510 public void setPlaybackState(PlaybackStateCompat state) { 511 mImpl.setPlaybackState(state); 512 } 513 514 /** 515 * Update the current metadata. New metadata can be created using 516 * {@link android.support.v4.media.MediaMetadataCompat.Builder}. This operation may take time 517 * proportional to the size of the bitmap to replace large bitmaps with a scaled down copy. 518 * 519 * @param metadata The new metadata 520 * @see android.support.v4.media.MediaMetadataCompat.Builder#putBitmap 521 */ 522 public void setMetadata(MediaMetadataCompat metadata) { 523 mImpl.setMetadata(metadata); 524 } 525 526 /** 527 * Update the list of items in the play queue. It is an ordered list and 528 * should contain the current item, and previous or upcoming items if they 529 * exist. Specify null if there is no current play queue. 530 * <p> 531 * The queue should be of reasonable size. If the play queue is unbounded 532 * within your app, it is better to send a reasonable amount in a sliding 533 * window instead. 534 * 535 * @param queue A list of items in the play queue. 536 */ 537 public void setQueue(List<QueueItem> queue) { 538 mImpl.setQueue(queue); 539 } 540 541 /** 542 * Set the title of the play queue. The UI should display this title along 543 * with the play queue itself. e.g. "Play Queue", "Now Playing", or an album 544 * name. 545 * 546 * @param title The title of the play queue. 547 */ 548 public void setQueueTitle(CharSequence title) { 549 mImpl.setQueueTitle(title); 550 } 551 552 /** 553 * Set the style of rating used by this session. Apps trying to set the 554 * rating should use this style. Must be one of the following: 555 * <ul> 556 * <li>{@link RatingCompat#RATING_NONE}</li> 557 * <li>{@link RatingCompat#RATING_3_STARS}</li> 558 * <li>{@link RatingCompat#RATING_4_STARS}</li> 559 * <li>{@link RatingCompat#RATING_5_STARS}</li> 560 * <li>{@link RatingCompat#RATING_HEART}</li> 561 * <li>{@link RatingCompat#RATING_PERCENTAGE}</li> 562 * <li>{@link RatingCompat#RATING_THUMB_UP_DOWN}</li> 563 * </ul> 564 */ 565 public void setRatingType(@RatingCompat.Style int type) { 566 mImpl.setRatingType(type); 567 } 568 569 /** 570 * Set the repeat mode for this session. 571 * <p> 572 * Note that if this method is not called before, {@link MediaControllerCompat#getRepeatMode} 573 * will return {@link PlaybackStateCompat#REPEAT_MODE_NONE}. 574 * 575 * @param repeatMode The repeat mode. Must be one of the followings: 576 * {@link PlaybackStateCompat#REPEAT_MODE_NONE}, 577 * {@link PlaybackStateCompat#REPEAT_MODE_ONE}, 578 * {@link PlaybackStateCompat#REPEAT_MODE_ALL} 579 */ 580 public void setRepeatMode(@PlaybackStateCompat.RepeatMode int repeatMode) { 581 mImpl.setRepeatMode(repeatMode); 582 } 583 584 /** 585 * Set the shuffle mode for this session. 586 * <p> 587 * Note that if this method is not called before, 588 * {@link MediaControllerCompat#isShuffleModeEnabled} will return {@code false}. 589 * 590 * @param enabled {@code true} to enable the shuffle mode, {@code false} to disable. 591 */ 592 public void setShuffleModeEnabled(boolean enabled) { 593 mImpl.setShuffleModeEnabled(enabled); 594 } 595 596 /** 597 * Set some extras that can be associated with the 598 * {@link MediaSessionCompat}. No assumptions should be made as to how a 599 * {@link MediaControllerCompat} will handle these extras. Keys should be 600 * fully qualified (e.g. com.example.MY_EXTRA) to avoid conflicts. 601 * 602 * @param extras The extras associated with the session. 603 */ 604 public void setExtras(Bundle extras) { 605 mImpl.setExtras(extras); 606 } 607 608 /** 609 * Gets the underlying framework {@link android.media.session.MediaSession} 610 * object. 611 * <p> 612 * This method is only supported on API 21+. 613 * </p> 614 * 615 * @return The underlying {@link android.media.session.MediaSession} object, 616 * or null if none. 617 */ 618 public Object getMediaSession() { 619 return mImpl.getMediaSession(); 620 } 621 622 /** 623 * Gets the underlying framework {@link android.media.RemoteControlClient} 624 * object. 625 * <p> 626 * This method is only supported on APIs 14-20. On API 21+ 627 * {@link #getMediaSession()} should be used instead. 628 * 629 * @return The underlying {@link android.media.RemoteControlClient} object, 630 * or null if none. 631 */ 632 public Object getRemoteControlClient() { 633 return mImpl.getRemoteControlClient(); 634 } 635 636 /** 637 * Returns the name of the package that sent the last media button, transport control, or 638 * command from controllers and the system. This is only valid while in a request callback, such 639 * as {@link Callback#onPlay}. This method is not available and returns null on pre-N devices. 640 * 641 * @hide 642 */ 643 @RestrictTo(LIBRARY_GROUP) 644 public String getCallingPackage() { 645 return mImpl.getCallingPackage(); 646 } 647 648 /** 649 * Adds a listener to be notified when the active status of this session 650 * changes. This is primarily used by the support library and should not be 651 * needed by apps. 652 * 653 * @param listener The listener to add. 654 */ 655 public void addOnActiveChangeListener(OnActiveChangeListener listener) { 656 if (listener == null) { 657 throw new IllegalArgumentException("Listener may not be null"); 658 } 659 mActiveListeners.add(listener); 660 } 661 662 /** 663 * Stops the listener from being notified when the active status of this 664 * session changes. 665 * 666 * @param listener The listener to remove. 667 */ 668 public void removeOnActiveChangeListener(OnActiveChangeListener listener) { 669 if (listener == null) { 670 throw new IllegalArgumentException("Listener may not be null"); 671 } 672 mActiveListeners.remove(listener); 673 } 674 675 /** 676 * Creates an instance from a framework {@link android.media.session.MediaSession} object. 677 * <p> 678 * This method is only supported on API 21+. On API 20 and below, it returns null. 679 * </p> 680 * 681 * @param context The context to use to create the session. 682 * @param mediaSession A {@link android.media.session.MediaSession} object. 683 * @return An equivalent {@link MediaSessionCompat} object, or null if none. 684 * @deprecated Use {@link #fromMediaSession(Context, Object)} instead. 685 */ 686 @Deprecated 687 public static MediaSessionCompat obtain(Context context, Object mediaSession) { 688 return fromMediaSession(context, mediaSession); 689 } 690 691 /** 692 * Creates an instance from a framework {@link android.media.session.MediaSession} object. 693 * <p> 694 * This method is only supported on API 21+. On API 20 and below, it returns null. 695 * </p> 696 * 697 * @param context The context to use to create the session. 698 * @param mediaSession A {@link android.media.session.MediaSession} object. 699 * @return An equivalent {@link MediaSessionCompat} object, or null if none. 700 */ 701 public static MediaSessionCompat fromMediaSession(Context context, Object mediaSession) { 702 if (context != null && mediaSession != null && Build.VERSION.SDK_INT >= 21) { 703 return new MediaSessionCompat(context, new MediaSessionImplApi21(mediaSession)); 704 } 705 return null; 706 } 707 708 /** 709 * Receives transport controls, media buttons, and commands from controllers 710 * and the system. The callback may be set using {@link #setCallback}. 711 */ 712 public abstract static class Callback { 713 final Object mCallbackObj; 714 WeakReference<MediaSessionImpl> mSessionImpl; 715 716 @SuppressLint("NewApi") 717 public Callback() { 718 if (BuildCompat.isAtLeastO()) { 719 mCallbackObj = MediaSessionCompatApi26.createCallback(new StubApi26()); 720 } else if (android.os.Build.VERSION.SDK_INT >= 24) { 721 mCallbackObj = MediaSessionCompatApi24.createCallback(new StubApi24()); 722 } else if (android.os.Build.VERSION.SDK_INT >= 23) { 723 mCallbackObj = MediaSessionCompatApi23.createCallback(new StubApi23()); 724 } else if (android.os.Build.VERSION.SDK_INT >= 21) { 725 mCallbackObj = MediaSessionCompatApi21.createCallback(new StubApi21()); 726 } else { 727 mCallbackObj = null; 728 } 729 } 730 731 /** 732 * Called when a controller has sent a custom command to this session. 733 * The owner of the session may handle custom commands but is not 734 * required to. 735 * 736 * @param command The command name. 737 * @param extras Optional parameters for the command, may be null. 738 * @param cb A result receiver to which a result may be sent by the command, may be null. 739 */ 740 public void onCommand(String command, Bundle extras, ResultReceiver cb) { 741 } 742 743 /** 744 * Override to handle media button events. 745 * 746 * @param mediaButtonEvent The media button event intent. 747 * @return True if the event was handled, false otherwise. 748 */ 749 public boolean onMediaButtonEvent(Intent mediaButtonEvent) { 750 return false; 751 } 752 753 /** 754 * Override to handle requests to prepare playback. During the preparation, a session 755 * should not hold audio focus in order to allow other session play seamlessly. 756 * The state of playback should be updated to {@link PlaybackStateCompat#STATE_PAUSED} 757 * after the preparation is done. 758 */ 759 public void onPrepare() { 760 } 761 762 /** 763 * Override to handle requests to prepare for playing a specific mediaId that was provided 764 * by your app. During the preparation, a session should not hold audio focus in order to 765 * allow other session play seamlessly. The state of playback should be updated to 766 * {@link PlaybackStateCompat#STATE_PAUSED} after the preparation is done. The playback 767 * of the prepared content should start in the implementation of {@link #onPlay}. Override 768 * {@link #onPlayFromMediaId} to handle requests for starting playback without preparation. 769 */ 770 public void onPrepareFromMediaId(String mediaId, Bundle extras) { 771 } 772 773 /** 774 * Override to handle requests to prepare playback from a search query. An 775 * empty query indicates that the app may prepare any music. The 776 * implementation should attempt to make a smart choice about what to 777 * play. During the preparation, a session should not hold audio focus in order to allow 778 * other session play seamlessly. The state of playback should be updated to 779 * {@link PlaybackStateCompat#STATE_PAUSED} after the preparation is done. 780 * The playback of the prepared content should start in the implementation of 781 * {@link #onPlay}. Override {@link #onPlayFromSearch} to handle requests for 782 * starting playback without preparation. 783 */ 784 public void onPrepareFromSearch(String query, Bundle extras) { 785 } 786 787 /** 788 * Override to handle requests to prepare a specific media item represented by a URI. 789 * During the preparation, a session should not hold audio focus in order to allow other 790 * session play seamlessly. The state of playback should be updated to 791 * {@link PlaybackStateCompat#STATE_PAUSED} after the preparation is done. The playback of 792 * the prepared content should start in the implementation of {@link #onPlay}. Override 793 * {@link #onPlayFromUri} to handle requests for starting playback without preparation. 794 */ 795 public void onPrepareFromUri(Uri uri, Bundle extras) { 796 } 797 798 /** 799 * Override to handle requests to begin playback. 800 */ 801 public void onPlay() { 802 } 803 804 /** 805 * Override to handle requests to play a specific mediaId that was 806 * provided by your app. 807 */ 808 public void onPlayFromMediaId(String mediaId, Bundle extras) { 809 } 810 811 /** 812 * Override to handle requests to begin playback from a search query. An 813 * empty query indicates that the app may play any music. The 814 * implementation should attempt to make a smart choice about what to 815 * play. 816 */ 817 public void onPlayFromSearch(String query, Bundle extras) { 818 } 819 820 /** 821 * Override to handle requests to play a specific media item represented by a URI. 822 */ 823 public void onPlayFromUri(Uri uri, Bundle extras) { 824 } 825 826 /** 827 * Override to handle requests to play an item with a given id from the 828 * play queue. 829 */ 830 public void onSkipToQueueItem(long id) { 831 } 832 833 /** 834 * Override to handle requests to pause playback. 835 */ 836 public void onPause() { 837 } 838 839 /** 840 * Override to handle requests to skip to the next media item. 841 */ 842 public void onSkipToNext() { 843 } 844 845 /** 846 * Override to handle requests to skip to the previous media item. 847 */ 848 public void onSkipToPrevious() { 849 } 850 851 /** 852 * Override to handle requests to fast forward. 853 */ 854 public void onFastForward() { 855 } 856 857 /** 858 * Override to handle requests to rewind. 859 */ 860 public void onRewind() { 861 } 862 863 /** 864 * Override to handle requests to stop playback. 865 */ 866 public void onStop() { 867 } 868 869 /** 870 * Override to handle requests to seek to a specific position in ms. 871 * 872 * @param pos New position to move to, in milliseconds. 873 */ 874 public void onSeekTo(long pos) { 875 } 876 877 /** 878 * Override to handle the item being rated. 879 * 880 * @param rating 881 */ 882 public void onSetRating(RatingCompat rating) { 883 } 884 885 /** 886 * Override to handle the setting of the repeat mode. 887 * <p> 888 * You should call {@link #setRepeatMode} before end of this method in order to notify 889 * the change to the {@link MediaControllerCompat}, or 890 * {@link MediaControllerCompat#getRepeatMode} could return an invalid value. 891 * 892 * @param repeatMode The repeat mode which is one of followings: 893 * {@link PlaybackStateCompat#REPEAT_MODE_NONE}, 894 * {@link PlaybackStateCompat#REPEAT_MODE_ONE}, 895 * {@link PlaybackStateCompat#REPEAT_MODE_ALL} 896 */ 897 public void onSetRepeatMode(@PlaybackStateCompat.RepeatMode int repeatMode) { 898 } 899 900 /** 901 * Override to handle the setting of the shuffle mode. 902 * <p> 903 * You should call {@link #setShuffleModeEnabled} before the end of this method in order to 904 * notify the change to the {@link MediaControllerCompat}, or 905 * {@link MediaControllerCompat#isShuffleModeEnabled} could return an invalid value. 906 * 907 * @param enabled true when the shuffle mode is enabled, false otherwise. 908 */ 909 public void onSetShuffleModeEnabled(boolean enabled) { 910 } 911 912 /** 913 * Called when a {@link MediaControllerCompat} wants a 914 * {@link PlaybackStateCompat.CustomAction} to be performed. 915 * 916 * @param action The action that was originally sent in the 917 * {@link PlaybackStateCompat.CustomAction}. 918 * @param extras Optional extras specified by the 919 * {@link MediaControllerCompat}. 920 * @see #ACTION_FLAG_AS_INAPPROPRIATE 921 * @see #ACTION_SKIP_AD 922 */ 923 public void onCustomAction(String action, Bundle extras) { 924 } 925 926 /** 927 * Called when a {@link MediaControllerCompat} wants to add a {@link QueueItem} 928 * with the given {@link MediaDescriptionCompat description} at the end of the play queue. 929 * 930 * @param description The {@link MediaDescriptionCompat} for creating the {@link QueueItem} 931 * to be inserted. 932 */ 933 public void onAddQueueItem(MediaDescriptionCompat description) { 934 } 935 936 /** 937 * Called when a {@link MediaControllerCompat} wants to add a {@link QueueItem} 938 * with the given {@link MediaDescriptionCompat description} at the specified position 939 * in the play queue. 940 * 941 * @param description The {@link MediaDescriptionCompat} for creating the {@link QueueItem} 942 * to be inserted. 943 * @param index The index at which the created {@link QueueItem} is to be inserted. 944 */ 945 public void onAddQueueItem(MediaDescriptionCompat description, int index) { 946 } 947 948 /** 949 * Called when a {@link MediaControllerCompat} wants to remove the first occurrence of the 950 * specified {@link QueueItem} with the given {@link MediaDescriptionCompat description} 951 * in the play queue. 952 * 953 * @param description The {@link MediaDescriptionCompat} for denoting the {@link QueueItem} 954 * to be removed. 955 */ 956 public void onRemoveQueueItem(MediaDescriptionCompat description) { 957 } 958 959 /** 960 * Called when a {@link MediaControllerCompat} wants to remove a {@link QueueItem} at the 961 * specified position in the play queue. 962 * 963 * @param index The index of the element to be removed. 964 */ 965 public void onRemoveQueueItemAt(int index) { 966 } 967 968 @RequiresApi(21) 969 private class StubApi21 implements MediaSessionCompatApi21.Callback { 970 971 StubApi21() { 972 } 973 974 @Override 975 public void onCommand(String command, Bundle extras, ResultReceiver cb) { 976 if (command.equals(MediaControllerCompat.COMMAND_GET_EXTRA_BINDER)) { 977 MediaSessionImplApi21 impl = (MediaSessionImplApi21) mSessionImpl.get(); 978 if (impl != null) { 979 Bundle result = new Bundle(); 980 BundleCompat.putBinder(result, EXTRA_BINDER, 981 (IBinder) impl.getSessionToken().getExtraBinder()); 982 cb.send(0, result); 983 } 984 } else if (command.equals(MediaControllerCompat.COMMAND_ADD_QUEUE_ITEM)) { 985 extras.setClassLoader(MediaDescriptionCompat.class.getClassLoader()); 986 Callback.this.onAddQueueItem( 987 (MediaDescriptionCompat) extras.getParcelable( 988 MediaControllerCompat.COMMAND_ARGUMENT_MEDIA_DESCRIPTION)); 989 } else if (command.equals(MediaControllerCompat.COMMAND_ADD_QUEUE_ITEM_AT)) { 990 extras.setClassLoader(MediaDescriptionCompat.class.getClassLoader()); 991 Callback.this.onAddQueueItem( 992 (MediaDescriptionCompat) extras.getParcelable( 993 MediaControllerCompat.COMMAND_ARGUMENT_MEDIA_DESCRIPTION), 994 extras.getInt(MediaControllerCompat.COMMAND_ARGUMENT_INDEX)); 995 } else if (command.equals(MediaControllerCompat.COMMAND_REMOVE_QUEUE_ITEM)) { 996 extras.setClassLoader(MediaDescriptionCompat.class.getClassLoader()); 997 Callback.this.onRemoveQueueItem( 998 (MediaDescriptionCompat) extras.getParcelable( 999 MediaControllerCompat.COMMAND_ARGUMENT_MEDIA_DESCRIPTION)); 1000 } else if (command.equals(MediaControllerCompat.COMMAND_REMOVE_QUEUE_ITEM_AT)) { 1001 Callback.this.onRemoveQueueItemAt( 1002 extras.getInt(MediaControllerCompat.COMMAND_ARGUMENT_INDEX)); 1003 } else { 1004 Callback.this.onCommand(command, extras, cb); 1005 } 1006 } 1007 1008 @Override 1009 public boolean onMediaButtonEvent(Intent mediaButtonIntent) { 1010 return Callback.this.onMediaButtonEvent(mediaButtonIntent); 1011 } 1012 1013 @Override 1014 public void onPlay() { 1015 Callback.this.onPlay(); 1016 } 1017 1018 @Override 1019 public void onPlayFromMediaId(String mediaId, Bundle extras) { 1020 Callback.this.onPlayFromMediaId(mediaId, extras); 1021 } 1022 1023 @Override 1024 public void onPlayFromSearch(String search, Bundle extras) { 1025 Callback.this.onPlayFromSearch(search, extras); 1026 } 1027 1028 @Override 1029 public void onSkipToQueueItem(long id) { 1030 Callback.this.onSkipToQueueItem(id); 1031 } 1032 1033 @Override 1034 public void onPause() { 1035 Callback.this.onPause(); 1036 } 1037 1038 @Override 1039 public void onSkipToNext() { 1040 Callback.this.onSkipToNext(); 1041 } 1042 1043 @Override 1044 public void onSkipToPrevious() { 1045 Callback.this.onSkipToPrevious(); 1046 } 1047 1048 @Override 1049 public void onFastForward() { 1050 Callback.this.onFastForward(); 1051 } 1052 1053 @Override 1054 public void onRewind() { 1055 Callback.this.onRewind(); 1056 } 1057 1058 @Override 1059 public void onStop() { 1060 Callback.this.onStop(); 1061 } 1062 1063 @Override 1064 public void onSeekTo(long pos) { 1065 Callback.this.onSeekTo(pos); 1066 } 1067 1068 @Override 1069 public void onSetRating(Object ratingObj) { 1070 Callback.this.onSetRating(RatingCompat.fromRating(ratingObj)); 1071 } 1072 1073 @Override 1074 public void onCustomAction(String action, Bundle extras) { 1075 if (action.equals(ACTION_PLAY_FROM_URI)) { 1076 Uri uri = extras.getParcelable(ACTION_ARGUMENT_URI); 1077 Bundle bundle = extras.getParcelable(ACTION_ARGUMENT_EXTRAS); 1078 Callback.this.onPlayFromUri(uri, bundle); 1079 } else if (action.equals(ACTION_PREPARE)) { 1080 Callback.this.onPrepare(); 1081 } else if (action.equals(ACTION_PREPARE_FROM_MEDIA_ID)) { 1082 String mediaId = extras.getString(ACTION_ARGUMENT_MEDIA_ID); 1083 Bundle bundle = extras.getBundle(ACTION_ARGUMENT_EXTRAS); 1084 Callback.this.onPrepareFromMediaId(mediaId, bundle); 1085 } else if (action.equals(ACTION_PREPARE_FROM_SEARCH)) { 1086 String query = extras.getString(ACTION_ARGUMENT_QUERY); 1087 Bundle bundle = extras.getBundle(ACTION_ARGUMENT_EXTRAS); 1088 Callback.this.onPrepareFromSearch(query, bundle); 1089 } else if (action.equals(ACTION_PREPARE_FROM_URI)) { 1090 Uri uri = extras.getParcelable(ACTION_ARGUMENT_URI); 1091 Bundle bundle = extras.getBundle(ACTION_ARGUMENT_EXTRAS); 1092 Callback.this.onPrepareFromUri(uri, bundle); 1093 } else if (action.equals(ACTION_SET_REPEAT_MODE)) { 1094 int repeatMode = extras.getInt(ACTION_ARGUMENT_REPEAT_MODE); 1095 Callback.this.onSetRepeatMode(repeatMode); 1096 } else if (action.equals(ACTION_SET_SHUFFLE_MODE_ENABLED)) { 1097 boolean enabled = extras.getBoolean(ACTION_ARGUMENT_SHUFFLE_MODE_ENABLED); 1098 Callback.this.onSetShuffleModeEnabled(enabled); 1099 } else { 1100 Callback.this.onCustomAction(action, extras); 1101 } 1102 } 1103 } 1104 1105 @RequiresApi(23) 1106 private class StubApi23 extends StubApi21 implements MediaSessionCompatApi23.Callback { 1107 1108 StubApi23() { 1109 } 1110 1111 @Override 1112 public void onPlayFromUri(Uri uri, Bundle extras) { 1113 Callback.this.onPlayFromUri(uri, extras); 1114 } 1115 } 1116 1117 @RequiresApi(24) 1118 private class StubApi24 extends StubApi23 implements MediaSessionCompatApi24.Callback { 1119 1120 StubApi24() { 1121 } 1122 1123 @Override 1124 public void onPrepare() { 1125 Callback.this.onPrepare(); 1126 } 1127 1128 @Override 1129 public void onPrepareFromMediaId(String mediaId, Bundle extras) { 1130 Callback.this.onPrepareFromMediaId(mediaId, extras); 1131 } 1132 1133 @Override 1134 public void onPrepareFromSearch(String query, Bundle extras) { 1135 Callback.this.onPrepareFromSearch(query, extras); 1136 } 1137 1138 @Override 1139 public void onPrepareFromUri(Uri uri, Bundle extras) { 1140 Callback.this.onPrepareFromUri(uri, extras); 1141 } 1142 } 1143 1144 @RequiresApi(26) 1145 private class StubApi26 extends StubApi24 implements MediaSessionCompatApi26.Callback { 1146 @Override 1147 public void onSetRepeatMode(int repeatMode) { 1148 Callback.this.onSetRepeatMode(repeatMode); 1149 } 1150 1151 @Override 1152 public void onSetShuffleModeEnabled(boolean enabled) { 1153 Callback.this.onSetShuffleModeEnabled(enabled); 1154 } 1155 1156 @Override 1157 public void onAddQueueItem(Object descriptionObject) { 1158 Callback.this.onAddQueueItem( 1159 MediaDescriptionCompat.fromMediaDescription(descriptionObject)); 1160 } 1161 1162 @Override 1163 public void onAddQueueItem(Object descriptionObject, int index) { 1164 Callback.this.onAddQueueItem( 1165 MediaDescriptionCompat.fromMediaDescription(descriptionObject), index); 1166 } 1167 1168 @Override 1169 public void onRemoveQueueItem(Object descriptionObject) { 1170 Callback.this.onRemoveQueueItem( 1171 MediaDescriptionCompat.fromMediaDescription(descriptionObject)); 1172 } 1173 1174 @Override 1175 public void onRemoveQueueItemAt(int index) { 1176 Callback.this.onRemoveQueueItemAt(index); 1177 } 1178 } 1179 } 1180 1181 /** 1182 * Represents an ongoing session. This may be passed to apps by the session 1183 * owner to allow them to create a {@link MediaControllerCompat} to communicate with 1184 * the session. 1185 */ 1186 public static final class Token implements Parcelable { 1187 private final Object mInner; 1188 private final IMediaSession mExtraBinder; 1189 1190 Token(Object inner) { 1191 this(inner, null); 1192 } 1193 1194 Token(Object inner, IMediaSession extraBinder) { 1195 mInner = inner; 1196 mExtraBinder = extraBinder; 1197 } 1198 1199 /** 1200 * Creates a compat Token from a framework 1201 * {@link android.media.session.MediaSession.Token} object. 1202 * <p> 1203 * This method is only supported on 1204 * {@link android.os.Build.VERSION_CODES#LOLLIPOP} and later. 1205 * </p> 1206 * 1207 * @param token The framework token object. 1208 * @return A compat Token for use with {@link MediaControllerCompat}. 1209 */ 1210 public static Token fromToken(Object token) { 1211 return fromToken(token, null); 1212 } 1213 1214 /** 1215 * Creates a compat Token from a framework 1216 * {@link android.media.session.MediaSession.Token} object, and the extra binder. 1217 * <p> 1218 * This method is only supported on 1219 * {@link android.os.Build.VERSION_CODES#LOLLIPOP} and later. 1220 * </p> 1221 * 1222 * @param token The framework token object. 1223 * @param extraBinder The extra binder. 1224 * @return A compat Token for use with {@link MediaControllerCompat}. 1225 * @hide 1226 */ 1227 @RestrictTo(LIBRARY_GROUP) 1228 public static Token fromToken(Object token, IMediaSession extraBinder) { 1229 if (token != null && android.os.Build.VERSION.SDK_INT >= 21) { 1230 return new Token(MediaSessionCompatApi21.verifyToken(token), extraBinder); 1231 } 1232 return null; 1233 } 1234 1235 @Override 1236 public int describeContents() { 1237 return 0; 1238 } 1239 1240 @Override 1241 public void writeToParcel(Parcel dest, int flags) { 1242 if (android.os.Build.VERSION.SDK_INT >= 21) { 1243 dest.writeParcelable((Parcelable) mInner, flags); 1244 dest.writeStrongBinder((IBinder) mExtraBinder); 1245 } else { 1246 dest.writeStrongBinder((IBinder) mInner); 1247 } 1248 } 1249 1250 @Override 1251 public int hashCode() { 1252 if (mInner == null) { 1253 return 0; 1254 } 1255 return mInner.hashCode(); 1256 } 1257 1258 @Override 1259 public boolean equals(Object obj) { 1260 if (this == obj) { 1261 return true; 1262 } 1263 if (!(obj instanceof Token)) { 1264 return false; 1265 } 1266 1267 Token other = (Token) obj; 1268 if (mInner == null) { 1269 return other.mInner == null; 1270 } 1271 if (other.mInner == null) { 1272 return false; 1273 } 1274 return mInner.equals(other.mInner); 1275 } 1276 1277 /** 1278 * Gets the underlying framework {@link android.media.session.MediaSession.Token} object. 1279 * <p> 1280 * This method is only supported on API 21+. 1281 * </p> 1282 * 1283 * @return The underlying {@link android.media.session.MediaSession.Token} object, 1284 * or null if none. 1285 */ 1286 public Object getToken() { 1287 return mInner; 1288 } 1289 1290 /** 1291 * @hide 1292 */ 1293 @RestrictTo(LIBRARY_GROUP) 1294 public IMediaSession getExtraBinder() { 1295 return mExtraBinder; 1296 } 1297 1298 public static final Parcelable.Creator<Token> CREATOR 1299 = new Parcelable.Creator<Token>() { 1300 @Override 1301 public Token createFromParcel(Parcel in) { 1302 Object inner; 1303 IMediaSession extraBinder = null; 1304 if (android.os.Build.VERSION.SDK_INT >= 21) { 1305 inner = in.readParcelable(null); 1306 extraBinder = (IMediaSession) in.readStrongBinder(); 1307 } else { 1308 inner = in.readStrongBinder(); 1309 } 1310 return new Token(inner, extraBinder); 1311 } 1312 1313 @Override 1314 public Token[] newArray(int size) { 1315 return new Token[size]; 1316 } 1317 }; 1318 } 1319 1320 /** 1321 * A single item that is part of the play queue. It contains a description 1322 * of the item and its id in the queue. 1323 */ 1324 public static final class QueueItem implements Parcelable { 1325 /** 1326 * This id is reserved. No items can be explicitly assigned this id. 1327 */ 1328 public static final int UNKNOWN_ID = -1; 1329 1330 private final MediaDescriptionCompat mDescription; 1331 private final long mId; 1332 1333 private Object mItem; 1334 1335 /** 1336 * Create a new {@link MediaSessionCompat.QueueItem}. 1337 * 1338 * @param description The {@link MediaDescriptionCompat} for this item. 1339 * @param id An identifier for this item. It must be unique within the 1340 * play queue and cannot be {@link #UNKNOWN_ID}. 1341 */ 1342 public QueueItem(MediaDescriptionCompat description, long id) { 1343 this(null, description, id); 1344 } 1345 1346 private QueueItem(Object queueItem, MediaDescriptionCompat description, long id) { 1347 if (description == null) { 1348 throw new IllegalArgumentException("Description cannot be null."); 1349 } 1350 if (id == UNKNOWN_ID) { 1351 throw new IllegalArgumentException("Id cannot be QueueItem.UNKNOWN_ID"); 1352 } 1353 mDescription = description; 1354 mId = id; 1355 mItem = queueItem; 1356 } 1357 1358 QueueItem(Parcel in) { 1359 mDescription = MediaDescriptionCompat.CREATOR.createFromParcel(in); 1360 mId = in.readLong(); 1361 } 1362 1363 /** 1364 * Get the description for this item. 1365 */ 1366 public MediaDescriptionCompat getDescription() { 1367 return mDescription; 1368 } 1369 1370 /** 1371 * Get the queue id for this item. 1372 */ 1373 public long getQueueId() { 1374 return mId; 1375 } 1376 1377 @Override 1378 public void writeToParcel(Parcel dest, int flags) { 1379 mDescription.writeToParcel(dest, flags); 1380 dest.writeLong(mId); 1381 } 1382 1383 @Override 1384 public int describeContents() { 1385 return 0; 1386 } 1387 1388 /** 1389 * Get the underlying 1390 * {@link android.media.session.MediaSession.QueueItem}. 1391 * <p> 1392 * On builds before {@link android.os.Build.VERSION_CODES#LOLLIPOP} null 1393 * is returned. 1394 * 1395 * @return The underlying 1396 * {@link android.media.session.MediaSession.QueueItem} or null. 1397 */ 1398 public Object getQueueItem() { 1399 if (mItem != null || android.os.Build.VERSION.SDK_INT < 21) { 1400 return mItem; 1401 } 1402 mItem = MediaSessionCompatApi21.QueueItem.createItem(mDescription.getMediaDescription(), 1403 mId); 1404 return mItem; 1405 } 1406 1407 /** 1408 * Creates an instance from a framework {@link android.media.session.MediaSession.QueueItem} 1409 * object. 1410 * <p> 1411 * This method is only supported on API 21+. On API 20 and below, it returns null. 1412 * </p> 1413 * 1414 * @param queueItem A {@link android.media.session.MediaSession.QueueItem} object. 1415 * @return An equivalent {@link QueueItem} object, or null if none. 1416 * @deprecated Use {@link #fromQueueItem(Object)} instead. 1417 */ 1418 @Deprecated 1419 public static QueueItem obtain(Object queueItem) { 1420 return fromQueueItem(queueItem); 1421 } 1422 1423 /** 1424 * Creates an instance from a framework {@link android.media.session.MediaSession.QueueItem} 1425 * object. 1426 * <p> 1427 * This method is only supported on API 21+. On API 20 and below, it returns null. 1428 * </p> 1429 * 1430 * @param queueItem A {@link android.media.session.MediaSession.QueueItem} object. 1431 * @return An equivalent {@link QueueItem} object, or null if none. 1432 */ 1433 public static QueueItem fromQueueItem(Object queueItem) { 1434 if (queueItem == null || Build.VERSION.SDK_INT < 21) { 1435 return null; 1436 } 1437 Object descriptionObj = MediaSessionCompatApi21.QueueItem.getDescription(queueItem); 1438 MediaDescriptionCompat description = MediaDescriptionCompat.fromMediaDescription( 1439 descriptionObj); 1440 long id = MediaSessionCompatApi21.QueueItem.getQueueId(queueItem); 1441 return new QueueItem(queueItem, description, id); 1442 } 1443 1444 /** 1445 * Creates a list of {@link QueueItem} objects from a framework 1446 * {@link android.media.session.MediaSession.QueueItem} object list. 1447 * <p> 1448 * This method is only supported on API 21+. On API 20 and below, it returns null. 1449 * </p> 1450 * 1451 * @param itemList A list of {@link android.media.session.MediaSession.QueueItem} objects. 1452 * @return An equivalent list of {@link QueueItem} objects, or null if none. 1453 */ 1454 public static List<QueueItem> fromQueueItemList(List<?> itemList) { 1455 if (itemList == null || Build.VERSION.SDK_INT < 21) { 1456 return null; 1457 } 1458 List<QueueItem> items = new ArrayList<>(); 1459 for (Object itemObj : itemList) { 1460 items.add(fromQueueItem(itemObj)); 1461 } 1462 return items; 1463 } 1464 1465 public static final Creator<MediaSessionCompat.QueueItem> CREATOR 1466 = new Creator<MediaSessionCompat.QueueItem>() { 1467 1468 @Override 1469 public MediaSessionCompat.QueueItem createFromParcel(Parcel p) { 1470 return new MediaSessionCompat.QueueItem(p); 1471 } 1472 1473 @Override 1474 public MediaSessionCompat.QueueItem[] newArray(int size) { 1475 return new MediaSessionCompat.QueueItem[size]; 1476 } 1477 }; 1478 1479 @Override 1480 public String toString() { 1481 return "MediaSession.QueueItem {" + 1482 "Description=" + mDescription + 1483 ", Id=" + mId + " }"; 1484 } 1485 } 1486 1487 /** 1488 * This is a wrapper for {@link ResultReceiver} for sending over aidl 1489 * interfaces. The framework version was not exposed to aidls until 1490 * {@link android.os.Build.VERSION_CODES#LOLLIPOP}. 1491 */ 1492 static final class ResultReceiverWrapper implements Parcelable { 1493 private ResultReceiver mResultReceiver; 1494 1495 public ResultReceiverWrapper(ResultReceiver resultReceiver) { 1496 mResultReceiver = resultReceiver; 1497 } 1498 1499 ResultReceiverWrapper(Parcel in) { 1500 mResultReceiver = ResultReceiver.CREATOR.createFromParcel(in); 1501 } 1502 1503 public static final Creator<ResultReceiverWrapper> 1504 CREATOR = new Creator<ResultReceiverWrapper>() { 1505 @Override 1506 public ResultReceiverWrapper createFromParcel(Parcel p) { 1507 return new ResultReceiverWrapper(p); 1508 } 1509 1510 @Override 1511 public ResultReceiverWrapper[] newArray(int size) { 1512 return new ResultReceiverWrapper[size]; 1513 } 1514 }; 1515 1516 @Override 1517 public int describeContents() { 1518 return 0; 1519 } 1520 1521 @Override 1522 public void writeToParcel(Parcel dest, int flags) { 1523 mResultReceiver.writeToParcel(dest, flags); 1524 } 1525 } 1526 1527 public interface OnActiveChangeListener { 1528 void onActiveChanged(); 1529 } 1530 1531 interface MediaSessionImpl { 1532 void setCallback(Callback callback, Handler handler); 1533 void setFlags(@SessionFlags int flags); 1534 void setPlaybackToLocal(int stream); 1535 void setPlaybackToRemote(VolumeProviderCompat volumeProvider); 1536 void setActive(boolean active); 1537 boolean isActive(); 1538 void sendSessionEvent(String event, Bundle extras); 1539 void release(); 1540 Token getSessionToken(); 1541 void setPlaybackState(PlaybackStateCompat state); 1542 void setMetadata(MediaMetadataCompat metadata); 1543 1544 void setSessionActivity(PendingIntent pi); 1545 1546 void setMediaButtonReceiver(PendingIntent mbr); 1547 void setQueue(List<QueueItem> queue); 1548 void setQueueTitle(CharSequence title); 1549 1550 void setRatingType(@RatingCompat.Style int type); 1551 void setRepeatMode(@PlaybackStateCompat.RepeatMode int repeatMode); 1552 void setShuffleModeEnabled(boolean enabled); 1553 void setExtras(Bundle extras); 1554 1555 Object getMediaSession(); 1556 1557 Object getRemoteControlClient(); 1558 1559 String getCallingPackage(); 1560 } 1561 1562 static class MediaSessionImplBase implements MediaSessionImpl { 1563 /***** RemoteControlClient States, we only need none as the others were public *******/ 1564 static final int RCC_PLAYSTATE_NONE = 0; 1565 1566 private final Context mContext; 1567 private final ComponentName mMediaButtonReceiverComponentName; 1568 private final PendingIntent mMediaButtonReceiverIntent; 1569 private final MediaSessionStub mStub; 1570 private final Token mToken; 1571 final String mPackageName; 1572 final String mTag; 1573 final AudioManager mAudioManager; 1574 final RemoteControlClient mRcc; 1575 1576 final Object mLock = new Object(); 1577 final RemoteCallbackList<IMediaControllerCallback> mControllerCallbacks 1578 = new RemoteCallbackList<>(); 1579 1580 private MessageHandler mHandler; 1581 boolean mDestroyed = false; 1582 boolean mIsActive = false; 1583 private boolean mIsMbrRegistered = false; 1584 private boolean mIsRccRegistered = false; 1585 volatile Callback mCallback; 1586 1587 @SessionFlags int mFlags; 1588 1589 MediaMetadataCompat mMetadata; 1590 PlaybackStateCompat mState; 1591 PendingIntent mSessionActivity; 1592 List<QueueItem> mQueue; 1593 CharSequence mQueueTitle; 1594 @RatingCompat.Style int mRatingType; 1595 @PlaybackStateCompat.RepeatMode int mRepeatMode; 1596 boolean mShuffleModeEnabled; 1597 Bundle mExtras; 1598 1599 int mVolumeType; 1600 int mLocalStream; 1601 VolumeProviderCompat mVolumeProvider; 1602 1603 private VolumeProviderCompat.Callback mVolumeCallback 1604 = new VolumeProviderCompat.Callback() { 1605 @Override 1606 public void onVolumeChanged(VolumeProviderCompat volumeProvider) { 1607 if (mVolumeProvider != volumeProvider) { 1608 return; 1609 } 1610 ParcelableVolumeInfo info = new ParcelableVolumeInfo(mVolumeType, mLocalStream, 1611 volumeProvider.getVolumeControl(), volumeProvider.getMaxVolume(), 1612 volumeProvider.getCurrentVolume()); 1613 sendVolumeInfoChanged(info); 1614 } 1615 }; 1616 1617 public MediaSessionImplBase(Context context, String tag, ComponentName mbrComponent, 1618 PendingIntent mbrIntent) { 1619 if (mbrComponent == null) { 1620 throw new IllegalArgumentException( 1621 "MediaButtonReceiver component may not be null."); 1622 } 1623 mContext = context; 1624 mPackageName = context.getPackageName(); 1625 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); 1626 mTag = tag; 1627 mMediaButtonReceiverComponentName = mbrComponent; 1628 mMediaButtonReceiverIntent = mbrIntent; 1629 mStub = new MediaSessionStub(); 1630 mToken = new Token(mStub); 1631 1632 mRatingType = RatingCompat.RATING_NONE; 1633 mVolumeType = MediaControllerCompat.PlaybackInfo.PLAYBACK_TYPE_LOCAL; 1634 mLocalStream = AudioManager.STREAM_MUSIC; 1635 mRcc = new RemoteControlClient(mbrIntent); 1636 } 1637 1638 @Override 1639 public void setCallback(Callback callback, Handler handler) { 1640 mCallback = callback; 1641 if (callback != null) { 1642 if (handler == null) { 1643 handler = new Handler(); 1644 } 1645 synchronized (mLock) { 1646 mHandler = new MessageHandler(handler.getLooper()); 1647 } 1648 } 1649 } 1650 1651 void postToHandler(int what) { 1652 postToHandler(what, null); 1653 } 1654 1655 void postToHandler(int what, int arg1) { 1656 postToHandler(what, null, arg1); 1657 } 1658 1659 void postToHandler(int what, Object obj) { 1660 postToHandler(what, obj, null); 1661 } 1662 1663 void postToHandler(int what, Object obj, int arg1) { 1664 synchronized (mLock) { 1665 if (mHandler != null) { 1666 mHandler.post(what, obj, arg1); 1667 } 1668 } 1669 } 1670 1671 void postToHandler(int what, Object obj, Bundle extras) { 1672 synchronized (mLock) { 1673 if (mHandler != null) { 1674 mHandler.post(what, obj, extras); 1675 } 1676 } 1677 } 1678 1679 @Override 1680 public void setFlags(@SessionFlags int flags) { 1681 synchronized (mLock) { 1682 mFlags = flags; 1683 } 1684 update(); 1685 } 1686 1687 @Override 1688 public void setPlaybackToLocal(int stream) { 1689 if (mVolumeProvider != null) { 1690 mVolumeProvider.setCallback(null); 1691 } 1692 mVolumeType = MediaControllerCompat.PlaybackInfo.PLAYBACK_TYPE_LOCAL; 1693 ParcelableVolumeInfo info = new ParcelableVolumeInfo(mVolumeType, mLocalStream, 1694 VolumeProviderCompat.VOLUME_CONTROL_ABSOLUTE, 1695 mAudioManager.getStreamMaxVolume(mLocalStream), 1696 mAudioManager.getStreamVolume(mLocalStream)); 1697 sendVolumeInfoChanged(info); 1698 } 1699 1700 @Override 1701 public void setPlaybackToRemote(VolumeProviderCompat volumeProvider) { 1702 if (volumeProvider == null) { 1703 throw new IllegalArgumentException("volumeProvider may not be null"); 1704 } 1705 if (mVolumeProvider != null) { 1706 mVolumeProvider.setCallback(null); 1707 } 1708 mVolumeType = MediaControllerCompat.PlaybackInfo.PLAYBACK_TYPE_REMOTE; 1709 mVolumeProvider = volumeProvider; 1710 ParcelableVolumeInfo info = new ParcelableVolumeInfo(mVolumeType, mLocalStream, 1711 mVolumeProvider.getVolumeControl(), mVolumeProvider.getMaxVolume(), 1712 mVolumeProvider.getCurrentVolume()); 1713 sendVolumeInfoChanged(info); 1714 1715 volumeProvider.setCallback(mVolumeCallback); 1716 } 1717 1718 @Override 1719 public void setActive(boolean active) { 1720 if (active == mIsActive) { 1721 return; 1722 } 1723 mIsActive = active; 1724 if (update()) { 1725 setMetadata(mMetadata); 1726 setPlaybackState(mState); 1727 } 1728 } 1729 1730 @Override 1731 public boolean isActive() { 1732 return mIsActive; 1733 } 1734 1735 @Override 1736 public void sendSessionEvent(String event, Bundle extras) { 1737 sendEvent(event, extras); 1738 } 1739 1740 @Override 1741 public void release() { 1742 mIsActive = false; 1743 mDestroyed = true; 1744 update(); 1745 sendSessionDestroyed(); 1746 } 1747 1748 @Override 1749 public Token getSessionToken() { 1750 return mToken; 1751 } 1752 1753 @Override 1754 public void setPlaybackState(PlaybackStateCompat state) { 1755 synchronized (mLock) { 1756 mState = state; 1757 } 1758 sendState(state); 1759 if (!mIsActive) { 1760 // Don't set the state until after the RCC is registered 1761 return; 1762 } 1763 if (state == null) { 1764 mRcc.setPlaybackState(0); 1765 mRcc.setTransportControlFlags(0); 1766 } else { 1767 // Set state 1768 setRccState(state); 1769 1770 // Set transport control flags 1771 mRcc.setTransportControlFlags( 1772 getRccTransportControlFlagsFromActions(state.getActions())); 1773 } 1774 } 1775 1776 void setRccState(PlaybackStateCompat state) { 1777 mRcc.setPlaybackState(getRccStateFromState(state.getState())); 1778 } 1779 1780 int getRccStateFromState(int state) { 1781 switch (state) { 1782 case PlaybackStateCompat.STATE_CONNECTING: 1783 case PlaybackStateCompat.STATE_BUFFERING: 1784 return RemoteControlClient.PLAYSTATE_BUFFERING; 1785 case PlaybackStateCompat.STATE_ERROR: 1786 return RemoteControlClient.PLAYSTATE_ERROR; 1787 case PlaybackStateCompat.STATE_FAST_FORWARDING: 1788 return RemoteControlClient.PLAYSTATE_FAST_FORWARDING; 1789 case PlaybackStateCompat.STATE_NONE: 1790 return RCC_PLAYSTATE_NONE; 1791 case PlaybackStateCompat.STATE_PAUSED: 1792 return RemoteControlClient.PLAYSTATE_PAUSED; 1793 case PlaybackStateCompat.STATE_PLAYING: 1794 return RemoteControlClient.PLAYSTATE_PLAYING; 1795 case PlaybackStateCompat.STATE_REWINDING: 1796 return RemoteControlClient.PLAYSTATE_REWINDING; 1797 case PlaybackStateCompat.STATE_SKIPPING_TO_PREVIOUS: 1798 return RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS; 1799 case PlaybackStateCompat.STATE_SKIPPING_TO_NEXT: 1800 case PlaybackStateCompat.STATE_SKIPPING_TO_QUEUE_ITEM: 1801 return RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS; 1802 case PlaybackStateCompat.STATE_STOPPED: 1803 return RemoteControlClient.PLAYSTATE_STOPPED; 1804 default: 1805 return -1; 1806 } 1807 } 1808 1809 int getRccTransportControlFlagsFromActions(long actions) { 1810 int transportControlFlags = 0; 1811 if ((actions & PlaybackStateCompat.ACTION_STOP) != 0) { 1812 transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_STOP; 1813 } 1814 if ((actions & PlaybackStateCompat.ACTION_PAUSE) != 0) { 1815 transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_PAUSE; 1816 } 1817 if ((actions & PlaybackStateCompat.ACTION_PLAY) != 0) { 1818 transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_PLAY; 1819 } 1820 if ((actions & PlaybackStateCompat.ACTION_REWIND) != 0) { 1821 transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_REWIND; 1822 } 1823 if ((actions & PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS) != 0) { 1824 transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS; 1825 } 1826 if ((actions & PlaybackStateCompat.ACTION_SKIP_TO_NEXT) != 0) { 1827 transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_NEXT; 1828 } 1829 if ((actions & PlaybackStateCompat.ACTION_FAST_FORWARD) != 0) { 1830 transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_FAST_FORWARD; 1831 } 1832 if ((actions & PlaybackStateCompat.ACTION_PLAY_PAUSE) != 0) { 1833 transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE; 1834 } 1835 return transportControlFlags; 1836 } 1837 1838 @Override 1839 public void setMetadata(MediaMetadataCompat metadata) { 1840 if (metadata != null) { 1841 // Clones the given {@link MediaMetadataCompat}, deep-copying bitmaps in the 1842 // metadata if necessary. Bitmaps can be scaled down if they are large. 1843 metadata = new MediaMetadataCompat.Builder(metadata, sMaxBitmapSize).build(); 1844 } 1845 1846 synchronized (mLock) { 1847 mMetadata = metadata; 1848 } 1849 sendMetadata(metadata); 1850 if (!mIsActive) { 1851 // Don't set metadata until after the rcc has been registered 1852 return; 1853 } 1854 RemoteControlClient.MetadataEditor editor = buildRccMetadata( 1855 metadata == null ? null : metadata.getBundle()); 1856 editor.apply(); 1857 } 1858 1859 RemoteControlClient.MetadataEditor buildRccMetadata(Bundle metadata) { 1860 RemoteControlClient.MetadataEditor editor = mRcc.editMetadata(true); 1861 if (metadata == null) { 1862 return editor; 1863 } 1864 if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_ART)) { 1865 Bitmap art = metadata.getParcelable(MediaMetadataCompat.METADATA_KEY_ART); 1866 editor.putBitmap(RemoteControlClient.MetadataEditor.BITMAP_KEY_ARTWORK, art); 1867 } else if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_ALBUM_ART)) { 1868 // Fall back to album art if the track art wasn't available 1869 Bitmap art = metadata.getParcelable(MediaMetadataCompat.METADATA_KEY_ALBUM_ART); 1870 editor.putBitmap(RemoteControlClient.MetadataEditor.BITMAP_KEY_ARTWORK, art); 1871 } 1872 if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_ALBUM)) { 1873 editor.putString(MediaMetadataRetriever.METADATA_KEY_ALBUM, 1874 metadata.getString(MediaMetadataCompat.METADATA_KEY_ALBUM)); 1875 } 1876 if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_ALBUM_ARTIST)) { 1877 editor.putString(MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST, 1878 metadata.getString(MediaMetadataCompat.METADATA_KEY_ALBUM_ARTIST)); 1879 } 1880 if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_ARTIST)) { 1881 editor.putString(MediaMetadataRetriever.METADATA_KEY_ARTIST, 1882 metadata.getString(MediaMetadataCompat.METADATA_KEY_ARTIST)); 1883 } 1884 if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_AUTHOR)) { 1885 editor.putString(MediaMetadataRetriever.METADATA_KEY_AUTHOR, 1886 metadata.getString(MediaMetadataCompat.METADATA_KEY_AUTHOR)); 1887 } 1888 if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_COMPILATION)) { 1889 editor.putString(MediaMetadataRetriever.METADATA_KEY_COMPILATION, 1890 metadata.getString(MediaMetadataCompat.METADATA_KEY_COMPILATION)); 1891 } 1892 if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_COMPOSER)) { 1893 editor.putString(MediaMetadataRetriever.METADATA_KEY_COMPOSER, 1894 metadata.getString(MediaMetadataCompat.METADATA_KEY_COMPOSER)); 1895 } 1896 if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_DATE)) { 1897 editor.putString(MediaMetadataRetriever.METADATA_KEY_DATE, 1898 metadata.getString(MediaMetadataCompat.METADATA_KEY_DATE)); 1899 } 1900 if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_DISC_NUMBER)) { 1901 editor.putLong(MediaMetadataRetriever.METADATA_KEY_DISC_NUMBER, 1902 metadata.getLong(MediaMetadataCompat.METADATA_KEY_DISC_NUMBER)); 1903 } 1904 if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_DURATION)) { 1905 editor.putLong(MediaMetadataRetriever.METADATA_KEY_DURATION, 1906 metadata.getLong(MediaMetadataCompat.METADATA_KEY_DURATION)); 1907 } 1908 if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_GENRE)) { 1909 editor.putString(MediaMetadataRetriever.METADATA_KEY_GENRE, 1910 metadata.getString(MediaMetadataCompat.METADATA_KEY_GENRE)); 1911 } 1912 if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_TITLE)) { 1913 editor.putString(MediaMetadataRetriever.METADATA_KEY_TITLE, 1914 metadata.getString(MediaMetadataCompat.METADATA_KEY_TITLE)); 1915 } 1916 if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER)) { 1917 editor.putLong(MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER, 1918 metadata.getLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER)); 1919 } 1920 if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_WRITER)) { 1921 editor.putString(MediaMetadataRetriever.METADATA_KEY_WRITER, 1922 metadata.getString(MediaMetadataCompat.METADATA_KEY_WRITER)); 1923 } 1924 return editor; 1925 } 1926 1927 @Override 1928 public void setSessionActivity(PendingIntent pi) { 1929 synchronized (mLock) { 1930 mSessionActivity = pi; 1931 } 1932 } 1933 1934 @Override 1935 public void setMediaButtonReceiver(PendingIntent mbr) { 1936 // Do nothing, changing this is not supported before API 21. 1937 } 1938 1939 @Override 1940 public void setQueue(List<QueueItem> queue) { 1941 mQueue = queue; 1942 sendQueue(queue); 1943 } 1944 1945 @Override 1946 public void setQueueTitle(CharSequence title) { 1947 mQueueTitle = title; 1948 sendQueueTitle(title); 1949 } 1950 1951 @Override 1952 public Object getMediaSession() { 1953 return null; 1954 } 1955 1956 @Override 1957 public Object getRemoteControlClient() { 1958 return null; 1959 } 1960 1961 @Override 1962 public String getCallingPackage() { 1963 return null; 1964 } 1965 1966 @Override 1967 public void setRatingType(@RatingCompat.Style int type) { 1968 mRatingType = type; 1969 } 1970 1971 @Override 1972 public void setRepeatMode(@PlaybackStateCompat.RepeatMode int repeatMode) { 1973 if (mRepeatMode != repeatMode) { 1974 mRepeatMode = repeatMode; 1975 sendRepeatMode(repeatMode); 1976 } 1977 } 1978 1979 @Override 1980 public void setShuffleModeEnabled(boolean enabled) { 1981 if (mShuffleModeEnabled != enabled) { 1982 mShuffleModeEnabled = enabled; 1983 sendShuffleModeEnabled(enabled); 1984 } 1985 } 1986 1987 @Override 1988 public void setExtras(Bundle extras) { 1989 mExtras = extras; 1990 sendExtras(extras); 1991 } 1992 1993 // Registers/unregisters components as needed. 1994 boolean update() { 1995 boolean registeredRcc = false; 1996 if (mIsActive) { 1997 // Register a MBR if it's supported, unregister it if support was removed. 1998 if (!mIsMbrRegistered && (mFlags & FLAG_HANDLES_MEDIA_BUTTONS) != 0) { 1999 registerMediaButtonEventReceiver(mMediaButtonReceiverIntent, 2000 mMediaButtonReceiverComponentName); 2001 mIsMbrRegistered = true; 2002 } else if (mIsMbrRegistered && (mFlags & FLAG_HANDLES_MEDIA_BUTTONS) == 0) { 2003 unregisterMediaButtonEventReceiver(mMediaButtonReceiverIntent, 2004 mMediaButtonReceiverComponentName); 2005 mIsMbrRegistered = false; 2006 } 2007 // Register a RCC if it's supported, unregister it if support was removed. 2008 if (!mIsRccRegistered && (mFlags & FLAG_HANDLES_TRANSPORT_CONTROLS) != 0) { 2009 mAudioManager.registerRemoteControlClient(mRcc); 2010 mIsRccRegistered = true; 2011 registeredRcc = true; 2012 } else if (mIsRccRegistered 2013 && (mFlags & FLAG_HANDLES_TRANSPORT_CONTROLS) == 0) { 2014 // RCC keeps the state while the system resets its state internally when 2015 // we register RCC. Reset the state so that the states in RCC and the system 2016 // are in sync when we re-register the RCC. 2017 mRcc.setPlaybackState(0); 2018 mAudioManager.unregisterRemoteControlClient(mRcc); 2019 mIsRccRegistered = false; 2020 } 2021 } else { 2022 // When inactive remove any registered components. 2023 if (mIsMbrRegistered) { 2024 unregisterMediaButtonEventReceiver(mMediaButtonReceiverIntent, 2025 mMediaButtonReceiverComponentName); 2026 mIsMbrRegistered = false; 2027 } 2028 if (mIsRccRegistered) { 2029 // RCC keeps the state while the system resets its state internally when 2030 // we register RCC. Reset the state so that the states in RCC and the system 2031 // are in sync when we re-register the RCC. 2032 mRcc.setPlaybackState(0); 2033 mAudioManager.unregisterRemoteControlClient(mRcc); 2034 mIsRccRegistered = false; 2035 } 2036 } 2037 return registeredRcc; 2038 } 2039 2040 void registerMediaButtonEventReceiver(PendingIntent mbrIntent, ComponentName mbrComponent) { 2041 mAudioManager.registerMediaButtonEventReceiver(mbrComponent); 2042 } 2043 2044 void unregisterMediaButtonEventReceiver(PendingIntent mbrIntent, 2045 ComponentName mbrComponent) { 2046 mAudioManager.unregisterMediaButtonEventReceiver(mbrComponent); 2047 } 2048 2049 void adjustVolume(int direction, int flags) { 2050 if (mVolumeType == MediaControllerCompat.PlaybackInfo.PLAYBACK_TYPE_REMOTE) { 2051 if (mVolumeProvider != null) { 2052 mVolumeProvider.onAdjustVolume(direction); 2053 } 2054 } else { 2055 mAudioManager.adjustStreamVolume(mLocalStream, direction, flags); 2056 } 2057 } 2058 2059 void setVolumeTo(int value, int flags) { 2060 if (mVolumeType == MediaControllerCompat.PlaybackInfo.PLAYBACK_TYPE_REMOTE) { 2061 if (mVolumeProvider != null) { 2062 mVolumeProvider.onSetVolumeTo(value); 2063 } 2064 } else { 2065 mAudioManager.setStreamVolume(mLocalStream, value, flags); 2066 } 2067 } 2068 2069 PlaybackStateCompat getStateWithUpdatedPosition() { 2070 PlaybackStateCompat state; 2071 long duration = -1; 2072 synchronized (mLock) { 2073 state = mState; 2074 if (mMetadata != null 2075 && mMetadata.containsKey(MediaMetadataCompat.METADATA_KEY_DURATION)) { 2076 duration = mMetadata.getLong(MediaMetadataCompat.METADATA_KEY_DURATION); 2077 } 2078 } 2079 2080 PlaybackStateCompat result = null; 2081 if (state != null) { 2082 if (state.getState() == PlaybackStateCompat.STATE_PLAYING 2083 || state.getState() == PlaybackStateCompat.STATE_FAST_FORWARDING 2084 || state.getState() == PlaybackStateCompat.STATE_REWINDING) { 2085 long updateTime = state.getLastPositionUpdateTime(); 2086 long currentTime = SystemClock.elapsedRealtime(); 2087 if (updateTime > 0) { 2088 long position = (long) (state.getPlaybackSpeed() 2089 * (currentTime - updateTime)) + state.getPosition(); 2090 if (duration >= 0 && position > duration) { 2091 position = duration; 2092 } else if (position < 0) { 2093 position = 0; 2094 } 2095 PlaybackStateCompat.Builder builder = new PlaybackStateCompat.Builder( 2096 state); 2097 builder.setState(state.getState(), position, state.getPlaybackSpeed(), 2098 currentTime); 2099 result = builder.build(); 2100 } 2101 } 2102 } 2103 return result == null ? state : result; 2104 } 2105 2106 void sendVolumeInfoChanged(ParcelableVolumeInfo info) { 2107 int size = mControllerCallbacks.beginBroadcast(); 2108 for (int i = size - 1; i >= 0; i--) { 2109 IMediaControllerCallback cb = mControllerCallbacks.getBroadcastItem(i); 2110 try { 2111 cb.onVolumeInfoChanged(info); 2112 } catch (RemoteException e) { 2113 } 2114 } 2115 mControllerCallbacks.finishBroadcast(); 2116 } 2117 2118 private void sendSessionDestroyed() { 2119 int size = mControllerCallbacks.beginBroadcast(); 2120 for (int i = size - 1; i >= 0; i--) { 2121 IMediaControllerCallback cb = mControllerCallbacks.getBroadcastItem(i); 2122 try { 2123 cb.onSessionDestroyed(); 2124 } catch (RemoteException e) { 2125 } 2126 } 2127 mControllerCallbacks.finishBroadcast(); 2128 mControllerCallbacks.kill(); 2129 } 2130 2131 private void sendEvent(String event, Bundle extras) { 2132 int size = mControllerCallbacks.beginBroadcast(); 2133 for (int i = size - 1; i >= 0; i--) { 2134 IMediaControllerCallback cb = mControllerCallbacks.getBroadcastItem(i); 2135 try { 2136 cb.onEvent(event, extras); 2137 } catch (RemoteException e) { 2138 } 2139 } 2140 mControllerCallbacks.finishBroadcast(); 2141 } 2142 2143 private void sendState(PlaybackStateCompat state) { 2144 int size = mControllerCallbacks.beginBroadcast(); 2145 for (int i = size - 1; i >= 0; i--) { 2146 IMediaControllerCallback cb = mControllerCallbacks.getBroadcastItem(i); 2147 try { 2148 cb.onPlaybackStateChanged(state); 2149 } catch (RemoteException e) { 2150 } 2151 } 2152 mControllerCallbacks.finishBroadcast(); 2153 } 2154 2155 private void sendMetadata(MediaMetadataCompat metadata) { 2156 int size = mControllerCallbacks.beginBroadcast(); 2157 for (int i = size - 1; i >= 0; i--) { 2158 IMediaControllerCallback cb = mControllerCallbacks.getBroadcastItem(i); 2159 try { 2160 cb.onMetadataChanged(metadata); 2161 } catch (RemoteException e) { 2162 } 2163 } 2164 mControllerCallbacks.finishBroadcast(); 2165 } 2166 2167 private void sendQueue(List<QueueItem> queue) { 2168 int size = mControllerCallbacks.beginBroadcast(); 2169 for (int i = size - 1; i >= 0; i--) { 2170 IMediaControllerCallback cb = mControllerCallbacks.getBroadcastItem(i); 2171 try { 2172 cb.onQueueChanged(queue); 2173 } catch (RemoteException e) { 2174 } 2175 } 2176 mControllerCallbacks.finishBroadcast(); 2177 } 2178 2179 private void sendQueueTitle(CharSequence queueTitle) { 2180 int size = mControllerCallbacks.beginBroadcast(); 2181 for (int i = size - 1; i >= 0; i--) { 2182 IMediaControllerCallback cb = mControllerCallbacks.getBroadcastItem(i); 2183 try { 2184 cb.onQueueTitleChanged(queueTitle); 2185 } catch (RemoteException e) { 2186 } 2187 } 2188 mControllerCallbacks.finishBroadcast(); 2189 } 2190 2191 private void sendRepeatMode(int repeatMode) { 2192 int size = mControllerCallbacks.beginBroadcast(); 2193 for (int i = size - 1; i >= 0; i--) { 2194 IMediaControllerCallback cb = mControllerCallbacks.getBroadcastItem(i); 2195 try { 2196 cb.onRepeatModeChanged(repeatMode); 2197 } catch (RemoteException e) { 2198 } 2199 } 2200 mControllerCallbacks.finishBroadcast(); 2201 } 2202 2203 private void sendShuffleModeEnabled(boolean enabled) { 2204 int size = mControllerCallbacks.beginBroadcast(); 2205 for (int i = size - 1; i >= 0; i--) { 2206 IMediaControllerCallback cb = mControllerCallbacks.getBroadcastItem(i); 2207 try { 2208 cb.onShuffleModeChanged(enabled); 2209 } catch (RemoteException e) { 2210 } 2211 } 2212 mControllerCallbacks.finishBroadcast(); 2213 } 2214 2215 private void sendExtras(Bundle extras) { 2216 int size = mControllerCallbacks.beginBroadcast(); 2217 for (int i = size - 1; i >= 0; i--) { 2218 IMediaControllerCallback cb = mControllerCallbacks.getBroadcastItem(i); 2219 try { 2220 cb.onExtrasChanged(extras); 2221 } catch (RemoteException e) { 2222 } 2223 } 2224 mControllerCallbacks.finishBroadcast(); 2225 } 2226 2227 class MediaSessionStub extends IMediaSession.Stub { 2228 @Override 2229 public void sendCommand(String command, Bundle args, ResultReceiverWrapper cb) { 2230 postToHandler(MessageHandler.MSG_COMMAND, 2231 new Command(command, args, cb.mResultReceiver)); 2232 } 2233 2234 @Override 2235 public boolean sendMediaButton(KeyEvent mediaButton) { 2236 boolean handlesMediaButtons = 2237 (mFlags & MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS) != 0; 2238 if (handlesMediaButtons) { 2239 postToHandler(MessageHandler.MSG_MEDIA_BUTTON, mediaButton); 2240 } 2241 return handlesMediaButtons; 2242 } 2243 2244 @Override 2245 public void registerCallbackListener(IMediaControllerCallback cb) { 2246 // If this session is already destroyed tell the caller and 2247 // don't add them. 2248 if (mDestroyed) { 2249 try { 2250 cb.onSessionDestroyed(); 2251 } catch (Exception e) { 2252 // ignored 2253 } 2254 return; 2255 } 2256 mControllerCallbacks.register(cb); 2257 } 2258 2259 @Override 2260 public void unregisterCallbackListener(IMediaControllerCallback cb) { 2261 mControllerCallbacks.unregister(cb); 2262 } 2263 2264 @Override 2265 public String getPackageName() { 2266 // mPackageName is final so doesn't need synchronize block 2267 return mPackageName; 2268 } 2269 2270 @Override 2271 public String getTag() { 2272 // mTag is final so doesn't need synchronize block 2273 return mTag; 2274 } 2275 2276 @Override 2277 public PendingIntent getLaunchPendingIntent() { 2278 synchronized (mLock) { 2279 return mSessionActivity; 2280 } 2281 } 2282 2283 @Override 2284 @SessionFlags 2285 public long getFlags() { 2286 synchronized (mLock) { 2287 return mFlags; 2288 } 2289 } 2290 2291 @Override 2292 public ParcelableVolumeInfo getVolumeAttributes() { 2293 int controlType; 2294 int max; 2295 int current; 2296 int stream; 2297 int volumeType; 2298 synchronized (mLock) { 2299 volumeType = mVolumeType; 2300 stream = mLocalStream; 2301 VolumeProviderCompat vp = mVolumeProvider; 2302 if (volumeType == MediaControllerCompat.PlaybackInfo.PLAYBACK_TYPE_REMOTE) { 2303 controlType = vp.getVolumeControl(); 2304 max = vp.getMaxVolume(); 2305 current = vp.getCurrentVolume(); 2306 } else { 2307 controlType = VolumeProviderCompat.VOLUME_CONTROL_ABSOLUTE; 2308 max = mAudioManager.getStreamMaxVolume(stream); 2309 current = mAudioManager.getStreamVolume(stream); 2310 } 2311 } 2312 return new ParcelableVolumeInfo(volumeType, stream, controlType, max, current); 2313 } 2314 2315 @Override 2316 public void adjustVolume(int direction, int flags, String packageName) { 2317 MediaSessionImplBase.this.adjustVolume(direction, flags); 2318 } 2319 2320 @Override 2321 public void setVolumeTo(int value, int flags, String packageName) { 2322 MediaSessionImplBase.this.setVolumeTo(value, flags); 2323 } 2324 2325 @Override 2326 public void prepare() throws RemoteException { 2327 postToHandler(MessageHandler.MSG_PREPARE); 2328 } 2329 2330 @Override 2331 public void prepareFromMediaId(String mediaId, Bundle extras) throws RemoteException { 2332 postToHandler(MessageHandler.MSG_PREPARE_MEDIA_ID, mediaId, extras); 2333 } 2334 2335 @Override 2336 public void prepareFromSearch(String query, Bundle extras) throws RemoteException { 2337 postToHandler(MessageHandler.MSG_PREPARE_SEARCH, query, extras); 2338 } 2339 2340 @Override 2341 public void prepareFromUri(Uri uri, Bundle extras) throws RemoteException { 2342 postToHandler(MessageHandler.MSG_PREPARE_URI, uri, extras); 2343 } 2344 2345 @Override 2346 public void play() throws RemoteException { 2347 postToHandler(MessageHandler.MSG_PLAY); 2348 } 2349 2350 @Override 2351 public void playFromMediaId(String mediaId, Bundle extras) throws RemoteException { 2352 postToHandler(MessageHandler.MSG_PLAY_MEDIA_ID, mediaId, extras); 2353 } 2354 2355 @Override 2356 public void playFromSearch(String query, Bundle extras) throws RemoteException { 2357 postToHandler(MessageHandler.MSG_PLAY_SEARCH, query, extras); 2358 } 2359 2360 @Override 2361 public void playFromUri(Uri uri, Bundle extras) throws RemoteException { 2362 postToHandler(MessageHandler.MSG_PLAY_URI, uri, extras); 2363 } 2364 2365 @Override 2366 public void skipToQueueItem(long id) { 2367 postToHandler(MessageHandler.MSG_SKIP_TO_ITEM, id); 2368 } 2369 2370 @Override 2371 public void pause() throws RemoteException { 2372 postToHandler(MessageHandler.MSG_PAUSE); 2373 } 2374 2375 @Override 2376 public void stop() throws RemoteException { 2377 postToHandler(MessageHandler.MSG_STOP); 2378 } 2379 2380 @Override 2381 public void next() throws RemoteException { 2382 postToHandler(MessageHandler.MSG_NEXT); 2383 } 2384 2385 @Override 2386 public void previous() throws RemoteException { 2387 postToHandler(MessageHandler.MSG_PREVIOUS); 2388 } 2389 2390 @Override 2391 public void fastForward() throws RemoteException { 2392 postToHandler(MessageHandler.MSG_FAST_FORWARD); 2393 } 2394 2395 @Override 2396 public void rewind() throws RemoteException { 2397 postToHandler(MessageHandler.MSG_REWIND); 2398 } 2399 2400 @Override 2401 public void seekTo(long pos) throws RemoteException { 2402 postToHandler(MessageHandler.MSG_SEEK_TO, pos); 2403 } 2404 2405 @Override 2406 public void rate(RatingCompat rating) throws RemoteException { 2407 postToHandler(MessageHandler.MSG_RATE, rating); 2408 } 2409 2410 @Override 2411 public void setRepeatMode(int repeatMode) throws RemoteException { 2412 postToHandler(MessageHandler.MSG_SET_REPEAT_MODE, repeatMode); 2413 } 2414 2415 @Override 2416 public void setShuffleModeEnabled(boolean enabled) throws RemoteException { 2417 postToHandler(MessageHandler.MSG_SET_SHUFFLE_MODE_ENABLED, enabled); 2418 } 2419 2420 @Override 2421 public void sendCustomAction(String action, Bundle args) 2422 throws RemoteException { 2423 postToHandler(MessageHandler.MSG_CUSTOM_ACTION, action, args); 2424 } 2425 2426 @Override 2427 public MediaMetadataCompat getMetadata() { 2428 return mMetadata; 2429 } 2430 2431 @Override 2432 public PlaybackStateCompat getPlaybackState() { 2433 return getStateWithUpdatedPosition(); 2434 } 2435 2436 @Override 2437 public List<QueueItem> getQueue() { 2438 synchronized (mLock) { 2439 return mQueue; 2440 } 2441 } 2442 2443 @Override 2444 public void addQueueItem(MediaDescriptionCompat description) { 2445 postToHandler(MessageHandler.MSG_ADD_QUEUE_ITEM, description); 2446 } 2447 2448 @Override 2449 public void addQueueItemAt(MediaDescriptionCompat description, int index) { 2450 postToHandler(MessageHandler.MSG_ADD_QUEUE_ITEM_AT, description, index); 2451 } 2452 2453 @Override 2454 public void removeQueueItem(MediaDescriptionCompat description) { 2455 postToHandler(MessageHandler.MSG_REMOVE_QUEUE_ITEM, description); 2456 } 2457 2458 @Override 2459 public void removeQueueItemAt(int index) { 2460 postToHandler(MessageHandler.MSG_REMOVE_QUEUE_ITEM_AT, index); 2461 } 2462 2463 @Override 2464 public CharSequence getQueueTitle() { 2465 return mQueueTitle; 2466 } 2467 2468 @Override 2469 public Bundle getExtras() { 2470 synchronized (mLock) { 2471 return mExtras; 2472 } 2473 } 2474 2475 @Override 2476 @RatingCompat.Style 2477 public int getRatingType() { 2478 return mRatingType; 2479 } 2480 2481 @Override 2482 @PlaybackStateCompat.RepeatMode 2483 public int getRepeatMode() { 2484 return mRepeatMode; 2485 } 2486 2487 @Override 2488 public boolean isShuffleModeEnabled() { 2489 return mShuffleModeEnabled; 2490 } 2491 2492 @Override 2493 public boolean isTransportControlEnabled() { 2494 return (mFlags & FLAG_HANDLES_TRANSPORT_CONTROLS) != 0; 2495 } 2496 } 2497 2498 private static final class Command { 2499 public final String command; 2500 public final Bundle extras; 2501 public final ResultReceiver stub; 2502 2503 public Command(String command, Bundle extras, ResultReceiver stub) { 2504 this.command = command; 2505 this.extras = extras; 2506 this.stub = stub; 2507 } 2508 } 2509 2510 class MessageHandler extends Handler { 2511 2512 private static final int MSG_COMMAND = 1; 2513 private static final int MSG_ADJUST_VOLUME = 2; 2514 private static final int MSG_PREPARE = 3; 2515 private static final int MSG_PREPARE_MEDIA_ID = 4; 2516 private static final int MSG_PREPARE_SEARCH = 5; 2517 private static final int MSG_PREPARE_URI = 6; 2518 private static final int MSG_PLAY = 7; 2519 private static final int MSG_PLAY_MEDIA_ID = 8; 2520 private static final int MSG_PLAY_SEARCH = 9; 2521 private static final int MSG_PLAY_URI = 10; 2522 private static final int MSG_SKIP_TO_ITEM = 11; 2523 private static final int MSG_PAUSE = 12; 2524 private static final int MSG_STOP = 13; 2525 private static final int MSG_NEXT = 14; 2526 private static final int MSG_PREVIOUS = 15; 2527 private static final int MSG_FAST_FORWARD = 16; 2528 private static final int MSG_REWIND = 17; 2529 private static final int MSG_SEEK_TO = 18; 2530 private static final int MSG_RATE = 19; 2531 private static final int MSG_CUSTOM_ACTION = 20; 2532 private static final int MSG_MEDIA_BUTTON = 21; 2533 private static final int MSG_SET_VOLUME = 22; 2534 private static final int MSG_SET_REPEAT_MODE = 23; 2535 private static final int MSG_SET_SHUFFLE_MODE_ENABLED = 24; 2536 private static final int MSG_ADD_QUEUE_ITEM = 25; 2537 private static final int MSG_ADD_QUEUE_ITEM_AT = 26; 2538 private static final int MSG_REMOVE_QUEUE_ITEM = 27; 2539 private static final int MSG_REMOVE_QUEUE_ITEM_AT = 28; 2540 2541 // KeyEvent constants only available on API 11+ 2542 private static final int KEYCODE_MEDIA_PAUSE = 127; 2543 private static final int KEYCODE_MEDIA_PLAY = 126; 2544 2545 public MessageHandler(Looper looper) { 2546 super(looper); 2547 } 2548 2549 public void post(int what, Object obj, Bundle bundle) { 2550 Message msg = obtainMessage(what, obj); 2551 msg.setData(bundle); 2552 msg.sendToTarget(); 2553 } 2554 2555 public void post(int what, Object obj) { 2556 obtainMessage(what, obj).sendToTarget(); 2557 } 2558 2559 public void post(int what) { 2560 post(what, null); 2561 } 2562 2563 public void post(int what, Object obj, int arg1) { 2564 obtainMessage(what, arg1, 0, obj).sendToTarget(); 2565 } 2566 2567 @Override 2568 public void handleMessage(Message msg) { 2569 MediaSessionCompat.Callback cb = mCallback; 2570 if (cb == null) { 2571 return; 2572 } 2573 switch (msg.what) { 2574 case MSG_COMMAND: 2575 Command cmd = (Command) msg.obj; 2576 cb.onCommand(cmd.command, cmd.extras, cmd.stub); 2577 break; 2578 case MSG_MEDIA_BUTTON: 2579 KeyEvent keyEvent = (KeyEvent) msg.obj; 2580 Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON); 2581 intent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent); 2582 // Let the Callback handle events first before using the default behavior 2583 if (!cb.onMediaButtonEvent(intent)) { 2584 onMediaButtonEvent(keyEvent, cb); 2585 } 2586 break; 2587 case MSG_PREPARE: 2588 cb.onPrepare(); 2589 break; 2590 case MSG_PREPARE_MEDIA_ID: 2591 cb.onPrepareFromMediaId((String) msg.obj, msg.getData()); 2592 break; 2593 case MSG_PREPARE_SEARCH: 2594 cb.onPrepareFromSearch((String) msg.obj, msg.getData()); 2595 break; 2596 case MSG_PREPARE_URI: 2597 cb.onPrepareFromUri((Uri) msg.obj, msg.getData()); 2598 break; 2599 case MSG_PLAY: 2600 cb.onPlay(); 2601 break; 2602 case MSG_PLAY_MEDIA_ID: 2603 cb.onPlayFromMediaId((String) msg.obj, msg.getData()); 2604 break; 2605 case MSG_PLAY_SEARCH: 2606 cb.onPlayFromSearch((String) msg.obj, msg.getData()); 2607 break; 2608 case MSG_PLAY_URI: 2609 cb.onPlayFromUri((Uri) msg.obj, msg.getData()); 2610 break; 2611 case MSG_SKIP_TO_ITEM: 2612 cb.onSkipToQueueItem((Long) msg.obj); 2613 break; 2614 case MSG_PAUSE: 2615 cb.onPause(); 2616 break; 2617 case MSG_STOP: 2618 cb.onStop(); 2619 break; 2620 case MSG_NEXT: 2621 cb.onSkipToNext(); 2622 break; 2623 case MSG_PREVIOUS: 2624 cb.onSkipToPrevious(); 2625 break; 2626 case MSG_FAST_FORWARD: 2627 cb.onFastForward(); 2628 break; 2629 case MSG_REWIND: 2630 cb.onRewind(); 2631 break; 2632 case MSG_SEEK_TO: 2633 cb.onSeekTo((Long) msg.obj); 2634 break; 2635 case MSG_RATE: 2636 cb.onSetRating((RatingCompat) msg.obj); 2637 break; 2638 case MSG_CUSTOM_ACTION: 2639 cb.onCustomAction((String) msg.obj, msg.getData()); 2640 break; 2641 case MSG_ADD_QUEUE_ITEM: 2642 cb.onAddQueueItem((MediaDescriptionCompat) msg.obj); 2643 break; 2644 case MSG_ADD_QUEUE_ITEM_AT: 2645 cb.onAddQueueItem((MediaDescriptionCompat) msg.obj, msg.arg1); 2646 break; 2647 case MSG_REMOVE_QUEUE_ITEM: 2648 cb.onRemoveQueueItem((MediaDescriptionCompat) msg.obj); 2649 break; 2650 case MSG_REMOVE_QUEUE_ITEM_AT: 2651 cb.onRemoveQueueItemAt(msg.arg1); 2652 break; 2653 case MSG_ADJUST_VOLUME: 2654 adjustVolume(msg.arg1, 0); 2655 break; 2656 case MSG_SET_VOLUME: 2657 setVolumeTo(msg.arg1, 0); 2658 break; 2659 case MSG_SET_REPEAT_MODE: 2660 cb.onSetRepeatMode(msg.arg1); 2661 break; 2662 case MSG_SET_SHUFFLE_MODE_ENABLED: 2663 cb.onSetShuffleModeEnabled((boolean) msg.obj); 2664 break; 2665 } 2666 } 2667 2668 private void onMediaButtonEvent(KeyEvent ke, MediaSessionCompat.Callback cb) { 2669 if (ke == null || ke.getAction() != KeyEvent.ACTION_DOWN) { 2670 return; 2671 } 2672 long validActions = mState == null ? 0 : mState.getActions(); 2673 switch (ke.getKeyCode()) { 2674 // Note KeyEvent.KEYCODE_MEDIA_PLAY is API 11+ 2675 case KEYCODE_MEDIA_PLAY: 2676 if ((validActions & PlaybackStateCompat.ACTION_PLAY) != 0) { 2677 cb.onPlay(); 2678 } 2679 break; 2680 // Note KeyEvent.KEYCODE_MEDIA_PAUSE is API 11+ 2681 case KEYCODE_MEDIA_PAUSE: 2682 if ((validActions & PlaybackStateCompat.ACTION_PAUSE) != 0) { 2683 cb.onPause(); 2684 } 2685 break; 2686 case KeyEvent.KEYCODE_MEDIA_NEXT: 2687 if ((validActions & PlaybackStateCompat.ACTION_SKIP_TO_NEXT) != 0) { 2688 cb.onSkipToNext(); 2689 } 2690 break; 2691 case KeyEvent.KEYCODE_MEDIA_PREVIOUS: 2692 if ((validActions & PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS) != 0) { 2693 cb.onSkipToPrevious(); 2694 } 2695 break; 2696 case KeyEvent.KEYCODE_MEDIA_STOP: 2697 if ((validActions & PlaybackStateCompat.ACTION_STOP) != 0) { 2698 cb.onStop(); 2699 } 2700 break; 2701 case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: 2702 if ((validActions & PlaybackStateCompat.ACTION_FAST_FORWARD) != 0) { 2703 cb.onFastForward(); 2704 } 2705 break; 2706 case KeyEvent.KEYCODE_MEDIA_REWIND: 2707 if ((validActions & PlaybackStateCompat.ACTION_REWIND) != 0) { 2708 cb.onRewind(); 2709 } 2710 break; 2711 case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: 2712 case KeyEvent.KEYCODE_HEADSETHOOK: 2713 boolean isPlaying = mState != null 2714 && mState.getState() == PlaybackStateCompat.STATE_PLAYING; 2715 boolean canPlay = (validActions & (PlaybackStateCompat.ACTION_PLAY_PAUSE 2716 | PlaybackStateCompat.ACTION_PLAY)) != 0; 2717 boolean canPause = (validActions & (PlaybackStateCompat.ACTION_PLAY_PAUSE 2718 | PlaybackStateCompat.ACTION_PAUSE)) != 0; 2719 if (isPlaying && canPause) { 2720 cb.onPause(); 2721 } else if (!isPlaying && canPlay) { 2722 cb.onPlay(); 2723 } 2724 break; 2725 } 2726 } 2727 } 2728 } 2729 2730 @RequiresApi(18) 2731 static class MediaSessionImplApi18 extends MediaSessionImplBase { 2732 private static boolean sIsMbrPendingIntentSupported = true; 2733 2734 MediaSessionImplApi18(Context context, String tag, ComponentName mbrComponent, 2735 PendingIntent mbrIntent) { 2736 super(context, tag, mbrComponent, mbrIntent); 2737 } 2738 2739 @Override 2740 public void setCallback(Callback callback, Handler handler) { 2741 super.setCallback(callback, handler); 2742 if (callback == null) { 2743 mRcc.setPlaybackPositionUpdateListener(null); 2744 } else { 2745 RemoteControlClient.OnPlaybackPositionUpdateListener listener = 2746 new RemoteControlClient.OnPlaybackPositionUpdateListener() { 2747 @Override 2748 public void onPlaybackPositionUpdate(long newPositionMs) { 2749 postToHandler(MessageHandler.MSG_SEEK_TO, newPositionMs); 2750 } 2751 }; 2752 mRcc.setPlaybackPositionUpdateListener(listener); 2753 } 2754 } 2755 2756 @Override 2757 void setRccState(PlaybackStateCompat state) { 2758 long position = state.getPosition(); 2759 float speed = state.getPlaybackSpeed(); 2760 long updateTime = state.getLastPositionUpdateTime(); 2761 long currTime = SystemClock.elapsedRealtime(); 2762 if (state.getState() == PlaybackStateCompat.STATE_PLAYING && position > 0) { 2763 long diff = 0; 2764 if (updateTime > 0) { 2765 diff = currTime - updateTime; 2766 if (speed > 0 && speed != 1f) { 2767 diff *= speed; 2768 } 2769 } 2770 position += diff; 2771 } 2772 mRcc.setPlaybackState(getRccStateFromState(state.getState()), position, speed); 2773 } 2774 2775 @Override 2776 int getRccTransportControlFlagsFromActions(long actions) { 2777 int transportControlFlags = super.getRccTransportControlFlagsFromActions(actions); 2778 if ((actions & PlaybackStateCompat.ACTION_SEEK_TO) != 0) { 2779 transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_POSITION_UPDATE; 2780 } 2781 return transportControlFlags; 2782 } 2783 2784 @Override 2785 void registerMediaButtonEventReceiver(PendingIntent mbrIntent, ComponentName mbrComponent) { 2786 // Some Android implementations are not able to register a media button event receiver 2787 // using a PendingIntent but need a ComponentName instead. These will raise a 2788 // NullPointerException. 2789 if (sIsMbrPendingIntentSupported) { 2790 try { 2791 mAudioManager.registerMediaButtonEventReceiver(mbrIntent); 2792 } catch (NullPointerException e) { 2793 Log.w(TAG, "Unable to register media button event receiver with " 2794 + "PendingIntent, falling back to ComponentName."); 2795 sIsMbrPendingIntentSupported = false; 2796 } 2797 } 2798 2799 if (!sIsMbrPendingIntentSupported) { 2800 super.registerMediaButtonEventReceiver(mbrIntent, mbrComponent); 2801 } 2802 } 2803 2804 @Override 2805 void unregisterMediaButtonEventReceiver(PendingIntent mbrIntent, 2806 ComponentName mbrComponent) { 2807 if (sIsMbrPendingIntentSupported) { 2808 mAudioManager.unregisterMediaButtonEventReceiver(mbrIntent); 2809 } else { 2810 super.unregisterMediaButtonEventReceiver(mbrIntent, mbrComponent); 2811 } 2812 } 2813 } 2814 2815 @RequiresApi(19) 2816 static class MediaSessionImplApi19 extends MediaSessionImplApi18 { 2817 MediaSessionImplApi19(Context context, String tag, ComponentName mbrComponent, 2818 PendingIntent mbrIntent) { 2819 super(context, tag, mbrComponent, mbrIntent); 2820 } 2821 2822 @Override 2823 public void setCallback(Callback callback, Handler handler) { 2824 super.setCallback(callback, handler); 2825 if (callback == null) { 2826 mRcc.setMetadataUpdateListener(null); 2827 } else { 2828 RemoteControlClient.OnMetadataUpdateListener listener = 2829 new RemoteControlClient.OnMetadataUpdateListener() { 2830 @Override 2831 public void onMetadataUpdate(int key, Object newValue) { 2832 if (key == MediaMetadataEditor.RATING_KEY_BY_USER 2833 && newValue instanceof Rating) { 2834 postToHandler(MessageHandler.MSG_RATE, 2835 RatingCompat.fromRating(newValue)); 2836 } 2837 } 2838 }; 2839 mRcc.setMetadataUpdateListener(listener); 2840 } 2841 } 2842 2843 @Override 2844 int getRccTransportControlFlagsFromActions(long actions) { 2845 int transportControlFlags = super.getRccTransportControlFlagsFromActions(actions); 2846 if ((actions & PlaybackStateCompat.ACTION_SET_RATING) != 0) { 2847 transportControlFlags |= RemoteControlClient.FLAG_KEY_MEDIA_RATING; 2848 } 2849 return transportControlFlags; 2850 } 2851 2852 @Override 2853 RemoteControlClient.MetadataEditor buildRccMetadata(Bundle metadata) { 2854 RemoteControlClient.MetadataEditor editor = super.buildRccMetadata(metadata); 2855 long actions = mState == null ? 0 : mState.getActions(); 2856 if ((actions & PlaybackStateCompat.ACTION_SET_RATING) != 0) { 2857 editor.addEditableKey(RemoteControlClient.MetadataEditor.RATING_KEY_BY_USER); 2858 } 2859 2860 if (metadata == null) { 2861 return editor; 2862 } 2863 if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_YEAR)) { 2864 editor.putLong(MediaMetadataRetriever.METADATA_KEY_YEAR, 2865 metadata.getLong(MediaMetadataCompat.METADATA_KEY_YEAR)); 2866 } 2867 if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_RATING)) { 2868 editor.putObject(MediaMetadataEditor.RATING_KEY_BY_OTHERS, 2869 metadata.getParcelable(MediaMetadataCompat.METADATA_KEY_RATING)); 2870 } 2871 if (metadata.containsKey(MediaMetadataCompat.METADATA_KEY_USER_RATING)) { 2872 editor.putObject(MediaMetadataEditor.RATING_KEY_BY_USER, 2873 metadata.getParcelable(MediaMetadataCompat.METADATA_KEY_USER_RATING)); 2874 } 2875 return editor; 2876 } 2877 } 2878 2879 @RequiresApi(21) 2880 static class MediaSessionImplApi21 implements MediaSessionImpl { 2881 private final Object mSessionObj; 2882 private final Token mToken; 2883 2884 private boolean mDestroyed = false; 2885 private final RemoteCallbackList<IMediaControllerCallback> mExtraControllerCallbacks = 2886 new RemoteCallbackList<>(); 2887 2888 private PlaybackStateCompat mPlaybackState; 2889 @RatingCompat.Style int mRatingType; 2890 @PlaybackStateCompat.RepeatMode int mRepeatMode; 2891 boolean mShuffleModeEnabled; 2892 2893 public MediaSessionImplApi21(Context context, String tag) { 2894 mSessionObj = MediaSessionCompatApi21.createSession(context, tag); 2895 mToken = new Token(MediaSessionCompatApi21.getSessionToken(mSessionObj), 2896 new ExtraSession()); 2897 } 2898 2899 public MediaSessionImplApi21(Object mediaSession) { 2900 mSessionObj = MediaSessionCompatApi21.verifySession(mediaSession); 2901 mToken = new Token(MediaSessionCompatApi21.getSessionToken(mSessionObj), 2902 new ExtraSession()); 2903 } 2904 2905 @Override 2906 public void setCallback(Callback callback, Handler handler) { 2907 MediaSessionCompatApi21.setCallback(mSessionObj, 2908 callback == null ? null : callback.mCallbackObj, handler); 2909 if (android.os.Build.VERSION.SDK_INT < 26 && callback != null) { 2910 callback.mSessionImpl = new WeakReference<MediaSessionImpl>(this); 2911 } 2912 } 2913 2914 @Override 2915 public void setFlags(@SessionFlags int flags) { 2916 MediaSessionCompatApi21.setFlags(mSessionObj, flags); 2917 } 2918 2919 @Override 2920 public void setPlaybackToLocal(int stream) { 2921 MediaSessionCompatApi21.setPlaybackToLocal(mSessionObj, stream); 2922 } 2923 2924 @Override 2925 public void setPlaybackToRemote(VolumeProviderCompat volumeProvider) { 2926 MediaSessionCompatApi21.setPlaybackToRemote(mSessionObj, 2927 volumeProvider.getVolumeProvider()); 2928 } 2929 2930 @Override 2931 public void setActive(boolean active) { 2932 MediaSessionCompatApi21.setActive(mSessionObj, active); 2933 } 2934 2935 @Override 2936 public boolean isActive() { 2937 return MediaSessionCompatApi21.isActive(mSessionObj); 2938 } 2939 2940 @Override 2941 public void sendSessionEvent(String event, Bundle extras) { 2942 if (android.os.Build.VERSION.SDK_INT < 23) { 2943 int size = mExtraControllerCallbacks.beginBroadcast(); 2944 for (int i = size - 1; i >= 0; i--) { 2945 IMediaControllerCallback cb = mExtraControllerCallbacks.getBroadcastItem(i); 2946 try { 2947 cb.onEvent(event, extras); 2948 } catch (RemoteException e) { 2949 } 2950 } 2951 mExtraControllerCallbacks.finishBroadcast(); 2952 } 2953 MediaSessionCompatApi21.sendSessionEvent(mSessionObj, event, extras); 2954 } 2955 2956 @Override 2957 public void release() { 2958 mDestroyed = true; 2959 MediaSessionCompatApi21.release(mSessionObj); 2960 } 2961 2962 @Override 2963 public Token getSessionToken() { 2964 return mToken; 2965 } 2966 2967 @Override 2968 public void setPlaybackState(PlaybackStateCompat state) { 2969 mPlaybackState = state; 2970 int size = mExtraControllerCallbacks.beginBroadcast(); 2971 for (int i = size - 1; i >= 0; i--) { 2972 IMediaControllerCallback cb = mExtraControllerCallbacks.getBroadcastItem(i); 2973 try { 2974 cb.onPlaybackStateChanged(state); 2975 } catch (RemoteException e) { 2976 } 2977 } 2978 mExtraControllerCallbacks.finishBroadcast(); 2979 MediaSessionCompatApi21.setPlaybackState(mSessionObj, 2980 state == null ? null : state.getPlaybackState()); 2981 } 2982 2983 @Override 2984 public void setMetadata(MediaMetadataCompat metadata) { 2985 MediaSessionCompatApi21.setMetadata(mSessionObj, 2986 metadata == null ? null : metadata.getMediaMetadata()); 2987 } 2988 2989 @Override 2990 public void setSessionActivity(PendingIntent pi) { 2991 MediaSessionCompatApi21.setSessionActivity(mSessionObj, pi); 2992 } 2993 2994 @Override 2995 public void setMediaButtonReceiver(PendingIntent mbr) { 2996 MediaSessionCompatApi21.setMediaButtonReceiver(mSessionObj, mbr); 2997 } 2998 2999 @Override 3000 public void setQueue(List<QueueItem> queue) { 3001 List<Object> queueObjs = null; 3002 if (queue != null) { 3003 queueObjs = new ArrayList<>(); 3004 for (QueueItem item : queue) { 3005 queueObjs.add(item.getQueueItem()); 3006 } 3007 } 3008 MediaSessionCompatApi21.setQueue(mSessionObj, queueObjs); 3009 } 3010 3011 @Override 3012 public void setQueueTitle(CharSequence title) { 3013 MediaSessionCompatApi21.setQueueTitle(mSessionObj, title); 3014 } 3015 3016 @Override 3017 public void setRatingType(@RatingCompat.Style int type) { 3018 if (android.os.Build.VERSION.SDK_INT < 22) { 3019 mRatingType = type; 3020 } else { 3021 MediaSessionCompatApi22.setRatingType(mSessionObj, type); 3022 } 3023 } 3024 3025 @Override 3026 public void setRepeatMode(@PlaybackStateCompat.RepeatMode int repeatMode) { 3027 if (android.os.Build.VERSION.SDK_INT < 26) { 3028 if (mRepeatMode != repeatMode) { 3029 mRepeatMode = repeatMode; 3030 int size = mExtraControllerCallbacks.beginBroadcast(); 3031 for (int i = size - 1; i >= 0; i--) { 3032 IMediaControllerCallback cb = mExtraControllerCallbacks.getBroadcastItem(i); 3033 try { 3034 cb.onRepeatModeChanged(repeatMode); 3035 } catch (RemoteException e) { 3036 } 3037 } 3038 mExtraControllerCallbacks.finishBroadcast(); 3039 } 3040 } else { 3041 MediaSessionCompatApi26.setRepeatMode(mSessionObj, repeatMode); 3042 } 3043 } 3044 3045 @Override 3046 public void setShuffleModeEnabled(boolean enabled) { 3047 if (android.os.Build.VERSION.SDK_INT < 26) { 3048 if (mShuffleModeEnabled != enabled) { 3049 mShuffleModeEnabled = enabled; 3050 int size = mExtraControllerCallbacks.beginBroadcast(); 3051 for (int i = size - 1; i >= 0; i--) { 3052 IMediaControllerCallback cb = mExtraControllerCallbacks.getBroadcastItem(i); 3053 try { 3054 cb.onShuffleModeChanged(enabled); 3055 } catch (RemoteException e) { 3056 } 3057 } 3058 mExtraControllerCallbacks.finishBroadcast(); 3059 } 3060 } else { 3061 MediaSessionCompatApi26.setShuffleModeEnabled(mSessionObj, enabled); 3062 } 3063 } 3064 3065 @Override 3066 public void setExtras(Bundle extras) { 3067 MediaSessionCompatApi21.setExtras(mSessionObj, extras); 3068 } 3069 3070 @Override 3071 public Object getMediaSession() { 3072 return mSessionObj; 3073 } 3074 3075 @Override 3076 public Object getRemoteControlClient() { 3077 return null; 3078 } 3079 3080 @Override 3081 public String getCallingPackage() { 3082 if (android.os.Build.VERSION.SDK_INT < 24) { 3083 return null; 3084 } else { 3085 return MediaSessionCompatApi24.getCallingPackage(mSessionObj); 3086 } 3087 } 3088 3089 class ExtraSession extends IMediaSession.Stub { 3090 @Override 3091 public void sendCommand(String command, Bundle args, ResultReceiverWrapper cb) { 3092 // Will not be called. 3093 throw new AssertionError(); 3094 } 3095 3096 @Override 3097 public boolean sendMediaButton(KeyEvent mediaButton) { 3098 // Will not be called. 3099 throw new AssertionError(); 3100 } 3101 3102 @Override 3103 public void registerCallbackListener(IMediaControllerCallback cb) { 3104 if (!mDestroyed) { 3105 mExtraControllerCallbacks.register(cb); 3106 } 3107 } 3108 3109 @Override 3110 public void unregisterCallbackListener(IMediaControllerCallback cb) { 3111 mExtraControllerCallbacks.unregister(cb); 3112 } 3113 3114 @Override 3115 public String getPackageName() { 3116 // Will not be called. 3117 throw new AssertionError(); 3118 } 3119 3120 @Override 3121 public String getTag() { 3122 // Will not be called. 3123 throw new AssertionError(); 3124 } 3125 3126 @Override 3127 public PendingIntent getLaunchPendingIntent() { 3128 // Will not be called. 3129 throw new AssertionError(); 3130 } 3131 3132 @Override 3133 @SessionFlags 3134 public long getFlags() { 3135 // Will not be called. 3136 throw new AssertionError(); 3137 } 3138 3139 @Override 3140 public ParcelableVolumeInfo getVolumeAttributes() { 3141 // Will not be called. 3142 throw new AssertionError(); 3143 } 3144 3145 @Override 3146 public void adjustVolume(int direction, int flags, String packageName) { 3147 // Will not be called. 3148 throw new AssertionError(); 3149 } 3150 3151 @Override 3152 public void setVolumeTo(int value, int flags, String packageName) { 3153 // Will not be called. 3154 throw new AssertionError(); 3155 } 3156 3157 @Override 3158 public void prepare() throws RemoteException { 3159 // Will not be called. 3160 throw new AssertionError(); 3161 } 3162 3163 @Override 3164 public void prepareFromMediaId(String mediaId, Bundle extras) throws RemoteException { 3165 // Will not be called. 3166 throw new AssertionError(); 3167 } 3168 3169 @Override 3170 public void prepareFromSearch(String query, Bundle extras) throws RemoteException { 3171 // Will not be called. 3172 throw new AssertionError(); 3173 } 3174 3175 @Override 3176 public void prepareFromUri(Uri uri, Bundle extras) throws RemoteException { 3177 // Will not be called. 3178 throw new AssertionError(); 3179 } 3180 3181 @Override 3182 public void play() throws RemoteException { 3183 // Will not be called. 3184 throw new AssertionError(); 3185 } 3186 3187 @Override 3188 public void playFromMediaId(String mediaId, Bundle extras) throws RemoteException { 3189 // Will not be called. 3190 throw new AssertionError(); 3191 } 3192 3193 @Override 3194 public void playFromSearch(String query, Bundle extras) throws RemoteException { 3195 // Will not be called. 3196 throw new AssertionError(); 3197 } 3198 3199 @Override 3200 public void playFromUri(Uri uri, Bundle extras) throws RemoteException { 3201 // Will not be called. 3202 throw new AssertionError(); 3203 } 3204 3205 @Override 3206 public void skipToQueueItem(long id) { 3207 // Will not be called. 3208 throw new AssertionError(); 3209 } 3210 3211 @Override 3212 public void pause() throws RemoteException { 3213 // Will not be called. 3214 throw new AssertionError(); 3215 } 3216 3217 @Override 3218 public void stop() throws RemoteException { 3219 // Will not be called. 3220 throw new AssertionError(); 3221 } 3222 3223 @Override 3224 public void next() throws RemoteException { 3225 // Will not be called. 3226 throw new AssertionError(); 3227 } 3228 3229 @Override 3230 public void previous() throws RemoteException { 3231 // Will not be called. 3232 throw new AssertionError(); 3233 } 3234 3235 @Override 3236 public void fastForward() throws RemoteException { 3237 // Will not be called. 3238 throw new AssertionError(); 3239 } 3240 3241 @Override 3242 public void rewind() throws RemoteException { 3243 // Will not be called. 3244 throw new AssertionError(); 3245 } 3246 3247 @Override 3248 public void seekTo(long pos) throws RemoteException { 3249 // Will not be called. 3250 throw new AssertionError(); 3251 } 3252 3253 @Override 3254 public void rate(RatingCompat rating) throws RemoteException { 3255 // Will not be called. 3256 throw new AssertionError(); 3257 } 3258 3259 @Override 3260 public void setRepeatMode(int repeatMode) throws RemoteException { 3261 // Will not be called. 3262 throw new AssertionError(); 3263 } 3264 3265 @Override 3266 public void setShuffleModeEnabled(boolean enabled) throws RemoteException { 3267 // Will not be called. 3268 throw new AssertionError(); 3269 } 3270 3271 @Override 3272 public void sendCustomAction(String action, Bundle args) throws RemoteException { 3273 // Will not be called. 3274 throw new AssertionError(); 3275 } 3276 3277 @Override 3278 public MediaMetadataCompat getMetadata() { 3279 // Will not be called. 3280 throw new AssertionError(); 3281 } 3282 3283 @Override 3284 public PlaybackStateCompat getPlaybackState() { 3285 return mPlaybackState; 3286 } 3287 3288 @Override 3289 public List<QueueItem> getQueue() { 3290 // Will not be called. 3291 return null; 3292 } 3293 3294 @Override 3295 public void addQueueItem(MediaDescriptionCompat descriptionCompat) { 3296 // Will not be called. 3297 throw new AssertionError(); 3298 } 3299 3300 @Override 3301 public void addQueueItemAt(MediaDescriptionCompat descriptionCompat, int index) { 3302 // Will not be called. 3303 throw new AssertionError(); 3304 } 3305 3306 @Override 3307 public void removeQueueItem(MediaDescriptionCompat description) { 3308 // Will not be called. 3309 throw new AssertionError(); 3310 } 3311 3312 @Override 3313 public void removeQueueItemAt(int index) { 3314 // Will not be called. 3315 throw new AssertionError(); 3316 } 3317 3318 @Override 3319 public CharSequence getQueueTitle() { 3320 // Will not be called. 3321 throw new AssertionError(); 3322 } 3323 3324 @Override 3325 public Bundle getExtras() { 3326 // Will not be called. 3327 throw new AssertionError(); 3328 } 3329 3330 @Override 3331 @RatingCompat.Style 3332 public int getRatingType() { 3333 return mRatingType; 3334 } 3335 3336 @Override 3337 @PlaybackStateCompat.RepeatMode 3338 public int getRepeatMode() { 3339 return mRepeatMode; 3340 } 3341 3342 @Override 3343 public boolean isShuffleModeEnabled() { 3344 return mShuffleModeEnabled; 3345 } 3346 3347 @Override 3348 public boolean isTransportControlEnabled() { 3349 // Will not be called. 3350 throw new AssertionError(); 3351 } 3352 } 3353 } 3354} 3355