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