1/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.support.v7.media;
18
19import android.app.PendingIntent;
20import android.os.Bundle;
21import android.os.SystemClock;
22import android.support.v4.util.TimeUtils;
23
24/**
25 * Describes the playback status of a media session.
26 * <p>
27 * This class is part of the remote playback protocol described by the
28 * {@link MediaControlIntent MediaControlIntent} class.
29 * </p><p>
30 * When a media session is created, it is initially in the
31 * {@link #SESSION_STATE_ACTIVE active} state.  When the media session ends
32 * normally, it transitions to the {@link #SESSION_STATE_ENDED ended} state.
33 * If the media session is invalidated due to another session forcibly taking
34 * control of the route, then it transitions to the
35 * {@link #SESSION_STATE_INVALIDATED invalidated} state.
36 * Refer to the documentation of each state for an explanation of its meaning.
37 * </p><p>
38 * To monitor session status, the application should supply a {@link PendingIntent} to use as the
39 * {@link MediaControlIntent#EXTRA_SESSION_STATUS_UPDATE_RECEIVER session status update receiver}
40 * for a given {@link MediaControlIntent#ACTION_START_SESSION session start request}.
41 * </p><p>
42 * This object is immutable once created using a {@link Builder} instance.
43 * </p>
44 */
45public final class MediaSessionStatus {
46    private static final String KEY_TIMESTAMP = "timestamp";
47    private static final String KEY_SESSION_STATE = "sessionState";
48    private static final String KEY_QUEUE_PAUSED = "queuePaused";
49    private static final String KEY_EXTRAS = "extras";
50
51    private final Bundle mBundle;
52
53    /**
54     * Session state: Active.
55     * <p>
56     * Indicates that the media session is active and in control of the route.
57     * </p>
58     */
59    public static final int SESSION_STATE_ACTIVE = 0;
60
61    /**
62     * Session state: Ended.
63     * <p>
64     * Indicates that the media session was ended normally using the
65     * {@link MediaControlIntent#ACTION_END_SESSION end session} action.
66     * </p><p>
67     * A terminated media session cannot be used anymore.  To play more media, the
68     * application must start a new session.
69     * </p>
70     */
71    public static final int SESSION_STATE_ENDED = 1;
72
73    /**
74     * Session state: Invalidated.
75     * <p>
76     * Indicates that the media session was invalidated involuntarily due to
77     * another session taking control of the route.
78     * </p><p>
79     * An invalidated media session cannot be used anymore.  To play more media, the
80     * application must start a new session.
81     * </p>
82     */
83    public static final int SESSION_STATE_INVALIDATED = 2;
84
85    private MediaSessionStatus(Bundle bundle) {
86        mBundle = bundle;
87    }
88
89    /**
90     * Gets the timestamp associated with the status information in
91     * milliseconds since boot in the {@link SystemClock#elapsedRealtime} time base.
92     *
93     * @return The status timestamp in the {@link SystemClock#elapsedRealtime()} time base.
94     */
95    public long getTimestamp() {
96        return mBundle.getLong(KEY_TIMESTAMP);
97    }
98
99    /**
100     * Gets the session state.
101     *
102     * @return The session state.  One of {@link #SESSION_STATE_ACTIVE},
103     * {@link #SESSION_STATE_ENDED}, or {@link #SESSION_STATE_INVALIDATED}.
104     */
105    public int getSessionState() {
106        return mBundle.getInt(KEY_SESSION_STATE, SESSION_STATE_INVALIDATED);
107    }
108
109    /**
110     * Returns true if the session's queue is paused.
111     *
112     * @return True if the session's queue is paused.
113     */
114    public boolean isQueuePaused() {
115        return mBundle.getBoolean(KEY_QUEUE_PAUSED);
116    }
117
118    /**
119     * Gets a bundle of extras for this status object.
120     * The extras will be ignored by the media router but they may be used
121     * by applications.
122     */
123    public Bundle getExtras() {
124        return mBundle.getBundle(KEY_EXTRAS);
125    }
126
127    @Override
128    public String toString() {
129        StringBuilder result = new StringBuilder();
130        result.append("MediaSessionStatus{ ");
131        result.append("timestamp=");
132        TimeUtils.formatDuration(SystemClock.elapsedRealtime() - getTimestamp(), result);
133        result.append(" ms ago");
134        result.append(", sessionState=").append(sessionStateToString(getSessionState()));
135        result.append(", queuePaused=").append(isQueuePaused());
136        result.append(", extras=").append(getExtras());
137        result.append(" }");
138        return result.toString();
139    }
140
141    private static String sessionStateToString(int sessionState) {
142        switch (sessionState) {
143            case SESSION_STATE_ACTIVE:
144                return "active";
145            case SESSION_STATE_ENDED:
146                return "ended";
147            case SESSION_STATE_INVALIDATED:
148                return "invalidated";
149        }
150        return Integer.toString(sessionState);
151    }
152
153    /**
154     * Converts this object to a bundle for serialization.
155     *
156     * @return The contents of the object represented as a bundle.
157     */
158    public Bundle asBundle() {
159        return mBundle;
160    }
161
162    /**
163     * Creates an instance from a bundle.
164     *
165     * @param bundle The bundle, or null if none.
166     * @return The new instance, or null if the bundle was null.
167     */
168    public static MediaSessionStatus fromBundle(Bundle bundle) {
169        return bundle != null ? new MediaSessionStatus(bundle) : null;
170    }
171
172    /**
173     * Builder for {@link MediaSessionStatus media session status objects}.
174     */
175    public static final class Builder {
176        private final Bundle mBundle;
177
178        /**
179         * Creates a media session status builder using the current time as the
180         * reference timestamp.
181         *
182         * @param sessionState The session state.
183         */
184        public Builder(int sessionState) {
185            mBundle = new Bundle();
186            setTimestamp(SystemClock.elapsedRealtime());
187            setSessionState(sessionState);
188        }
189
190        /**
191         * Creates a media session status builder whose initial contents are
192         * copied from an existing status.
193         */
194        public Builder(MediaSessionStatus status) {
195            if (status == null) {
196                throw new IllegalArgumentException("status must not be null");
197            }
198
199            mBundle = new Bundle(status.mBundle);
200        }
201
202        /**
203         * Sets the timestamp associated with the status information in
204         * milliseconds since boot in the {@link SystemClock#elapsedRealtime} time base.
205         */
206        public Builder setTimestamp(long elapsedRealtimeTimestamp) {
207            mBundle.putLong(KEY_TIMESTAMP, elapsedRealtimeTimestamp);
208            return this;
209        }
210
211        /**
212         * Sets the session state.
213         */
214        public Builder setSessionState(int sessionState) {
215            mBundle.putInt(KEY_SESSION_STATE, sessionState);
216            return this;
217        }
218
219        /**
220         * Sets whether the queue is paused.
221         */
222        public Builder setQueuePaused(boolean queuePaused) {
223            mBundle.putBoolean(KEY_QUEUE_PAUSED, queuePaused);
224            return this;
225        }
226
227        /**
228         * Sets a bundle of extras for this status object.
229         * The extras will be ignored by the media router but they may be used
230         * by applications.
231         */
232        public Builder setExtras(Bundle extras) {
233            mBundle.putBundle(KEY_EXTRAS, extras);
234            return this;
235        }
236
237        /**
238         * Builds the {@link MediaSessionStatus media session status object}.
239         */
240        public MediaSessionStatus build() {
241            return new MediaSessionStatus(mBundle);
242        }
243    }
244}
245