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.LongDef;
21import android.annotation.Nullable;
22import android.media.RemoteControlClient;
23import android.os.Bundle;
24import android.os.Parcel;
25import android.os.Parcelable;
26import android.os.SystemClock;
27import android.text.TextUtils;
28import java.util.ArrayList;
29import java.util.List;
30
31import java.lang.annotation.Retention;
32import 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 */
39public 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
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
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
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
342    public int describeContents() {
343        return 0;
344    }
345
346    @Override
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
378    public int getState() {
379        return mState;
380    }
381
382    /**
383     * Get the current playback position in ms.
384     */
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     */
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     */
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
434    public long getActions() {
435        return mActions;
436    }
437
438    /**
439     * Get the list of custom actions.
440     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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
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
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