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