blob: e5ad569bb24f4c337a4f481367ef0a2387fe93cf [file] [log] [blame]
/*
* Copyright 2019 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.media;
import static android.media.MediaRouter2Utils.toUniqueId;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
/**
* Describes the properties of a route.
*/
public final class MediaRoute2Info implements Parcelable {
@NonNull
public static final Creator<MediaRoute2Info> CREATOR = new Creator<MediaRoute2Info>() {
@Override
public MediaRoute2Info createFromParcel(Parcel in) {
return new MediaRoute2Info(in);
}
@Override
public MediaRoute2Info[] newArray(int size) {
return new MediaRoute2Info[size];
}
};
/** @hide */
@IntDef({CONNECTION_STATE_DISCONNECTED, CONNECTION_STATE_CONNECTING,
CONNECTION_STATE_CONNECTED})
@Retention(RetentionPolicy.SOURCE)
public @interface ConnectionState {}
/**
* The default connection state indicating the route is disconnected.
*
* @see #getConnectionState
*/
public static final int CONNECTION_STATE_DISCONNECTED = 0;
/**
* A connection state indicating the route is in the process of connecting and is not yet
* ready for use.
*
* @see #getConnectionState
*/
public static final int CONNECTION_STATE_CONNECTING = 1;
/**
* A connection state indicating the route is connected.
*
* @see #getConnectionState
*/
public static final int CONNECTION_STATE_CONNECTED = 2;
/** @hide */
@IntDef({PLAYBACK_VOLUME_FIXED, PLAYBACK_VOLUME_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface PlaybackVolume {}
/**
* Playback information indicating the playback volume is fixed, i&#46;e&#46; it cannot be
* controlled from this object. An example of fixed playback volume is a remote player,
* playing over HDMI where the user prefers to control the volume on the HDMI sink, rather
* than attenuate at the source.
*
* @see #getVolumeHandling()
*/
public static final int PLAYBACK_VOLUME_FIXED = 0;
/**
* Playback information indicating the playback volume is variable and can be controlled
* from this object.
*
* @see #getVolumeHandling()
*/
public static final int PLAYBACK_VOLUME_VARIABLE = 1;
/** @hide */
@IntDef({
TYPE_UNKNOWN, TYPE_BUILTIN_SPEAKER, TYPE_WIRED_HEADSET,
TYPE_WIRED_HEADPHONES, TYPE_BLUETOOTH_A2DP, TYPE_HDMI, TYPE_USB_DEVICE,
TYPE_USB_ACCESSORY, TYPE_DOCK, TYPE_USB_HEADSET, TYPE_HEARING_AID,
TYPE_REMOTE_TV, TYPE_REMOTE_SPEAKER, TYPE_GROUP})
@Retention(RetentionPolicy.SOURCE)
public @interface Type {}
/**
* The default route type indicating the type is unknown.
*
* @see #getType
* @hide
*/
public static final int TYPE_UNKNOWN = 0;
/**
* A route type describing the speaker system (i.e. a mono speaker or stereo speakers) built
* in a device.
*
* @see #getType
* @hide
*/
public static final int TYPE_BUILTIN_SPEAKER = AudioDeviceInfo.TYPE_BUILTIN_SPEAKER;
/**
* A route type describing a headset, which is the combination of a headphones and microphone.
*
* @see #getType
* @hide
*/
public static final int TYPE_WIRED_HEADSET = AudioDeviceInfo.TYPE_WIRED_HEADSET;
/**
* A route type describing a pair of wired headphones.
*
* @see #getType
* @hide
*/
public static final int TYPE_WIRED_HEADPHONES = AudioDeviceInfo.TYPE_WIRED_HEADPHONES;
/**
* A route type indicating the presentation of the media is happening
* on a bluetooth device such as a bluetooth speaker.
*
* @see #getType
* @hide
*/
public static final int TYPE_BLUETOOTH_A2DP = AudioDeviceInfo.TYPE_BLUETOOTH_A2DP;
/**
* A route type describing an HDMI connection.
*
* @see #getType
* @hide
*/
public static final int TYPE_HDMI = AudioDeviceInfo.TYPE_HDMI;
/**
* A route type describing a USB audio device.
*
* @see #getType
* @hide
*/
public static final int TYPE_USB_DEVICE = AudioDeviceInfo.TYPE_USB_DEVICE;
/**
* A route type describing a USB audio device in accessory mode.
*
* @see #getType
* @hide
*/
public static final int TYPE_USB_ACCESSORY = AudioDeviceInfo.TYPE_USB_ACCESSORY;
/**
* A route type describing the audio device associated with a dock.
*
* @see #getType
* @hide
*/
public static final int TYPE_DOCK = AudioDeviceInfo.TYPE_DOCK;
/**
* A device type describing a USB audio headset.
*
* @see #getType
* @hide
*/
public static final int TYPE_USB_HEADSET = AudioDeviceInfo.TYPE_USB_HEADSET;
/**
* A route type describing a Hearing Aid.
*
* @see #getType
* @hide
*/
public static final int TYPE_HEARING_AID = AudioDeviceInfo.TYPE_HEARING_AID;
/**
* A route type indicating the presentation of the media is happening on a TV.
*
* @see #getType
* @hide
*/
public static final int TYPE_REMOTE_TV = 1001;
/**
* A route type indicating the presentation of the media is happening on a speaker.
*
* @see #getType
* @hide
*/
public static final int TYPE_REMOTE_SPEAKER = 1002;
/**
* A route type indicating the presentation of the media is happening on multiple devices.
*
* @see #getType
* @hide
*/
public static final int TYPE_GROUP = 2000;
/**
* Media feature: Live audio.
* <p>
* A route that supports live audio routing will allow the media audio stream
* to be sent to supported destinations. This can include internal speakers or
* audio jacks on the device itself, A2DP devices, and more.
* </p><p>
* When a live audio route is selected, audio routing is transparent to the application.
* All audio played on the media stream will be routed to the selected destination.
* </p><p>
* Refer to the class documentation for details about live audio routes.
* </p>
*/
public static final String FEATURE_LIVE_AUDIO = "android.media.route.feature.LIVE_AUDIO";
/**
* Media feature: Live video.
* <p>
* A route that supports live video routing will allow a mirrored version
* of the device's primary display or a customized
* {@link android.app.Presentation Presentation} to be sent to supported
* destinations.
* </p><p>
* When a live video route is selected, audio and video routing is transparent
* to the application. By default, audio and video is routed to the selected
* destination. For certain live video routes, the application may also use a
* {@link android.app.Presentation Presentation} to replace the mirrored view
* on the external display with different content.
* </p><p>
* Refer to the class documentation for details about live video routes.
* </p>
*
* @see android.app.Presentation
*/
public static final String FEATURE_LIVE_VIDEO = "android.media.route.feature.LIVE_VIDEO";
/**
* Media feature: Remote playback.
* <p>
* A route that supports remote playback routing will allow an application to send
* requests to play content remotely to supported destinations.
* A route may only support {@link #FEATURE_REMOTE_AUDIO_PLAYBACK audio playback} or
* {@link #FEATURE_REMOTE_VIDEO_PLAYBACK video playback}.
* </p><p>
* Remote playback routes destinations operate independently of the local device.
* When a remote playback route is selected, the application can control the content
* playing on the destination using {@link MediaRouter2.RoutingController#getControlHints()}.
* The application may also receive status updates from the route regarding remote playback.
* </p><p>
* Refer to the class documentation for details about remote playback routes.
* </p>
* @see #FEATURE_REMOTE_AUDIO_PLAYBACK
* @see #FEATURE_REMOTE_VIDEO_PLAYBACK
*/
public static final String FEATURE_REMOTE_PLAYBACK =
"android.media.route.feature.REMOTE_PLAYBACK";
/**
* Media feature: Remote audio playback.
* <p>
* A route that supports remote audio playback routing will allow an application to send
* requests to play audio content remotely to supported destinations.
*
* @see #FEATURE_REMOTE_PLAYBACK
* @see #FEATURE_REMOTE_VIDEO_PLAYBACK
*/
public static final String FEATURE_REMOTE_AUDIO_PLAYBACK =
"android.media.route.feature.REMOTE_AUDIO_PLAYBACK";
/**
* Media feature: Remote video playback.
* <p>
* A route that supports remote video playback routing will allow an application to send
* requests to play video content remotely to supported destinations.
*
* @see #FEATURE_REMOTE_PLAYBACK
* @see #FEATURE_REMOTE_AUDIO_PLAYBACK
*/
public static final String FEATURE_REMOTE_VIDEO_PLAYBACK =
"android.media.route.feature.REMOTE_VIDEO_PLAYBACK";
final String mId;
final CharSequence mName;
final List<String> mFeatures;
@Type
final int mType;
final boolean mIsSystem;
final Uri mIconUri;
final CharSequence mDescription;
@ConnectionState
final int mConnectionState;
final String mClientPackageName;
final int mVolume;
final int mVolumeMax;
final int mVolumeHandling;
final Bundle mExtras;
final String mProviderId;
MediaRoute2Info(@NonNull Builder builder) {
mId = builder.mId;
mName = builder.mName;
mFeatures = builder.mFeatures;
mType = builder.mType;
mIsSystem = builder.mIsSystem;
mIconUri = builder.mIconUri;
mDescription = builder.mDescription;
mConnectionState = builder.mConnectionState;
mClientPackageName = builder.mClientPackageName;
mVolumeHandling = builder.mVolumeHandling;
mVolumeMax = builder.mVolumeMax;
mVolume = builder.mVolume;
mExtras = builder.mExtras;
mProviderId = builder.mProviderId;
}
MediaRoute2Info(@NonNull Parcel in) {
mId = in.readString();
mName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
mFeatures = in.createStringArrayList();
mType = in.readInt();
mIsSystem = in.readBoolean();
mIconUri = in.readParcelable(null);
mDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
mConnectionState = in.readInt();
mClientPackageName = in.readString();
mVolumeHandling = in.readInt();
mVolumeMax = in.readInt();
mVolume = in.readInt();
mExtras = in.readBundle();
mProviderId = in.readString();
}
/**
* Gets the id of the route. The routes which are given by {@link MediaRouter2} will have
* unique IDs.
* <p>
* In order to ensure uniqueness in {@link MediaRouter2} side, the value of this method
* can be different from what was set in {@link MediaRoute2ProviderService}.
*
* @see Builder#Builder(String, CharSequence)
*/
@NonNull
public String getId() {
if (mProviderId != null) {
return toUniqueId(mProviderId, mId);
} else {
return mId;
}
}
/**
* Gets the user-visible name of the route.
*/
@NonNull
public CharSequence getName() {
return mName;
}
/**
* Gets the supported features of the route.
*/
@NonNull
public List<String> getFeatures() {
return mFeatures;
}
/**
* Gets the type of this route.
*
* @return The type of this route:
* {@link #TYPE_UNKNOWN},
* {@link #TYPE_BUILTIN_SPEAKER}, {@link #TYPE_WIRED_HEADSET}, {@link #TYPE_WIRED_HEADPHONES},
* {@link #TYPE_BLUETOOTH_A2DP}, {@link #TYPE_HDMI}, {@link #TYPE_DOCK},
* {@Link #TYPE_USB_DEVICE}, {@link #TYPE_USB_ACCESSORY}, {@link #TYPE_USB_HEADSET}
* {@link #TYPE_HEARING_AID},
* {@link #TYPE_REMOTE_TV}, {@link #TYPE_REMOTE_SPEAKER}, {@link #TYPE_GROUP}.
* @hide
*/
@Type
public int getType() {
return mType;
}
/**
* Returns whether the route is a system route or not.
* <p>
* System routes are media routes directly controlled by the system
* such as phone speaker, wired headset, and Bluetooth devices.
* </p>
*/
public boolean isSystemRoute() {
return mIsSystem;
}
/**
* Gets the URI of the icon representing this route.
* <p>
* This icon will be used in picker UIs if available.
*
* @return The URI of the icon representing this route, or null if none.
*/
@Nullable
public Uri getIconUri() {
return mIconUri;
}
/**
* Gets the user-visible description of the route.
*/
@Nullable
public CharSequence getDescription() {
return mDescription;
}
/**
* Gets the connection state of the route.
*
* @return The connection state of this route: {@link #CONNECTION_STATE_DISCONNECTED},
* {@link #CONNECTION_STATE_CONNECTING}, or {@link #CONNECTION_STATE_CONNECTED}.
*/
@ConnectionState
public int getConnectionState() {
return mConnectionState;
}
/**
* Gets the package name of the app using the route.
* Returns null if no apps are using this route.
*/
@Nullable
public String getClientPackageName() {
return mClientPackageName;
}
/**
* Gets information about how volume is handled on the route.
*
* @return {@link #PLAYBACK_VOLUME_FIXED} or {@link #PLAYBACK_VOLUME_VARIABLE}
*/
@PlaybackVolume
public int getVolumeHandling() {
return mVolumeHandling;
}
/**
* Gets the maximum volume of the route.
*/
public int getVolumeMax() {
return mVolumeMax;
}
/**
* Gets the current volume of the route. This may be invalid if the route is not selected.
*/
public int getVolume() {
return mVolume;
}
@Nullable
public Bundle getExtras() {
return mExtras == null ? null : new Bundle(mExtras);
}
/**
* Gets the original id set by {@link Builder#Builder(String, CharSequence)}.
* @hide
*/
@NonNull
@TestApi
public String getOriginalId() {
return mId;
}
/**
* Gets the provider id of the route. It is assigned automatically by
* {@link com.android.server.media.MediaRouterService}.
*
* @return provider id of the route or null if it's not set.
* @hide
*/
@Nullable
public String getProviderId() {
return mProviderId;
}
/**
* Returns if the route has at least one of the specified route features.
*
* @param features the list of route features to consider
* @return true if the route has at least one feature in the list
* @hide
*/
public boolean hasAnyFeatures(@NonNull Collection<String> features) {
Objects.requireNonNull(features, "features must not be null");
for (String feature : features) {
if (getFeatures().contains(feature)) {
return true;
}
}
return false;
}
/**
* Returns true if the route info has all of the required field.
* A route is valid if and only if it is obtained from
* {@link com.android.server.media.MediaRouterService}.
* @hide
*/
public boolean isValid() {
if (TextUtils.isEmpty(getId()) || TextUtils.isEmpty(getName())
|| TextUtils.isEmpty(getProviderId())) {
return false;
}
return true;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof MediaRoute2Info)) {
return false;
}
MediaRoute2Info other = (MediaRoute2Info) obj;
// Note: mExtras is not included.
return Objects.equals(mId, other.mId)
&& Objects.equals(mName, other.mName)
&& Objects.equals(mFeatures, other.mFeatures)
&& (mType == other.mType)
&& (mIsSystem == other.mIsSystem)
&& Objects.equals(mIconUri, other.mIconUri)
&& Objects.equals(mDescription, other.mDescription)
&& (mConnectionState == other.mConnectionState)
&& Objects.equals(mClientPackageName, other.mClientPackageName)
&& (mVolumeHandling == other.mVolumeHandling)
&& (mVolumeMax == other.mVolumeMax)
&& (mVolume == other.mVolume)
&& Objects.equals(mProviderId, other.mProviderId);
}
@Override
public int hashCode() {
// Note: mExtras is not included.
return Objects.hash(mId, mName, mFeatures, mType, mIsSystem, mIconUri, mDescription,
mConnectionState, mClientPackageName, mVolumeHandling, mVolumeMax, mVolume,
mProviderId);
}
@Override
public String toString() {
// Note: mExtras is not printed here.
StringBuilder result = new StringBuilder()
.append("MediaRoute2Info{ ")
.append("id=").append(getId())
.append(", name=").append(getName())
.append(", features=").append(getFeatures())
.append(", iconUri=").append(getIconUri())
.append(", description=").append(getDescription())
.append(", connectionState=").append(getConnectionState())
.append(", clientPackageName=").append(getClientPackageName())
.append(", volumeHandling=").append(getVolumeHandling())
.append(", volumeMax=").append(getVolumeMax())
.append(", volume=").append(getVolume())
.append(", providerId=").append(getProviderId())
.append(" }");
return result.toString();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString(mId);
TextUtils.writeToParcel(mName, dest, flags);
dest.writeStringList(mFeatures);
dest.writeInt(mType);
dest.writeBoolean(mIsSystem);
dest.writeParcelable(mIconUri, flags);
TextUtils.writeToParcel(mDescription, dest, flags);
dest.writeInt(mConnectionState);
dest.writeString(mClientPackageName);
dest.writeInt(mVolumeHandling);
dest.writeInt(mVolumeMax);
dest.writeInt(mVolume);
dest.writeBundle(mExtras);
dest.writeString(mProviderId);
}
/**
* Builder for {@link MediaRoute2Info media route info}.
*/
public static final class Builder {
final String mId;
final CharSequence mName;
final List<String> mFeatures;
@Type
int mType = TYPE_UNKNOWN;
boolean mIsSystem;
Uri mIconUri;
CharSequence mDescription;
@ConnectionState
int mConnectionState;
String mClientPackageName;
int mVolumeHandling = PLAYBACK_VOLUME_FIXED;
int mVolumeMax;
int mVolume;
Bundle mExtras;
String mProviderId;
/**
* Constructor for builder to create {@link MediaRoute2Info}.
* <p>
* In order to ensure ID uniqueness, the {@link MediaRoute2Info#getId() ID} of a route info
* obtained from {@link MediaRouter2} can be different from what was set in
* {@link MediaRoute2ProviderService}.
* </p>
* @param id The ID of the route. Must not be empty.
* @param name The user-visible name of the route.
*/
public Builder(@NonNull String id, @NonNull CharSequence name) {
if (TextUtils.isEmpty(id)) {
throw new IllegalArgumentException("id must not be empty");
}
if (TextUtils.isEmpty(name)) {
throw new IllegalArgumentException("name must not be empty");
}
mId = id;
mName = name;
mFeatures = new ArrayList<>();
}
/**
* Constructor for builder to create {@link MediaRoute2Info} with existing
* {@link MediaRoute2Info} instance.
*
* @param routeInfo the existing instance to copy data from.
*/
public Builder(@NonNull MediaRoute2Info routeInfo) {
Objects.requireNonNull(routeInfo, "routeInfo must not be null");
mId = routeInfo.mId;
mName = routeInfo.mName;
mFeatures = new ArrayList<>(routeInfo.mFeatures);
mType = routeInfo.mType;
mIsSystem = routeInfo.mIsSystem;
mIconUri = routeInfo.mIconUri;
mDescription = routeInfo.mDescription;
mConnectionState = routeInfo.mConnectionState;
mClientPackageName = routeInfo.mClientPackageName;
mVolumeHandling = routeInfo.mVolumeHandling;
mVolumeMax = routeInfo.mVolumeMax;
mVolume = routeInfo.mVolume;
if (routeInfo.mExtras != null) {
mExtras = new Bundle(routeInfo.mExtras);
}
mProviderId = routeInfo.mProviderId;
}
/**
* Constructor for builder to create {@link MediaRoute2Info} with existing
* {@link MediaRoute2Info} instance and replace ID with the given {@code id}.
*
* @param id The ID of the new route. Must not be empty.
* @param routeInfo the existing instance to copy data from.
* @hide
*/
public Builder(@NonNull String id, @NonNull MediaRoute2Info routeInfo) {
if (TextUtils.isEmpty(id)) {
throw new IllegalArgumentException("id must not be empty");
}
Objects.requireNonNull(routeInfo, "routeInfo must not be null");
mId = id;
mName = routeInfo.mName;
mFeatures = new ArrayList<>(routeInfo.mFeatures);
mType = routeInfo.mType;
mIsSystem = routeInfo.mIsSystem;
mIconUri = routeInfo.mIconUri;
mDescription = routeInfo.mDescription;
mConnectionState = routeInfo.mConnectionState;
mClientPackageName = routeInfo.mClientPackageName;
mVolumeHandling = routeInfo.mVolumeHandling;
mVolumeMax = routeInfo.mVolumeMax;
mVolume = routeInfo.mVolume;
if (routeInfo.mExtras != null) {
mExtras = new Bundle(routeInfo.mExtras);
}
mProviderId = routeInfo.mProviderId;
}
/**
* Adds a feature for the route.
* @param feature a feature that the route has. May be one of predefined features
* such as {@link #FEATURE_LIVE_AUDIO}, {@link #FEATURE_LIVE_VIDEO} or
* {@link #FEATURE_REMOTE_PLAYBACK} or a custom feature defined by
* a provider.
*
* @see #addFeatures(Collection)
*/
@NonNull
public Builder addFeature(@NonNull String feature) {
if (TextUtils.isEmpty(feature)) {
throw new IllegalArgumentException("feature must not be null or empty");
}
mFeatures.add(feature);
return this;
}
/**
* Adds features for the route. A route must support at least one route type.
* @param features features that the route has. May include predefined features
* such as {@link #FEATURE_LIVE_AUDIO}, {@link #FEATURE_LIVE_VIDEO} or
* {@link #FEATURE_REMOTE_PLAYBACK} or custom features defined by
* a provider.
*
* @see #addFeature(String)
*/
@NonNull
public Builder addFeatures(@NonNull Collection<String> features) {
Objects.requireNonNull(features, "features must not be null");
for (String feature : features) {
addFeature(feature);
}
return this;
}
/**
* Clears the features of the route. A route must support at least one route type.
*/
@NonNull
public Builder clearFeatures() {
mFeatures.clear();
return this;
}
/**
* Sets the route's type.
* @hide
*/
@NonNull
public Builder setType(@Type int type) {
mType = type;
return this;
}
/**
* Sets whether the route is a system route or not.
* @hide
*/
@NonNull
public Builder setSystemRoute(boolean isSystem) {
mIsSystem = isSystem;
return this;
}
/**
* Sets the URI of the icon representing this route.
* <p>
* This icon will be used in picker UIs if available.
* </p><p>
* The URI must be one of the following formats:
* <ul>
* <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})</li>
* <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE})
* </li>
* <li>file ({@link android.content.ContentResolver#SCHEME_FILE})</li>
* </ul>
* </p>
*/
@NonNull
public Builder setIconUri(@Nullable Uri iconUri) {
mIconUri = iconUri;
return this;
}
/**
* Sets the user-visible description of the route.
*/
@NonNull
public Builder setDescription(@Nullable CharSequence description) {
mDescription = description;
return this;
}
/**
* Sets the route's connection state.
*
* {@link #CONNECTION_STATE_DISCONNECTED},
* {@link #CONNECTION_STATE_CONNECTING}, or
* {@link #CONNECTION_STATE_CONNECTED}.
*/
@NonNull
public Builder setConnectionState(@ConnectionState int connectionState) {
mConnectionState = connectionState;
return this;
}
/**
* Sets the package name of the app using the route.
*/
@NonNull
public Builder setClientPackageName(@Nullable String packageName) {
mClientPackageName = packageName;
return this;
}
/**
* Sets the route's volume handling.
*/
@NonNull
public Builder setVolumeHandling(@PlaybackVolume int volumeHandling) {
mVolumeHandling = volumeHandling;
return this;
}
/**
* Sets the route's maximum volume, or 0 if unknown.
*/
@NonNull
public Builder setVolumeMax(int volumeMax) {
mVolumeMax = volumeMax;
return this;
}
/**
* Sets the route's current volume, or 0 if unknown.
*/
@NonNull
public Builder setVolume(int volume) {
mVolume = volume;
return this;
}
/**
* Sets a bundle of extras for the route.
* <p>
* Note: The extras will not affect the result of {@link MediaRoute2Info#equals(Object)}.
*/
@NonNull
public Builder setExtras(@Nullable Bundle extras) {
if (extras == null) {
mExtras = null;
return this;
}
mExtras = new Bundle(extras);
return this;
}
/**
* Sets the provider id of the route.
* @hide
*/
@NonNull
public Builder setProviderId(@NonNull String providerId) {
if (TextUtils.isEmpty(providerId)) {
throw new IllegalArgumentException("providerId must not be null or empty");
}
mProviderId = providerId;
return this;
}
/**
* Builds the {@link MediaRoute2Info media route info}.
*
* @throws IllegalArgumentException if no features are added.
*/
@NonNull
public MediaRoute2Info build() {
if (mFeatures.isEmpty()) {
throw new IllegalArgumentException("features must not be empty!");
}
return new MediaRoute2Info(this);
}
}
}