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