/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.support.v7.media; import android.content.IntentFilter; import android.content.IntentSender; import android.net.Uri; import android.os.Bundle; import android.text.TextUtils; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; /** * Describes the properties of a route. *

* Each route is uniquely identified by an opaque id string. This token * may take any form as long as it is unique within the media route provider. *

* This object is immutable once created using a {@link Builder} instance. *

*/ public final class MediaRouteDescriptor { private static final String KEY_ID = "id"; private static final String KEY_GROUP_MEMBER_IDS = "groupMemberIds"; private static final String KEY_NAME = "name"; private static final String KEY_DESCRIPTION = "status"; private static final String KEY_ICON_URI = "iconUri"; private static final String KEY_ENABLED = "enabled"; private static final String KEY_CONNECTING = "connecting"; private static final String KEY_CONNECTION_STATE = "connectionState"; private static final String KEY_CONTROL_FILTERS = "controlFilters"; private static final String KEY_PLAYBACK_TYPE = "playbackType"; private static final String KEY_PLAYBACK_STREAM = "playbackStream"; private static final String KEY_DEVICE_TYPE = "deviceType"; private static final String KEY_VOLUME = "volume"; private static final String KEY_VOLUME_MAX = "volumeMax"; private static final String KEY_VOLUME_HANDLING = "volumeHandling"; private static final String KEY_PRESENTATION_DISPLAY_ID = "presentationDisplayId"; private static final String KEY_EXTRAS = "extras"; private static final String KEY_CAN_DISCONNECT = "canDisconnect"; private static final String KEY_SETTINGS_INTENT = "settingsIntent"; private final Bundle mBundle; private List mControlFilters; private MediaRouteDescriptor(Bundle bundle, List controlFilters) { mBundle = bundle; mControlFilters = controlFilters; } /** * Gets the unique id of the route. *

* The route id associated with a route descriptor functions as a stable * identifier for the route and must be unique among all routes offered * by the provider. *

*/ public String getId() { return mBundle.getString(KEY_ID); } /** * Gets the group member ids of the route. *

* A route descriptor that has one or more group member route ids * represents a route group. A member route may belong to another group. *

* @hide */ public List getGroupMemberIds() { return mBundle.getStringArrayList(KEY_GROUP_MEMBER_IDS); } /** * Gets the user-visible name of the route. *

* The route name identifies the destination represented by the route. * It may be a user-supplied name, an alias, or device serial number. *

*/ public String getName() { return mBundle.getString(KEY_NAME); } /** * Gets the user-visible description of the route. *

* The route description describes the kind of destination represented by the route. * It may be a user-supplied string, a model number or brand of device. *

*/ public String getDescription() { return mBundle.getString(KEY_DESCRIPTION); } /** * Gets the URI of the icon representing this route. *

* This icon will be used in picker UIs if available. *

*/ public Uri getIconUri() { String iconUri = mBundle.getString(KEY_ICON_URI); return iconUri == null ? null : Uri.parse(iconUri); } /** * Gets whether the route is enabled. */ public boolean isEnabled() { return mBundle.getBoolean(KEY_ENABLED, true); } /** * Gets whether the route is connecting. * @deprecated Use {@link #getConnectionState} instead */ @Deprecated public boolean isConnecting() { return mBundle.getBoolean(KEY_CONNECTING, false); } /** * Gets the connection state of the route. * * @return The connection state of this route: * {@link MediaRouter.RouteInfo#CONNECTION_STATE_DISCONNECTED}, * {@link MediaRouter.RouteInfo#CONNECTION_STATE_CONNECTING}, or * {@link MediaRouter.RouteInfo#CONNECTION_STATE_CONNECTED}. */ public int getConnectionState() { return mBundle.getInt(KEY_CONNECTION_STATE, MediaRouter.RouteInfo.CONNECTION_STATE_DISCONNECTED); } /** * Gets whether the route can be disconnected without stopping playback. *

* The route can normally be disconnected without stopping playback when * the destination device on the route is connected to two or more source * devices. The route provider should update the route immediately when the * number of connected devices changes. *

* To specify that the route should disconnect without stopping use * {@link MediaRouter#unselect(int)} with * {@link MediaRouter#UNSELECT_REASON_DISCONNECTED}. *

*/ public boolean canDisconnectAndKeepPlaying() { return mBundle.getBoolean(KEY_CAN_DISCONNECT, false); } /** * Gets an {@link IntentSender} for starting a settings activity for this * route. The activity may have specific route settings or general settings * for the connected device or route provider. * * @return An {@link IntentSender} to start a settings activity. */ public IntentSender getSettingsActivity() { return mBundle.getParcelable(KEY_SETTINGS_INTENT); } /** * Gets the route's {@link MediaControlIntent media control intent} filters. */ public List getControlFilters() { ensureControlFilters(); return mControlFilters; } private void ensureControlFilters() { if (mControlFilters == null) { mControlFilters = mBundle.getParcelableArrayList(KEY_CONTROL_FILTERS); if (mControlFilters == null) { mControlFilters = Collections.emptyList(); } } } /** * Gets the type of playback associated with this route. * * @return The type of playback associated with this route: * {@link MediaRouter.RouteInfo#PLAYBACK_TYPE_LOCAL} or * {@link MediaRouter.RouteInfo#PLAYBACK_TYPE_REMOTE}. */ public int getPlaybackType() { return mBundle.getInt(KEY_PLAYBACK_TYPE, MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE); } /** * Gets the route's playback stream. */ public int getPlaybackStream() { return mBundle.getInt(KEY_PLAYBACK_STREAM, -1); } /** * Gets the type of the receiver device associated with this route. * * @return The type of the receiver device associated with this route: * {@link MediaRouter.RouteInfo#DEVICE_TYPE_TV} or * {@link MediaRouter.RouteInfo#DEVICE_TYPE_SPEAKER}. */ public int getDeviceType() { return mBundle.getInt(KEY_DEVICE_TYPE); } /** * Gets the route's current volume, or 0 if unknown. */ public int getVolume() { return mBundle.getInt(KEY_VOLUME); } /** * Gets the route's maximum volume, or 0 if unknown. */ public int getVolumeMax() { return mBundle.getInt(KEY_VOLUME_MAX); } /** * Gets information about how volume is handled on the route. * * @return How volume is handled on the route: * {@link MediaRouter.RouteInfo#PLAYBACK_VOLUME_FIXED} or * {@link MediaRouter.RouteInfo#PLAYBACK_VOLUME_VARIABLE}. */ public int getVolumeHandling() { return mBundle.getInt(KEY_VOLUME_HANDLING, MediaRouter.RouteInfo.PLAYBACK_VOLUME_FIXED); } /** * Gets the route's presentation display id, or -1 if none. */ public int getPresentationDisplayId() { return mBundle.getInt( KEY_PRESENTATION_DISPLAY_ID, MediaRouter.RouteInfo.PRESENTATION_DISPLAY_ID_NONE); } /** * Gets a bundle of extras for this route descriptor. * The extras will be ignored by the media router but they may be used * by applications. */ public Bundle getExtras() { return mBundle.getBundle(KEY_EXTRAS); } /** * Returns true if the route descriptor has all of the required fields. */ public boolean isValid() { ensureControlFilters(); if (TextUtils.isEmpty(getId()) || TextUtils.isEmpty(getName()) || mControlFilters.contains(null)) { return false; } return true; } @Override public String toString() { StringBuilder result = new StringBuilder(); result.append("MediaRouteDescriptor{ "); result.append("id=").append(getId()); result.append(", groupMemberIds=").append(getGroupMemberIds()); result.append(", name=").append(getName()); result.append(", description=").append(getDescription()); result.append(", iconUri=").append(getIconUri()); result.append(", isEnabled=").append(isEnabled()); result.append(", isConnecting=").append(isConnecting()); result.append(", connectionState=").append(getConnectionState()); result.append(", controlFilters=").append(Arrays.toString(getControlFilters().toArray())); result.append(", playbackType=").append(getPlaybackType()); result.append(", playbackStream=").append(getPlaybackStream()); result.append(", deviceType=").append(getDeviceType()); result.append(", volume=").append(getVolume()); result.append(", volumeMax=").append(getVolumeMax()); result.append(", volumeHandling=").append(getVolumeHandling()); result.append(", presentationDisplayId=").append(getPresentationDisplayId()); result.append(", extras=").append(getExtras()); result.append(", isValid=").append(isValid()); result.append(" }"); return result.toString(); } /** * Converts this object to a bundle for serialization. * * @return The contents of the object represented as a bundle. */ public Bundle asBundle() { return mBundle; } /** * Creates an instance from a bundle. * * @param bundle The bundle, or null if none. * @return The new instance, or null if the bundle was null. */ public static MediaRouteDescriptor fromBundle(Bundle bundle) { return bundle != null ? new MediaRouteDescriptor(bundle, null) : null; } /** * Builder for {@link MediaRouteDescriptor media route descriptors}. */ public static final class Builder { private final Bundle mBundle; private ArrayList mGroupMemberIds; private ArrayList mControlFilters; /** * Creates a media route descriptor builder. * * @param id The unique id of the route. * @param name The user-visible name of the route. */ public Builder(String id, String name) { mBundle = new Bundle(); setId(id); setName(name); } /** * Creates a media route descriptor builder whose initial contents are * copied from an existing descriptor. */ public Builder(MediaRouteDescriptor descriptor) { if (descriptor == null) { throw new IllegalArgumentException("descriptor must not be null"); } mBundle = new Bundle(descriptor.mBundle); descriptor.ensureControlFilters(); if (!descriptor.mControlFilters.isEmpty()) { mControlFilters = new ArrayList(descriptor.mControlFilters); } } /** * Sets the unique id of the route. *

* The route id associated with a route descriptor functions as a stable * identifier for the route and must be unique among all routes offered * by the provider. *

*/ public Builder setId(String id) { mBundle.putString(KEY_ID, id); return this; } /** * Adds a group member id of the route. *

* A route descriptor that has one or more group member route ids * represents a route group. A member route may belong to another group. *

* @hide */ public Builder addGroupMemberId(String groupMemberId) { if (TextUtils.isEmpty(groupMemberId)) { throw new IllegalArgumentException("groupMemberId must not be empty"); } if (mGroupMemberIds == null) { mGroupMemberIds = new ArrayList<>(); } if (!mGroupMemberIds.contains(groupMemberId)) { mGroupMemberIds.add(groupMemberId); } return this; } /** * Adds a list of group member ids of the route. *

* A route descriptor that has one or more group member route ids * represents a route group. A member route may belong to another group. *

* @hide */ public Builder addGroupMemberIds(Collection groupMemberIds) { if (groupMemberIds == null) { throw new IllegalArgumentException("groupMemberIds must not be null"); } if (!groupMemberIds.isEmpty()) { for (String groupMemberId : groupMemberIds) { addGroupMemberId(groupMemberId); } } return this; } /** * Sets the user-visible name of the route. *

* The route name identifies the destination represented by the route. * It may be a user-supplied name, an alias, or device serial number. *

*/ public Builder setName(String name) { mBundle.putString(KEY_NAME, name); return this; } /** * Sets the user-visible description of the route. *

* The route description describes the kind of destination represented by the route. * It may be a user-supplied string, a model number or brand of device. *

*/ public Builder setDescription(String description) { mBundle.putString(KEY_DESCRIPTION, description); return this; } /** * Sets the URI of the icon representing this route. *

* This icon will be used in picker UIs if available. *

* The URI must be one of the following formats: *

    *
  • content ({@link android.content.ContentResolver#SCHEME_CONTENT})
  • *
  • android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE}) *
  • *
  • file ({@link android.content.ContentResolver#SCHEME_FILE})
  • *
*

*/ public Builder setIconUri(Uri iconUri) { if (iconUri == null) { throw new IllegalArgumentException("iconUri must not be null"); } mBundle.putString(KEY_ICON_URI, iconUri.toString()); return this; } /** * Sets whether the route is enabled. *

* Disabled routes represent routes that a route provider knows about, such as paired * Wifi Display receivers, but that are not currently available for use. *

*/ public Builder setEnabled(boolean enabled) { mBundle.putBoolean(KEY_ENABLED, enabled); return this; } /** * Sets whether the route is in the process of connecting and is not yet * ready for use. * @deprecated Use {@link #setConnectionState} instead. */ @Deprecated public Builder setConnecting(boolean connecting) { mBundle.putBoolean(KEY_CONNECTING, connecting); return this; } /** * Sets the route's connection state. * * @param connectionState The connection state of the route: * {@link MediaRouter.RouteInfo#CONNECTION_STATE_DISCONNECTED}, * {@link MediaRouter.RouteInfo#CONNECTION_STATE_CONNECTING}, or * {@link MediaRouter.RouteInfo#CONNECTION_STATE_CONNECTED}. */ public Builder setConnectionState(int connectionState) { mBundle.putInt(KEY_CONNECTION_STATE, connectionState); return this; } /** * Sets whether the route can be disconnected without stopping playback. */ public Builder setCanDisconnect(boolean canDisconnect) { mBundle.putBoolean(KEY_CAN_DISCONNECT, canDisconnect); return this; } /** * Sets an intent sender for launching the settings activity for this * route. */ public Builder setSettingsActivity(IntentSender is) { mBundle.putParcelable(KEY_SETTINGS_INTENT, is); return this; } /** * Adds a {@link MediaControlIntent media control intent} filter for the route. */ public Builder addControlFilter(IntentFilter filter) { if (filter == null) { throw new IllegalArgumentException("filter must not be null"); } if (mControlFilters == null) { mControlFilters = new ArrayList(); } if (!mControlFilters.contains(filter)) { mControlFilters.add(filter); } return this; } /** * Adds a list of {@link MediaControlIntent media control intent} filters for the route. */ public Builder addControlFilters(Collection filters) { if (filters == null) { throw new IllegalArgumentException("filters must not be null"); } if (!filters.isEmpty()) { for (IntentFilter filter : filters) { addControlFilter(filter); } } return this; } /** * Sets the route's playback type. * * @param playbackType The playback type of the route: * {@link MediaRouter.RouteInfo#PLAYBACK_TYPE_LOCAL} or * {@link MediaRouter.RouteInfo#PLAYBACK_TYPE_REMOTE}. */ public Builder setPlaybackType(int playbackType) { mBundle.putInt(KEY_PLAYBACK_TYPE, playbackType); return this; } /** * Sets the route's playback stream. */ public Builder setPlaybackStream(int playbackStream) { mBundle.putInt(KEY_PLAYBACK_STREAM, playbackStream); return this; } /** * Sets the route's receiver device type. * * @param deviceType The receive device type of the route: * {@link MediaRouter.RouteInfo#DEVICE_TYPE_TV} or * {@link MediaRouter.RouteInfo#DEVICE_TYPE_SPEAKER}. */ public Builder setDeviceType(int deviceType) { mBundle.putInt(KEY_DEVICE_TYPE, deviceType); return this; } /** * Sets the route's current volume, or 0 if unknown. */ public Builder setVolume(int volume) { mBundle.putInt(KEY_VOLUME, volume); return this; } /** * Sets the route's maximum volume, or 0 if unknown. */ public Builder setVolumeMax(int volumeMax) { mBundle.putInt(KEY_VOLUME_MAX, volumeMax); return this; } /** * Sets the route's volume handling. * * @param volumeHandling how volume is handled on the route: * {@link MediaRouter.RouteInfo#PLAYBACK_VOLUME_FIXED} or * {@link MediaRouter.RouteInfo#PLAYBACK_VOLUME_VARIABLE}. */ public Builder setVolumeHandling(int volumeHandling) { mBundle.putInt(KEY_VOLUME_HANDLING, volumeHandling); return this; } /** * Sets the route's presentation display id, or -1 if none. */ public Builder setPresentationDisplayId(int presentationDisplayId) { mBundle.putInt(KEY_PRESENTATION_DISPLAY_ID, presentationDisplayId); return this; } /** * Sets a bundle of extras for this route descriptor. * The extras will be ignored by the media router but they may be used * by applications. */ public Builder setExtras(Bundle extras) { mBundle.putBundle(KEY_EXTRAS, extras); return this; } /** * Builds the {@link MediaRouteDescriptor media route descriptor}. */ public MediaRouteDescriptor build() { if (mControlFilters != null) { mBundle.putParcelableArrayList(KEY_CONTROL_FILTERS, mControlFilters); } if (mGroupMemberIds != null) { mBundle.putStringArrayList(KEY_GROUP_MEMBER_IDS, mGroupMemberIds); } return new MediaRouteDescriptor(mBundle, mControlFilters); } } }