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