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