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