PlaybackStateCompat.java revision 44cc5e3d6dafb4aed32b4aeb394ed2533e4077f5
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 */ 16package android.support.v4.media.session; 17 18 19import android.os.Build; 20import android.os.Bundle; 21import android.os.Parcel; 22import android.os.Parcelable; 23import android.os.SystemClock; 24import android.support.annotation.IntDef; 25import android.support.annotation.Nullable; 26import android.text.TextUtils; 27import android.view.KeyEvent; 28 29import java.lang.annotation.Retention; 30import java.lang.annotation.RetentionPolicy; 31import java.util.ArrayList; 32import java.util.List; 33 34/** 35 * Playback state for a {@link MediaSessionCompat}. This includes a state like 36 * {@link PlaybackStateCompat#STATE_PLAYING}, the current playback position, 37 * and the current control capabilities. 38 */ 39public final class PlaybackStateCompat implements Parcelable { 40 41 /** 42 * @hide 43 */ 44 @IntDef(flag=true, value={ACTION_STOP, ACTION_PAUSE, ACTION_PLAY, ACTION_REWIND, 45 ACTION_SKIP_TO_PREVIOUS, ACTION_SKIP_TO_NEXT, ACTION_FAST_FORWARD, ACTION_SET_RATING, 46 ACTION_SEEK_TO, ACTION_PLAY_PAUSE, ACTION_PLAY_FROM_MEDIA_ID, ACTION_PLAY_FROM_SEARCH, 47 ACTION_SKIP_TO_QUEUE_ITEM, ACTION_PLAY_FROM_URI, ACTION_PREPARE, 48 ACTION_PREPARE_FROM_MEDIA_ID, ACTION_PREPARE_FROM_SEARCH, ACTION_PREPARE_FROM_URI}) 49 @Retention(RetentionPolicy.SOURCE) 50 public @interface Actions {} 51 52 /** 53 * @hide 54 */ 55 @IntDef({ACTION_STOP, ACTION_PAUSE, ACTION_PLAY, ACTION_REWIND, ACTION_SKIP_TO_PREVIOUS, 56 ACTION_SKIP_TO_NEXT, ACTION_FAST_FORWARD, ACTION_PLAY_PAUSE}) 57 @Retention(RetentionPolicy.SOURCE) 58 public @interface MediaKeyAction {} 59 60 /** 61 * Indicates this session supports the stop command. 62 * 63 * @see Builder#setActions(long) 64 */ 65 public static final long ACTION_STOP = 1 << 0; 66 67 /** 68 * Indicates this session supports the pause command. 69 * 70 * @see Builder#setActions(long) 71 */ 72 public static final long ACTION_PAUSE = 1 << 1; 73 74 /** 75 * Indicates this session supports the play command. 76 * 77 * @see Builder#setActions(long) 78 */ 79 public static final long ACTION_PLAY = 1 << 2; 80 81 /** 82 * Indicates this session supports the rewind command. 83 * 84 * @see Builder#setActions(long) 85 */ 86 public static final long ACTION_REWIND = 1 << 3; 87 88 /** 89 * Indicates this session supports the previous command. 90 * 91 * @see Builder#setActions(long) 92 */ 93 public static final long ACTION_SKIP_TO_PREVIOUS = 1 << 4; 94 95 /** 96 * Indicates this session supports the next command. 97 * 98 * @see Builder#setActions(long) 99 */ 100 public static final long ACTION_SKIP_TO_NEXT = 1 << 5; 101 102 /** 103 * Indicates this session supports the fast forward command. 104 * 105 * @see Builder#setActions(long) 106 */ 107 public static final long ACTION_FAST_FORWARD = 1 << 6; 108 109 /** 110 * Indicates this session supports the set rating command. 111 * 112 * @see Builder#setActions(long) 113 */ 114 public static final long ACTION_SET_RATING = 1 << 7; 115 116 /** 117 * Indicates this session supports the seek to command. 118 * 119 * @see Builder#setActions(long) 120 */ 121 public static final long ACTION_SEEK_TO = 1 << 8; 122 123 /** 124 * Indicates this session supports the play/pause toggle command. 125 * 126 * @see Builder#setActions(long) 127 */ 128 public static final long ACTION_PLAY_PAUSE = 1 << 9; 129 130 /** 131 * Indicates this session supports the play from media id command. 132 * 133 * @see Builder#setActions(long) 134 */ 135 public static final long ACTION_PLAY_FROM_MEDIA_ID = 1 << 10; 136 137 /** 138 * Indicates this session supports the play from search command. 139 * 140 * @see Builder#setActions(long) 141 */ 142 public static final long ACTION_PLAY_FROM_SEARCH = 1 << 11; 143 144 /** 145 * Indicates this session supports the skip to queue item command. 146 * 147 * @see Builder#setActions(long) 148 */ 149 public static final long ACTION_SKIP_TO_QUEUE_ITEM = 1 << 12; 150 151 /** 152 * Indicates this session supports the play from URI command. 153 * 154 * @see Builder#setActions(long) 155 */ 156 public static final long ACTION_PLAY_FROM_URI = 1 << 13; 157 158 /** 159 * Indicates this session supports the prepare command. 160 * 161 * @see Builder#setActions(long) 162 */ 163 public static final long ACTION_PREPARE = 1 << 14; 164 165 /** 166 * Indicates this session supports the prepare from media id command. 167 * 168 * @see Builder#setActions(long) 169 */ 170 public static final long ACTION_PREPARE_FROM_MEDIA_ID = 1 << 15; 171 172 /** 173 * Indicates this session supports the prepare from search command. 174 * 175 * @see Builder#setActions(long) 176 */ 177 public static final long ACTION_PREPARE_FROM_SEARCH = 1 << 16; 178 179 /** 180 * Indicates this session supports the prepare from URI command. 181 * 182 * @see Builder#setActions(long) 183 */ 184 public static final long ACTION_PREPARE_FROM_URI = 1 << 17; 185 186 /** 187 * @hide 188 */ 189 @IntDef({STATE_NONE, STATE_STOPPED, STATE_PAUSED, STATE_PLAYING, STATE_FAST_FORWARDING, 190 STATE_REWINDING, STATE_BUFFERING, STATE_ERROR, STATE_CONNECTING, 191 STATE_SKIPPING_TO_PREVIOUS, STATE_SKIPPING_TO_NEXT, STATE_SKIPPING_TO_QUEUE_ITEM}) 192 @Retention(RetentionPolicy.SOURCE) 193 public @interface State {} 194 195 /** 196 * This is the default playback state and indicates that no media has been 197 * added yet, or the performer has been reset and has no content to play. 198 * 199 * @see Builder#setState 200 */ 201 public final static int STATE_NONE = 0; 202 203 /** 204 * State indicating this item is currently stopped. 205 * 206 * @see Builder#setState 207 */ 208 public final static int STATE_STOPPED = 1; 209 210 /** 211 * State indicating this item is currently paused. 212 * 213 * @see Builder#setState 214 */ 215 public final static int STATE_PAUSED = 2; 216 217 /** 218 * State indicating this item is currently playing. 219 * 220 * @see Builder#setState 221 */ 222 public final static int STATE_PLAYING = 3; 223 224 /** 225 * State indicating this item is currently fast forwarding. 226 * 227 * @see Builder#setState 228 */ 229 public final static int STATE_FAST_FORWARDING = 4; 230 231 /** 232 * State indicating this item is currently rewinding. 233 * 234 * @see Builder#setState 235 */ 236 public final static int STATE_REWINDING = 5; 237 238 /** 239 * State indicating this item is currently buffering and will begin playing 240 * when enough data has buffered. 241 * 242 * @see Builder#setState 243 */ 244 public final static int STATE_BUFFERING = 6; 245 246 /** 247 * State indicating this item is currently in an error state. The error 248 * message should also be set when entering this state. 249 * 250 * @see Builder#setState 251 */ 252 public final static int STATE_ERROR = 7; 253 254 /** 255 * State indicating the class doing playback is currently connecting to a 256 * route. Depending on the implementation you may return to the previous 257 * state when the connection finishes or enter {@link #STATE_NONE}. If 258 * the connection failed {@link #STATE_ERROR} should be used. 259 * <p> 260 * On devices earlier than API 21, this will appear as {@link #STATE_BUFFERING} 261 * </p> 262 * 263 * @see Builder#setState 264 */ 265 public final static int STATE_CONNECTING = 8; 266 267 /** 268 * State indicating the player is currently skipping to the previous item. 269 * 270 * @see Builder#setState 271 */ 272 public final static int STATE_SKIPPING_TO_PREVIOUS = 9; 273 274 /** 275 * State indicating the player is currently skipping to the next item. 276 * 277 * @see Builder#setState 278 */ 279 public final static int STATE_SKIPPING_TO_NEXT = 10; 280 281 /** 282 * State indicating the player is currently skipping to a specific item in 283 * the queue. 284 * <p> 285 * On devices earlier than API 21, this will appear as {@link #STATE_SKIPPING_TO_NEXT} 286 * </p> 287 * 288 * @see Builder#setState 289 */ 290 public final static int STATE_SKIPPING_TO_QUEUE_ITEM = 11; 291 292 /** 293 * Use this value for the position to indicate the position is not known. 294 */ 295 public final static long PLAYBACK_POSITION_UNKNOWN = -1; 296 297 // KeyEvent constants only available on API 11+ 298 private static final int KEYCODE_MEDIA_PAUSE = 127; 299 private static final int KEYCODE_MEDIA_PLAY = 126; 300 301 /** 302 * Translates a given action into a matched key code defined in {@link KeyEvent}. The given 303 * action should be one of the following: 304 * <ul> 305 * <li>{@link PlaybackStateCompat#ACTION_PLAY}</li> 306 * <li>{@link PlaybackStateCompat#ACTION_PAUSE}</li> 307 * <li>{@link PlaybackStateCompat#ACTION_SKIP_TO_NEXT}</li> 308 * <li>{@link PlaybackStateCompat#ACTION_SKIP_TO_PREVIOUS}</li> 309 * <li>{@link PlaybackStateCompat#ACTION_STOP}</li> 310 * <li>{@link PlaybackStateCompat#ACTION_FAST_FORWARD}</li> 311 * <li>{@link PlaybackStateCompat#ACTION_REWIND}</li> 312 * <li>{@link PlaybackStateCompat#ACTION_PLAY_PAUSE}</li> 313 * </ul> 314 * 315 * @param action The action to be translated. 316 * 317 * @return the key code matched to the given action. 318 */ 319 public static int toKeyCode(@MediaKeyAction long action) { 320 if (action == ACTION_PLAY) { 321 return KEYCODE_MEDIA_PLAY; 322 } else if (action == ACTION_PAUSE) { 323 return KEYCODE_MEDIA_PAUSE; 324 } else if (action == ACTION_SKIP_TO_NEXT) { 325 return KeyEvent.KEYCODE_MEDIA_NEXT; 326 } else if (action == ACTION_SKIP_TO_PREVIOUS) { 327 return KeyEvent.KEYCODE_MEDIA_PREVIOUS; 328 } else if (action == ACTION_STOP) { 329 return KeyEvent.KEYCODE_MEDIA_STOP; 330 } else if (action == ACTION_FAST_FORWARD) { 331 return KeyEvent.KEYCODE_MEDIA_FAST_FORWARD; 332 } else if (action == ACTION_REWIND) { 333 return KeyEvent.KEYCODE_MEDIA_REWIND; 334 } else if (action == ACTION_PLAY_PAUSE) { 335 return KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE; 336 } 337 return KeyEvent.KEYCODE_UNKNOWN; 338 } 339 340 private final int mState; 341 private final long mPosition; 342 private final long mBufferedPosition; 343 private final float mSpeed; 344 private final long mActions; 345 private final CharSequence mErrorMessage; 346 private final long mUpdateTime; 347 private List<PlaybackStateCompat.CustomAction> mCustomActions; 348 private final long mActiveItemId; 349 private final Bundle mExtras; 350 351 private Object mStateObj; 352 353 private PlaybackStateCompat(int state, long position, long bufferedPosition, 354 float rate, long actions, CharSequence errorMessage, long updateTime, 355 List<PlaybackStateCompat.CustomAction> customActions, 356 long activeItemId, Bundle extras) { 357 mState = state; 358 mPosition = position; 359 mBufferedPosition = bufferedPosition; 360 mSpeed = rate; 361 mActions = actions; 362 mErrorMessage = errorMessage; 363 mUpdateTime = updateTime; 364 mCustomActions = new ArrayList<>(customActions); 365 mActiveItemId = activeItemId; 366 mExtras = extras; 367 } 368 369 private PlaybackStateCompat(Parcel in) { 370 mState = in.readInt(); 371 mPosition = in.readLong(); 372 mSpeed = in.readFloat(); 373 mUpdateTime = in.readLong(); 374 mBufferedPosition = in.readLong(); 375 mActions = in.readLong(); 376 mErrorMessage = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); 377 mCustomActions = in.createTypedArrayList(CustomAction.CREATOR); 378 mActiveItemId = in.readLong(); 379 mExtras = in.readBundle(); 380 } 381 382 @Override 383 public String toString() { 384 StringBuilder bob = new StringBuilder("PlaybackState {"); 385 bob.append("state=").append(mState); 386 bob.append(", position=").append(mPosition); 387 bob.append(", buffered position=").append(mBufferedPosition); 388 bob.append(", speed=").append(mSpeed); 389 bob.append(", updated=").append(mUpdateTime); 390 bob.append(", actions=").append(mActions); 391 bob.append(", error=").append(mErrorMessage); 392 bob.append(", custom actions=").append(mCustomActions); 393 bob.append(", active item id=").append(mActiveItemId); 394 bob.append("}"); 395 return bob.toString(); 396 } 397 398 @Override 399 public int describeContents() { 400 return 0; 401 } 402 403 @Override 404 public void writeToParcel(Parcel dest, int flags) { 405 dest.writeInt(mState); 406 dest.writeLong(mPosition); 407 dest.writeFloat(mSpeed); 408 dest.writeLong(mUpdateTime); 409 dest.writeLong(mBufferedPosition); 410 dest.writeLong(mActions); 411 TextUtils.writeToParcel(mErrorMessage, dest, flags); 412 dest.writeTypedList(mCustomActions); 413 dest.writeLong(mActiveItemId); 414 dest.writeBundle(mExtras); 415 } 416 417 /** 418 * Get the current state of playback. One of the following: 419 * <ul> 420 * <li> {@link PlaybackStateCompat#STATE_NONE}</li> 421 * <li> {@link PlaybackStateCompat#STATE_STOPPED}</li> 422 * <li> {@link PlaybackStateCompat#STATE_PLAYING}</li> 423 * <li> {@link PlaybackStateCompat#STATE_PAUSED}</li> 424 * <li> {@link PlaybackStateCompat#STATE_FAST_FORWARDING}</li> 425 * <li> {@link PlaybackStateCompat#STATE_REWINDING}</li> 426 * <li> {@link PlaybackStateCompat#STATE_BUFFERING}</li> 427 * <li> {@link PlaybackStateCompat#STATE_ERROR}</li> 428 * <li> {@link PlaybackStateCompat#STATE_CONNECTING}</li> 429 * <li> {@link PlaybackStateCompat#STATE_SKIPPING_TO_PREVIOUS}</li> 430 * <li> {@link PlaybackStateCompat#STATE_SKIPPING_TO_NEXT}</li> 431 * <li> {@link PlaybackStateCompat#STATE_SKIPPING_TO_QUEUE_ITEM}</li> 432 */ 433 @State 434 public int getState() { 435 return mState; 436 } 437 438 /** 439 * Get the current playback position in ms. 440 */ 441 public long getPosition() { 442 return mPosition; 443 } 444 445 /** 446 * Get the current buffered position in ms. This is the farthest playback 447 * point that can be reached from the current position using only buffered 448 * content. 449 */ 450 public long getBufferedPosition() { 451 return mBufferedPosition; 452 } 453 454 /** 455 * Get the current playback speed as a multiple of normal playback. This 456 * should be negative when rewinding. A value of 1 means normal playback and 457 * 0 means paused. 458 * 459 * @return The current speed of playback. 460 */ 461 public float getPlaybackSpeed() { 462 return mSpeed; 463 } 464 465 /** 466 * Get the current actions available on this session. This should use a 467 * bitmask of the available actions. 468 * <ul> 469 * <li> {@link PlaybackStateCompat#ACTION_SKIP_TO_PREVIOUS}</li> 470 * <li> {@link PlaybackStateCompat#ACTION_REWIND}</li> 471 * <li> {@link PlaybackStateCompat#ACTION_PLAY}</li> 472 * <li> {@link PlaybackStateCompat#ACTION_PLAY_PAUSE}</li> 473 * <li> {@link PlaybackStateCompat#ACTION_PAUSE}</li> 474 * <li> {@link PlaybackStateCompat#ACTION_STOP}</li> 475 * <li> {@link PlaybackStateCompat#ACTION_FAST_FORWARD}</li> 476 * <li> {@link PlaybackStateCompat#ACTION_SKIP_TO_NEXT}</li> 477 * <li> {@link PlaybackStateCompat#ACTION_SEEK_TO}</li> 478 * <li> {@link PlaybackStateCompat#ACTION_SET_RATING}</li> 479 * <li> {@link PlaybackStateCompat#ACTION_PLAY_FROM_MEDIA_ID}</li> 480 * <li> {@link PlaybackStateCompat#ACTION_PLAY_FROM_SEARCH}</li> 481 * <li> {@link PlaybackStateCompat#ACTION_SKIP_TO_QUEUE_ITEM}</li> 482 * <li> {@link PlaybackStateCompat#ACTION_PLAY_FROM_URI}</li> 483 * <li> {@link PlaybackStateCompat#ACTION_PREPARE}</li> 484 * <li> {@link PlaybackStateCompat#ACTION_PREPARE_FROM_MEDIA_ID}</li> 485 * <li> {@link PlaybackStateCompat#ACTION_PREPARE_FROM_SEARCH}</li> 486 * <li> {@link PlaybackStateCompat#ACTION_PREPARE_FROM_URI}</li> 487 * </ul> 488 */ 489 @Actions 490 public long getActions() { 491 return mActions; 492 } 493 494 /** 495 * Get the list of custom actions. 496 */ 497 public List<PlaybackStateCompat.CustomAction> getCustomActions() { 498 return mCustomActions; 499 } 500 501 /** 502 * Get a user readable error message. This should be set when the state is 503 * {@link PlaybackStateCompat#STATE_ERROR}. 504 */ 505 public CharSequence getErrorMessage() { 506 return mErrorMessage; 507 } 508 509 /** 510 * Get the elapsed real time at which position was last updated. If the 511 * position has never been set this will return 0; 512 * 513 * @return The last time the position was updated. 514 */ 515 public long getLastPositionUpdateTime() { 516 return mUpdateTime; 517 } 518 519 /** 520 * Get the id of the currently active item in the queue. If there is no 521 * queue or a queue is not supported by the session this will be 522 * {@link MediaSessionCompat.QueueItem#UNKNOWN_ID}. 523 * 524 * @return The id of the currently active item in the queue or 525 * {@link MediaSessionCompat.QueueItem#UNKNOWN_ID}. 526 */ 527 public long getActiveQueueItemId() { 528 return mActiveItemId; 529 } 530 531 /** 532 * Get any custom extras that were set on this playback state. 533 * 534 * @return The extras for this state or null. 535 */ 536 public @Nullable Bundle getExtras() { 537 return mExtras; 538 } 539 540 /** 541 * Creates an instance from a framework {@link android.media.session.PlaybackState} object. 542 * <p> 543 * This method is only supported on API 21+. 544 * </p> 545 * 546 * @param stateObj A {@link android.media.session.PlaybackState} object, or null if none. 547 * @return An equivalent {@link PlaybackStateCompat} object, or null if none. 548 */ 549 public static PlaybackStateCompat fromPlaybackState(Object stateObj) { 550 if (stateObj == null || Build.VERSION.SDK_INT < 21) { 551 return null; 552 } 553 554 List<Object> customActionObjs = PlaybackStateCompatApi21.getCustomActions(stateObj); 555 List<PlaybackStateCompat.CustomAction> customActions = null; 556 if (customActionObjs != null) { 557 customActions = new ArrayList<>(customActionObjs.size()); 558 for (Object customActionObj : customActionObjs) { 559 customActions.add(CustomAction.fromCustomAction(customActionObj)); 560 } 561 } 562 Bundle extras = Build.VERSION.SDK_INT >= 22 563 ? PlaybackStateCompatApi22.getExtras(stateObj) 564 : null; 565 PlaybackStateCompat state = new PlaybackStateCompat( 566 PlaybackStateCompatApi21.getState(stateObj), 567 PlaybackStateCompatApi21.getPosition(stateObj), 568 PlaybackStateCompatApi21.getBufferedPosition(stateObj), 569 PlaybackStateCompatApi21.getPlaybackSpeed(stateObj), 570 PlaybackStateCompatApi21.getActions(stateObj), 571 PlaybackStateCompatApi21.getErrorMessage(stateObj), 572 PlaybackStateCompatApi21.getLastPositionUpdateTime(stateObj), 573 customActions, 574 PlaybackStateCompatApi21.getActiveQueueItemId(stateObj), 575 extras); 576 state.mStateObj = stateObj; 577 return state; 578 } 579 580 /** 581 * Gets the underlying framework {@link android.media.session.PlaybackState} object. 582 * <p> 583 * This method is only supported on API 21+. 584 * </p> 585 * 586 * @return An equivalent {@link android.media.session.PlaybackState} object, or null if none. 587 */ 588 public Object getPlaybackState() { 589 if (mStateObj != null || Build.VERSION.SDK_INT < 21) { 590 return mStateObj; 591 } 592 593 List<Object> customActions = null; 594 if (mCustomActions != null) { 595 customActions = new ArrayList<>(mCustomActions.size()); 596 for (PlaybackStateCompat.CustomAction customAction : mCustomActions) { 597 customActions.add(customAction.getCustomAction()); 598 } 599 } 600 if (Build.VERSION.SDK_INT >= 22) { 601 mStateObj = PlaybackStateCompatApi22.newInstance(mState, mPosition, mBufferedPosition, 602 mSpeed, mActions, mErrorMessage, mUpdateTime, 603 customActions, mActiveItemId, mExtras); 604 } else { 605 mStateObj = PlaybackStateCompatApi21.newInstance(mState, mPosition, mBufferedPosition, 606 mSpeed, mActions, mErrorMessage, mUpdateTime, 607 customActions, mActiveItemId); 608 } 609 return mStateObj; 610 } 611 612 public static final Parcelable.Creator<PlaybackStateCompat> CREATOR = 613 new Parcelable.Creator<PlaybackStateCompat>() { 614 @Override 615 public PlaybackStateCompat createFromParcel(Parcel in) { 616 return new PlaybackStateCompat(in); 617 } 618 619 @Override 620 public PlaybackStateCompat[] newArray(int size) { 621 return new PlaybackStateCompat[size]; 622 } 623 }; 624 625 /** 626 * {@link PlaybackStateCompat.CustomAction CustomActions} can be used to 627 * extend the capabilities of the standard transport controls by exposing 628 * app specific actions to {@link MediaControllerCompat Controllers}. 629 */ 630 public static final class CustomAction implements Parcelable { 631 private final String mAction; 632 private final CharSequence mName; 633 private final int mIcon; 634 private final Bundle mExtras; 635 636 private Object mCustomActionObj; 637 638 /** 639 * Use {@link PlaybackStateCompat.CustomAction.Builder#build()}. 640 */ 641 private CustomAction(String action, CharSequence name, int icon, Bundle extras) { 642 mAction = action; 643 mName = name; 644 mIcon = icon; 645 mExtras = extras; 646 } 647 648 private CustomAction(Parcel in) { 649 mAction = in.readString(); 650 mName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); 651 mIcon = in.readInt(); 652 mExtras = in.readBundle(); 653 } 654 655 @Override 656 public void writeToParcel(Parcel dest, int flags) { 657 dest.writeString(mAction); 658 TextUtils.writeToParcel(mName, dest, flags); 659 dest.writeInt(mIcon); 660 dest.writeBundle(mExtras); 661 } 662 663 @Override 664 public int describeContents() { 665 return 0; 666 } 667 668 /** 669 * Creates an instance from a framework 670 * {@link android.media.session.PlaybackState.CustomAction} object. 671 * <p> 672 * This method is only supported on API 21+. 673 * </p> 674 * 675 * @param customActionObj A {@link android.media.session.PlaybackState.CustomAction} object, 676 * or null if none. 677 * @return An equivalent {@link PlaybackStateCompat.CustomAction} object, or null if none. 678 */ 679 public static PlaybackStateCompat.CustomAction fromCustomAction(Object customActionObj) { 680 if (customActionObj == null || Build.VERSION.SDK_INT < 21) { 681 return null; 682 } 683 684 PlaybackStateCompat.CustomAction customAction = new PlaybackStateCompat.CustomAction( 685 PlaybackStateCompatApi21.CustomAction.getAction(customActionObj), 686 PlaybackStateCompatApi21.CustomAction.getName(customActionObj), 687 PlaybackStateCompatApi21.CustomAction.getIcon(customActionObj), 688 PlaybackStateCompatApi21.CustomAction.getExtras(customActionObj)); 689 customAction.mCustomActionObj = customActionObj; 690 return customAction; 691 } 692 693 /** 694 * Gets the underlying framework {@link android.media.session.PlaybackState.CustomAction} 695 * object. 696 * <p> 697 * This method is only supported on API 21+. 698 * </p> 699 * 700 * @return An equivalent {@link android.media.session.PlaybackState.CustomAction} object, 701 * or null if none. 702 */ 703 public Object getCustomAction() { 704 if (mCustomActionObj != null || Build.VERSION.SDK_INT < 21) { 705 return mCustomActionObj; 706 } 707 708 mCustomActionObj = PlaybackStateCompatApi21.CustomAction.newInstance(mAction, 709 mName, mIcon, mExtras); 710 return mCustomActionObj; 711 } 712 713 public static final Parcelable.Creator<PlaybackStateCompat.CustomAction> CREATOR 714 = new Parcelable.Creator<PlaybackStateCompat.CustomAction>() { 715 716 @Override 717 public PlaybackStateCompat.CustomAction createFromParcel(Parcel p) { 718 return new PlaybackStateCompat.CustomAction(p); 719 } 720 721 @Override 722 public PlaybackStateCompat.CustomAction[] newArray(int size) { 723 return new PlaybackStateCompat.CustomAction[size]; 724 } 725 }; 726 727 /** 728 * Returns the action of the {@link CustomAction}. 729 * 730 * @return The action of the {@link CustomAction}. 731 */ 732 public String getAction() { 733 return mAction; 734 } 735 736 /** 737 * Returns the display name of this action. e.g. "Favorite" 738 * 739 * @return The display name of this {@link CustomAction}. 740 */ 741 public CharSequence getName() { 742 return mName; 743 } 744 745 /** 746 * Returns the resource id of the icon in the {@link MediaSessionCompat 747 * Session's} package. 748 * 749 * @return The resource id of the icon in the {@link MediaSessionCompat 750 * Session's} package. 751 */ 752 public int getIcon() { 753 return mIcon; 754 } 755 756 /** 757 * Returns extras which provide additional application-specific 758 * information about the action, or null if none. These arguments are 759 * meant to be consumed by a {@link MediaControllerCompat} if it knows 760 * how to handle them. 761 * 762 * @return Optional arguments for the {@link CustomAction}. 763 */ 764 public Bundle getExtras() { 765 return mExtras; 766 } 767 768 @Override 769 public String toString() { 770 return "Action:" + 771 "mName='" + mName + 772 ", mIcon=" + mIcon + 773 ", mExtras=" + mExtras; 774 } 775 776 /** 777 * Builder for {@link CustomAction} objects. 778 */ 779 public static final class Builder { 780 private final String mAction; 781 private final CharSequence mName; 782 private final int mIcon; 783 private Bundle mExtras; 784 785 /** 786 * Creates a {@link CustomAction} builder with the id, name, and 787 * icon set. 788 * 789 * @param action The action of the {@link CustomAction}. 790 * @param name The display name of the {@link CustomAction}. This 791 * name will be displayed along side the action if the UI 792 * supports it. 793 * @param icon The icon resource id of the {@link CustomAction}. 794 * This resource id must be in the same package as the 795 * {@link MediaSessionCompat}. It will be displayed with 796 * the custom action if the UI supports it. 797 */ 798 public Builder(String action, CharSequence name, int icon) { 799 if (TextUtils.isEmpty(action)) { 800 throw new IllegalArgumentException( 801 "You must specify an action to build a CustomAction."); 802 } 803 if (TextUtils.isEmpty(name)) { 804 throw new IllegalArgumentException( 805 "You must specify a name to build a CustomAction."); 806 } 807 if (icon == 0) { 808 throw new IllegalArgumentException( 809 "You must specify an icon resource id to build a CustomAction."); 810 } 811 mAction = action; 812 mName = name; 813 mIcon = icon; 814 } 815 816 /** 817 * Set optional extras for the {@link CustomAction}. These extras 818 * are meant to be consumed by a {@link MediaControllerCompat} if it 819 * knows how to handle them. Keys should be fully qualified (e.g. 820 * "com.example.MY_ARG") to avoid collisions. 821 * 822 * @param extras Optional extras for the {@link CustomAction}. 823 * @return this. 824 */ 825 public Builder setExtras(Bundle extras) { 826 mExtras = extras; 827 return this; 828 } 829 830 /** 831 * Build and return the {@link CustomAction} instance with the 832 * specified values. 833 * 834 * @return A new {@link CustomAction} instance. 835 */ 836 public CustomAction build() { 837 return new CustomAction(mAction, mName, mIcon, mExtras); 838 } 839 } 840 } 841 842 /** 843 * Builder for {@link PlaybackStateCompat} objects. 844 */ 845 public static final class Builder { 846 private final List<PlaybackStateCompat.CustomAction> mCustomActions = new ArrayList<>(); 847 848 private int mState; 849 private long mPosition; 850 private long mBufferedPosition; 851 private float mRate; 852 private long mActions; 853 private CharSequence mErrorMessage; 854 private long mUpdateTime; 855 private long mActiveItemId = MediaSessionCompat.QueueItem.UNKNOWN_ID; 856 private Bundle mExtras; 857 858 /** 859 * Create an empty Builder. 860 */ 861 public Builder() { 862 } 863 864 /** 865 * Create a Builder using a {@link PlaybackStateCompat} instance to set the 866 * initial values. 867 * 868 * @param source The playback state to copy. 869 */ 870 public Builder(PlaybackStateCompat source) { 871 mState = source.mState; 872 mPosition = source.mPosition; 873 mRate = source.mSpeed; 874 mUpdateTime = source.mUpdateTime; 875 mBufferedPosition = source.mBufferedPosition; 876 mActions = source.mActions; 877 mErrorMessage = source.mErrorMessage; 878 if (source.mCustomActions != null) { 879 mCustomActions.addAll(source.mCustomActions); 880 } 881 mActiveItemId = source.mActiveItemId; 882 mExtras = source.mExtras; 883 } 884 885 /** 886 * Set the current state of playback. 887 * <p> 888 * The position must be in ms and indicates the current playback 889 * position within the track. If the position is unknown use 890 * {@link #PLAYBACK_POSITION_UNKNOWN}. 891 * <p> 892 * The rate is a multiple of normal playback and should be 0 when paused 893 * and negative when rewinding. Normal playback rate is 1.0. 894 * <p> 895 * The state must be one of the following: 896 * <ul> 897 * <li> {@link PlaybackStateCompat#STATE_NONE}</li> 898 * <li> {@link PlaybackStateCompat#STATE_STOPPED}</li> 899 * <li> {@link PlaybackStateCompat#STATE_PLAYING}</li> 900 * <li> {@link PlaybackStateCompat#STATE_PAUSED}</li> 901 * <li> {@link PlaybackStateCompat#STATE_FAST_FORWARDING}</li> 902 * <li> {@link PlaybackStateCompat#STATE_REWINDING}</li> 903 * <li> {@link PlaybackStateCompat#STATE_BUFFERING}</li> 904 * <li> {@link PlaybackStateCompat#STATE_ERROR}</li> 905 * <li> {@link PlaybackStateCompat#STATE_CONNECTING}</li> 906 * <li> {@link PlaybackStateCompat#STATE_SKIPPING_TO_PREVIOUS}</li> 907 * <li> {@link PlaybackStateCompat#STATE_SKIPPING_TO_NEXT}</li> 908 * <li> {@link PlaybackStateCompat#STATE_SKIPPING_TO_QUEUE_ITEM}</li> 909 * </ul> 910 * 911 * @param state The current state of playback. 912 * @param position The position in the current track in ms. 913 * @param playbackSpeed The current rate of playback as a multiple of 914 * normal playback. 915 */ 916 public Builder setState(@State int state, long position, float playbackSpeed) { 917 return setState(state, position, playbackSpeed, SystemClock.elapsedRealtime()); 918 } 919 920 /** 921 * Set the current state of playback. 922 * <p> 923 * The position must be in ms and indicates the current playback 924 * position within the track. If the position is unknown use 925 * {@link #PLAYBACK_POSITION_UNKNOWN}. 926 * <p> 927 * The rate is a multiple of normal playback and should be 0 when paused 928 * and negative when rewinding. Normal playback rate is 1.0. 929 * <p> 930 * The state must be one of the following: 931 * <ul> 932 * <li> {@link PlaybackStateCompat#STATE_NONE}</li> 933 * <li> {@link PlaybackStateCompat#STATE_STOPPED}</li> 934 * <li> {@link PlaybackStateCompat#STATE_PLAYING}</li> 935 * <li> {@link PlaybackStateCompat#STATE_PAUSED}</li> 936 * <li> {@link PlaybackStateCompat#STATE_FAST_FORWARDING}</li> 937 * <li> {@link PlaybackStateCompat#STATE_REWINDING}</li> 938 * <li> {@link PlaybackStateCompat#STATE_BUFFERING}</li> 939 * <li> {@link PlaybackStateCompat#STATE_ERROR}</li> 940 * <li> {@link PlaybackStateCompat#STATE_CONNECTING}</li> 941 * <li> {@link PlaybackStateCompat#STATE_SKIPPING_TO_PREVIOUS}</li> 942 * <li> {@link PlaybackStateCompat#STATE_SKIPPING_TO_NEXT}</li> 943 * <li> {@link PlaybackStateCompat#STATE_SKIPPING_TO_QUEUE_ITEM}</li> 944 * </ul> 945 * 946 * @param state The current state of playback. 947 * @param position The position in the current item in ms. 948 * @param playbackSpeed The current speed of playback as a multiple of 949 * normal playback. 950 * @param updateTime The time in the {@link SystemClock#elapsedRealtime} 951 * timebase that the position was updated at. 952 * @return this 953 */ 954 public Builder setState(@State int state, long position, float playbackSpeed, 955 long updateTime) { 956 mState = state; 957 mPosition = position; 958 mUpdateTime = updateTime; 959 mRate = playbackSpeed; 960 return this; 961 } 962 963 /** 964 * Set the current buffered position in ms. This is the farthest 965 * playback point that can be reached from the current position using 966 * only buffered content. 967 * 968 * @return this 969 */ 970 public Builder setBufferedPosition(long bufferPosition) { 971 mBufferedPosition = bufferPosition; 972 return this; 973 } 974 975 /** 976 * Set the current capabilities available on this session. This should 977 * use a bitmask of the available capabilities. 978 * <ul> 979 * <li> {@link PlaybackStateCompat#ACTION_SKIP_TO_PREVIOUS}</li> 980 * <li> {@link PlaybackStateCompat#ACTION_REWIND}</li> 981 * <li> {@link PlaybackStateCompat#ACTION_PLAY}</li> 982 * <li> {@link PlaybackStateCompat#ACTION_PLAY_PAUSE}</li> 983 * <li> {@link PlaybackStateCompat#ACTION_PAUSE}</li> 984 * <li> {@link PlaybackStateCompat#ACTION_STOP}</li> 985 * <li> {@link PlaybackStateCompat#ACTION_FAST_FORWARD}</li> 986 * <li> {@link PlaybackStateCompat#ACTION_SKIP_TO_NEXT}</li> 987 * <li> {@link PlaybackStateCompat#ACTION_SEEK_TO}</li> 988 * <li> {@link PlaybackStateCompat#ACTION_SET_RATING}</li> 989 * <li> {@link PlaybackStateCompat#ACTION_PLAY_FROM_MEDIA_ID}</li> 990 * <li> {@link PlaybackStateCompat#ACTION_PLAY_FROM_SEARCH}</li> 991 * <li> {@link PlaybackStateCompat#ACTION_SKIP_TO_QUEUE_ITEM}</li> 992 * <li> {@link PlaybackStateCompat#ACTION_PLAY_FROM_URI}</li> 993 * <li> {@link PlaybackStateCompat#ACTION_PREPARE}</li> 994 * <li> {@link PlaybackStateCompat#ACTION_PREPARE_FROM_MEDIA_ID}</li> 995 * <li> {@link PlaybackStateCompat#ACTION_PREPARE_FROM_SEARCH}</li> 996 * <li> {@link PlaybackStateCompat#ACTION_PREPARE_FROM_URI}</li> 997 * </ul> 998 * 999 * @return this 1000 */ 1001 public Builder setActions(@Actions long capabilities) { 1002 mActions = capabilities; 1003 return this; 1004 } 1005 1006 /** 1007 * Add a custom action to the playback state. Actions can be used to 1008 * expose additional functionality to {@link MediaControllerCompat 1009 * Controllers} beyond what is offered by the standard transport 1010 * controls. 1011 * <p> 1012 * e.g. start a radio station based on the current item or skip ahead by 1013 * 30 seconds. 1014 * 1015 * @param action An identifier for this action. It can be sent back to 1016 * the {@link MediaSessionCompat} through 1017 * {@link MediaControllerCompat.TransportControls#sendCustomAction(String, Bundle)}. 1018 * @param name The display name for the action. If text is shown with 1019 * the action or used for accessibility, this is what should 1020 * be used. 1021 * @param icon The resource action of the icon that should be displayed 1022 * for the action. The resource should be in the package of 1023 * the {@link MediaSessionCompat}. 1024 * @return this 1025 */ 1026 public Builder addCustomAction(String action, String name, int icon) { 1027 return addCustomAction(new PlaybackStateCompat.CustomAction(action, name, icon, null)); 1028 } 1029 1030 /** 1031 * Add a custom action to the playback state. Actions can be used to expose additional 1032 * functionality to {@link MediaControllerCompat Controllers} beyond what is offered 1033 * by the standard transport controls. 1034 * <p> 1035 * An example of an action would be to start a radio station based on the current item 1036 * or to skip ahead by 30 seconds. 1037 * 1038 * @param customAction The custom action to add to the {@link PlaybackStateCompat}. 1039 * @return this 1040 */ 1041 public Builder addCustomAction(PlaybackStateCompat.CustomAction customAction) { 1042 if (customAction == null) { 1043 throw new IllegalArgumentException( 1044 "You may not add a null CustomAction to PlaybackStateCompat."); 1045 } 1046 mCustomActions.add(customAction); 1047 return this; 1048 } 1049 1050 /** 1051 * Set the active item in the play queue by specifying its id. The 1052 * default value is {@link MediaSessionCompat.QueueItem#UNKNOWN_ID} 1053 * 1054 * @param id The id of the active item. 1055 * @return this 1056 */ 1057 public Builder setActiveQueueItemId(long id) { 1058 mActiveItemId = id; 1059 return this; 1060 } 1061 1062 /** 1063 * Set a user readable error message. This should be set when the state 1064 * is {@link PlaybackStateCompat#STATE_ERROR}. 1065 * 1066 * @return this 1067 */ 1068 public Builder setErrorMessage(CharSequence errorMessage) { 1069 mErrorMessage = errorMessage; 1070 return this; 1071 } 1072 1073 /** 1074 * Set any custom extras to be included with the playback state. 1075 * 1076 * @param extras The extras to include. 1077 * @return this 1078 */ 1079 public Builder setExtras(Bundle extras) { 1080 mExtras = extras; 1081 return this; 1082 } 1083 1084 /** 1085 * Creates the playback state object. 1086 */ 1087 public PlaybackStateCompat build() { 1088 return new PlaybackStateCompat(mState, mPosition, mBufferedPosition, 1089 mRate, mActions, mErrorMessage, mUpdateTime, 1090 mCustomActions, mActiveItemId, mExtras); 1091 } 1092 } 1093} 1094