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