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