MediaRouteDescriptor.java revision 893feff5ea67f24af45af6dc9d6d09640ea66b86
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 */
16package android.support.v7.media;
17
18import android.content.IntentFilter;
19import android.content.IntentSender;
20import android.net.Uri;
21import android.os.Bundle;
22import android.text.TextUtils;
23
24import java.util.ArrayList;
25import java.util.Arrays;
26import java.util.Collection;
27import java.util.Collections;
28import java.util.List;
29
30/**
31 * Describes the properties of a route.
32 * <p>
33 * Each route is uniquely identified by an opaque id string.  This token
34 * may take any form as long as it is unique within the media route provider.
35 * </p><p>
36 * This object is immutable once created using a {@link Builder} instance.
37 * </p>
38 */
39public final class MediaRouteDescriptor {
40    private static final String KEY_ID = "id";
41    private static final String KEY_GROUP_MEMBER_IDS = "groupMemberIds";
42    private static final String KEY_NAME = "name";
43    private static final String KEY_DESCRIPTION = "status";
44    private static final String KEY_ICON_URI = "iconUri";
45    private static final String KEY_ENABLED = "enabled";
46    private static final String KEY_CONNECTING = "connecting";
47    private static final String KEY_CONNECTION_STATE = "connectionState";
48    private static final String KEY_CONTROL_FILTERS = "controlFilters";
49    private static final String KEY_PLAYBACK_TYPE = "playbackType";
50    private static final String KEY_PLAYBACK_STREAM = "playbackStream";
51    private static final String KEY_DEVICE_TYPE = "deviceType";
52    private static final String KEY_VOLUME = "volume";
53    private static final String KEY_VOLUME_MAX = "volumeMax";
54    private static final String KEY_VOLUME_HANDLING = "volumeHandling";
55    private static final String KEY_PRESENTATION_DISPLAY_ID = "presentationDisplayId";
56    private static final String KEY_EXTRAS = "extras";
57    private static final String KEY_CAN_DISCONNECT = "canDisconnect";
58    private static final String KEY_SETTINGS_INTENT = "settingsIntent";
59
60    private final Bundle mBundle;
61    private List<IntentFilter> mControlFilters;
62
63    private MediaRouteDescriptor(Bundle bundle, List<IntentFilter> controlFilters) {
64        mBundle = bundle;
65        mControlFilters = controlFilters;
66    }
67
68    /**
69     * Gets the unique id of the route.
70     * <p>
71     * The route id associated with a route descriptor functions as a stable
72     * identifier for the route and must be unique among all routes offered
73     * by the provider.
74     * </p>
75     */
76    public String getId() {
77        return mBundle.getString(KEY_ID);
78    }
79
80    /**
81     * Gets the group member ids of the route.
82     * <p>
83     * A route descriptor that has one or more group member route ids
84     * represents a route group. A member route may belong to another group.
85     * </p>
86     * @hide
87     * STOPSHIP: Unhide or remove.
88     */
89    public List<String> getGroupMemberIds() {
90        return mBundle.getStringArrayList(KEY_GROUP_MEMBER_IDS);
91    }
92
93    /**
94     * Gets the user-visible name of the route.
95     * <p>
96     * The route name identifies the destination represented by the route.
97     * It may be a user-supplied name, an alias, or device serial number.
98     * </p>
99     */
100    public String getName() {
101        return mBundle.getString(KEY_NAME);
102    }
103
104    /**
105     * Gets the user-visible description of the route.
106     * <p>
107     * The route description describes the kind of destination represented by the route.
108     * It may be a user-supplied string, a model number or brand of device.
109     * </p>
110     */
111    public String getDescription() {
112        return mBundle.getString(KEY_DESCRIPTION);
113    }
114
115    /**
116     * Gets the URI of the icon representing this route.
117     * <p>
118     * This icon will be used in picker UIs if available.
119     * </p>
120     * @hide
121     * STOPSHIP: Unhide or remove.
122     */
123    public Uri getIconUri() {
124        String iconUri = mBundle.getString(KEY_ICON_URI);
125        return iconUri == null ? null : Uri.parse(iconUri);
126    }
127
128    /**
129     * Gets whether the route is enabled.
130     */
131    public boolean isEnabled() {
132        return mBundle.getBoolean(KEY_ENABLED, true);
133    }
134
135    /**
136     * Gets whether the route is connecting.
137     * STOPSHIP: Deprecate or keep.
138     */
139    public boolean isConnecting() {
140        return mBundle.getBoolean(KEY_CONNECTING, false);
141    }
142
143    /**
144     * Gets the connection state of the route.
145     * @hide
146     * STOPSHIP: Unhide or remove.
147     */
148    public int getConnectionState() {
149        return mBundle.getInt(KEY_CONNECTION_STATE,
150                MediaRouter.RouteInfo.CONNECTION_STATE_DISCONNECTED);
151    }
152
153    /**
154     * Gets whether the route can be disconnected without stopping playback.
155     * <p>
156     * The route can normally be disconnected without stopping playback when
157     * the destination device on the route is connected to two or more source
158     * devices. The route provider should update the route immediately when the
159     * number of connected devices changes.
160     * </p><p>
161     * To specify that the route should disconnect without stopping use
162     * {@link MediaRouter#unselect(int)} with
163     * {@link MediaRouter#UNSELECT_REASON_DISCONNECTED}.
164     * </p>
165     */
166    public boolean canDisconnectAndKeepPlaying() {
167        return mBundle.getBoolean(KEY_CAN_DISCONNECT, false);
168    }
169
170    /**
171     * Gets an {@link IntentSender} for starting a settings activity for this
172     * route. The activity may have specific route settings or general settings
173     * for the connected device or route provider.
174     *
175     * @return An {@link IntentSender} to start a settings activity.
176     */
177    public IntentSender getSettingsActivity() {
178        return mBundle.getParcelable(KEY_SETTINGS_INTENT);
179    }
180
181    /**
182     * Gets the route's {@link MediaControlIntent media control intent} filters.
183     */
184    public List<IntentFilter> getControlFilters() {
185        ensureControlFilters();
186        return mControlFilters;
187    }
188
189    private void ensureControlFilters() {
190        if (mControlFilters == null) {
191            mControlFilters = mBundle.<IntentFilter>getParcelableArrayList(KEY_CONTROL_FILTERS);
192            if (mControlFilters == null) {
193                mControlFilters = Collections.<IntentFilter>emptyList();
194            }
195        }
196    }
197
198    /**
199     * Gets the route's playback type.
200     */
201    public int getPlaybackType() {
202        return mBundle.getInt(KEY_PLAYBACK_TYPE, MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE);
203    }
204
205    /**
206     * Gets the route's playback stream.
207     */
208    public int getPlaybackStream() {
209        return mBundle.getInt(KEY_PLAYBACK_STREAM, -1);
210    }
211
212    /**
213     * Gets the route's receiver device type.
214     *
215     * @hide
216     * STOPSHIP: Unhide or remove.
217     */
218    public int getDeviceType() {
219        return mBundle.getInt(KEY_DEVICE_TYPE);
220    }
221
222    /**
223     * Gets the route's current volume, or 0 if unknown.
224     */
225    public int getVolume() {
226        return mBundle.getInt(KEY_VOLUME);
227    }
228
229    /**
230     * Gets the route's maximum volume, or 0 if unknown.
231     */
232    public int getVolumeMax() {
233        return mBundle.getInt(KEY_VOLUME_MAX);
234    }
235
236    /**
237     * Gets the route's volume handling.
238     */
239    public int getVolumeHandling() {
240        return mBundle.getInt(KEY_VOLUME_HANDLING,
241                MediaRouter.RouteInfo.PLAYBACK_VOLUME_FIXED);
242    }
243
244    /**
245     * Gets the route's presentation display id, or -1 if none.
246     */
247    public int getPresentationDisplayId() {
248        return mBundle.getInt(
249                KEY_PRESENTATION_DISPLAY_ID, MediaRouter.RouteInfo.PRESENTATION_DISPLAY_ID_NONE);
250    }
251
252    /**
253     * Gets a bundle of extras for this route descriptor.
254     * The extras will be ignored by the media router but they may be used
255     * by applications.
256     */
257    public Bundle getExtras() {
258        return mBundle.getBundle(KEY_EXTRAS);
259    }
260
261    /**
262     * Returns true if the route descriptor has all of the required fields.
263     */
264    public boolean isValid() {
265        ensureControlFilters();
266        if (TextUtils.isEmpty(getId())
267                || TextUtils.isEmpty(getName())
268                || mControlFilters.contains(null)) {
269            return false;
270        }
271        return true;
272    }
273
274    @Override
275    public String toString() {
276        StringBuilder result = new StringBuilder();
277        result.append("MediaRouteDescriptor{ ");
278        result.append("id=").append(getId());
279        result.append(", groupMemberIds=").append(getGroupMemberIds());
280        result.append(", name=").append(getName());
281        result.append(", description=").append(getDescription());
282        result.append(", iconUri=").append(getIconUri());
283        result.append(", isEnabled=").append(isEnabled());
284        result.append(", isConnecting=").append(isConnecting());
285        result.append(", connectionState=").append(getConnectionState());
286        result.append(", controlFilters=").append(Arrays.toString(getControlFilters().toArray()));
287        result.append(", playbackType=").append(getPlaybackType());
288        result.append(", playbackStream=").append(getPlaybackStream());
289        result.append(", deviceType=").append(getDeviceType());
290        result.append(", volume=").append(getVolume());
291        result.append(", volumeMax=").append(getVolumeMax());
292        result.append(", volumeHandling=").append(getVolumeHandling());
293        result.append(", presentationDisplayId=").append(getPresentationDisplayId());
294        result.append(", extras=").append(getExtras());
295        result.append(", isValid=").append(isValid());
296        result.append(" }");
297        return result.toString();
298    }
299
300    /**
301     * Converts this object to a bundle for serialization.
302     *
303     * @return The contents of the object represented as a bundle.
304     */
305    public Bundle asBundle() {
306        return mBundle;
307    }
308
309    /**
310     * Creates an instance from a bundle.
311     *
312     * @param bundle The bundle, or null if none.
313     * @return The new instance, or null if the bundle was null.
314     */
315    public static MediaRouteDescriptor fromBundle(Bundle bundle) {
316        return bundle != null ? new MediaRouteDescriptor(bundle, null) : null;
317    }
318
319    /**
320     * Builder for {@link MediaRouteDescriptor media route descriptors}.
321     */
322    public static final class Builder {
323        private final Bundle mBundle;
324        private ArrayList<String> mGroupMemberIds;
325        private ArrayList<IntentFilter> mControlFilters;
326
327        /**
328         * Creates a media route descriptor builder.
329         *
330         * @param id The unique id of the route.
331         * @param name The user-visible name of the route.
332         */
333        public Builder(String id, String name) {
334            mBundle = new Bundle();
335            setId(id);
336            setName(name);
337        }
338
339        /**
340         * Creates a media route descriptor builder whose initial contents are
341         * copied from an existing descriptor.
342         */
343        public Builder(MediaRouteDescriptor descriptor) {
344            if (descriptor == null) {
345                throw new IllegalArgumentException("descriptor must not be null");
346            }
347
348            mBundle = new Bundle(descriptor.mBundle);
349
350            descriptor.ensureControlFilters();
351            if (!descriptor.mControlFilters.isEmpty()) {
352                mControlFilters = new ArrayList<IntentFilter>(descriptor.mControlFilters);
353            }
354        }
355
356        /**
357         * Sets the unique id of the route.
358         * <p>
359         * The route id associated with a route descriptor functions as a stable
360         * identifier for the route and must be unique among all routes offered
361         * by the provider.
362         * </p>
363         */
364        public Builder setId(String id) {
365            mBundle.putString(KEY_ID, id);
366            return this;
367        }
368
369        /**
370         * Adds a group member id of the route.
371         * <p>
372         * A route descriptor that has one or more group member route ids
373         * represents a route group. A member route may belong to another group.
374         * </p>
375         * @hide
376         * STOPSHIP: Unhide or remove.
377         */
378        public Builder addGroupMemberId(String groupMemberId) {
379            if (TextUtils.isEmpty(groupMemberId)) {
380                throw new IllegalArgumentException("groupMemberId must not be empty");
381            }
382
383            if (mGroupMemberIds == null) {
384                mGroupMemberIds = new ArrayList<>();
385            }
386            if (!mGroupMemberIds.contains(groupMemberId)) {
387                mGroupMemberIds.add(groupMemberId);
388            }
389            return this;
390        }
391
392        /**
393         * Adds a list of group member ids of the route.
394         * <p>
395         * A route descriptor that has one or more group member route ids
396         * represents a route group. A member route may belong to another group.
397         * </p>
398         * @hide
399         * STOPSHIP: Unhide or remove.
400         */
401        public Builder addGroupMemberIds(Collection<String> groupMemberIds) {
402            if (groupMemberIds == null) {
403                throw new IllegalArgumentException("groupMemberIds must not be null");
404            }
405
406            if (!groupMemberIds.isEmpty()) {
407                for (String groupMemberId : groupMemberIds) {
408                    addGroupMemberId(groupMemberId);
409                }
410            }
411            return this;
412        }
413
414        /**
415         * Sets the user-visible name of the route.
416         * <p>
417         * The route name identifies the destination represented by the route.
418         * It may be a user-supplied name, an alias, or device serial number.
419         * </p>
420         */
421        public Builder setName(String name) {
422            mBundle.putString(KEY_NAME, name);
423            return this;
424        }
425
426        /**
427         * Sets the user-visible description of the route.
428         * <p>
429         * The route description describes the kind of destination represented by the route.
430         * It may be a user-supplied string, a model number or brand of device.
431         * </p>
432         */
433        public Builder setDescription(String description) {
434            mBundle.putString(KEY_DESCRIPTION, description);
435            return this;
436        }
437
438        /**
439         * Sets the URI of the icon representing this route.
440         * <p>
441         * This icon will be used in picker UIs if available.
442         * </p><p>
443         * The URI must be one of the following formats:
444         * <ul>
445         * <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})</li>
446         * <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE})
447         * </li>
448         * <li>file ({@link android.content.ContentResolver#SCHEME_FILE})</li>
449         * </ul>
450         * </p>
451         * @hide
452         * STOPSHIP: Unhide or remove.
453         */
454        public Builder setIconUri(Uri iconUri) {
455            if (iconUri == null) {
456                throw new IllegalArgumentException("iconUri must not be null");
457            }
458            mBundle.putString(KEY_ICON_URI, iconUri.toString());
459            return this;
460        }
461
462        /**
463         * Sets whether the route is enabled.
464         * <p>
465         * Disabled routes represent routes that a route provider knows about, such as paired
466         * Wifi Display receivers, but that are not currently available for use.
467         * </p>
468         */
469        public Builder setEnabled(boolean enabled) {
470            mBundle.putBoolean(KEY_ENABLED, enabled);
471            return this;
472        }
473
474        /**
475         * Sets whether the route is in the process of connecting and is not yet
476         * ready for use.
477         * STOPSHIP: Deprecate or keep.
478         */
479        public Builder setConnecting(boolean connecting) {
480            mBundle.putBoolean(KEY_CONNECTING, connecting);
481            return this;
482        }
483
484        /**
485         * Sets the route's connection state.
486         * @hide
487         * STOPSHIP: Unhide or remove.
488         */
489        public Builder setConnectionState(int connectionState) {
490            mBundle.putInt(KEY_CONNECTION_STATE, connectionState);
491            return this;
492        }
493
494        /**
495         * Sets whether the route can be disconnected without stopping playback.
496         */
497        public Builder setCanDisconnect(boolean canDisconnect) {
498            mBundle.putBoolean(KEY_CAN_DISCONNECT, canDisconnect);
499            return this;
500        }
501
502        /**
503         * Sets an intent sender for launching the settings activity for this
504         * route.
505         */
506        public Builder setSettingsActivity(IntentSender is) {
507            mBundle.putParcelable(KEY_SETTINGS_INTENT, is);
508            return this;
509        }
510
511        /**
512         * Adds a {@link MediaControlIntent media control intent} filter for the route.
513         */
514        public Builder addControlFilter(IntentFilter filter) {
515            if (filter == null) {
516                throw new IllegalArgumentException("filter must not be null");
517            }
518
519            if (mControlFilters == null) {
520                mControlFilters = new ArrayList<IntentFilter>();
521            }
522            if (!mControlFilters.contains(filter)) {
523                mControlFilters.add(filter);
524            }
525            return this;
526        }
527
528        /**
529         * Adds a list of {@link MediaControlIntent media control intent} filters for the route.
530         */
531        public Builder addControlFilters(Collection<IntentFilter> filters) {
532            if (filters == null) {
533                throw new IllegalArgumentException("filters must not be null");
534            }
535
536            if (!filters.isEmpty()) {
537                for (IntentFilter filter : filters) {
538                    addControlFilter(filter);
539                }
540            }
541            return this;
542        }
543
544        /**
545         * Sets the route's playback type.
546         */
547        public Builder setPlaybackType(int playbackType) {
548            mBundle.putInt(KEY_PLAYBACK_TYPE, playbackType);
549            return this;
550        }
551
552        /**
553         * Sets the route's playback stream.
554         */
555        public Builder setPlaybackStream(int playbackStream) {
556            mBundle.putInt(KEY_PLAYBACK_STREAM, playbackStream);
557            return this;
558        }
559
560        /**
561         * Sets the route's receiver device type.
562         *
563         * @hide
564         * STOPSHIP: Unhide or remove.
565         */
566        public Builder setDeviceType(int deviceType) {
567            mBundle.putInt(KEY_DEVICE_TYPE, deviceType);
568            return this;
569        }
570
571        /**
572         * Sets the route's current volume, or 0 if unknown.
573         */
574        public Builder setVolume(int volume) {
575            mBundle.putInt(KEY_VOLUME, volume);
576            return this;
577        }
578
579        /**
580         * Sets the route's maximum volume, or 0 if unknown.
581         */
582        public Builder setVolumeMax(int volumeMax) {
583            mBundle.putInt(KEY_VOLUME_MAX, volumeMax);
584            return this;
585        }
586
587        /**
588         * Sets the route's volume handling.
589         */
590        public Builder setVolumeHandling(int volumeHandling) {
591            mBundle.putInt(KEY_VOLUME_HANDLING, volumeHandling);
592            return this;
593        }
594
595        /**
596         * Sets the route's presentation display id, or -1 if none.
597         */
598        public Builder setPresentationDisplayId(int presentationDisplayId) {
599            mBundle.putInt(KEY_PRESENTATION_DISPLAY_ID, presentationDisplayId);
600            return this;
601        }
602
603        /**
604         * Sets a bundle of extras for this route descriptor.
605         * The extras will be ignored by the media router but they may be used
606         * by applications.
607         */
608        public Builder setExtras(Bundle extras) {
609            mBundle.putBundle(KEY_EXTRAS, extras);
610            return this;
611        }
612
613        /**
614         * Builds the {@link MediaRouteDescriptor media route descriptor}.
615         */
616        public MediaRouteDescriptor build() {
617            if (mControlFilters != null) {
618                mBundle.putParcelableArrayList(KEY_CONTROL_FILTERS, mControlFilters);
619            }
620            if (mGroupMemberIds != null) {
621                mBundle.putStringArrayList(KEY_GROUP_MEMBER_IDS, mGroupMemberIds);
622            }
623            return new MediaRouteDescriptor(mBundle, mControlFilters);
624        }
625    }
626}