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