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.os.Bundle; 21import android.text.TextUtils; 22 23import java.util.ArrayList; 24import java.util.Arrays; 25import java.util.Collection; 26import java.util.Collections; 27import java.util.List; 28 29/** 30 * Describes the properties of a route. 31 * <p> 32 * Each route is uniquely identified by an opaque id string. This token 33 * may take any form as long as it is unique within the media route provider. 34 * </p><p> 35 * This object is immutable once created using a {@link Builder} instance. 36 * </p> 37 */ 38public final class MediaRouteDescriptor { 39 private static final String KEY_ID = "id"; 40 private static final String KEY_NAME = "name"; 41 private static final String KEY_DESCRIPTION = "status"; 42 private static final String KEY_ENABLED = "enabled"; 43 private static final String KEY_CONNECTING = "connecting"; 44 private static final String KEY_CONTROL_FILTERS = "controlFilters"; 45 private static final String KEY_PLAYBACK_TYPE = "playbackType"; 46 private static final String KEY_PLAYBACK_STREAM = "playbackStream"; 47 private static final String KEY_VOLUME = "volume"; 48 private static final String KEY_VOLUME_MAX = "volumeMax"; 49 private static final String KEY_VOLUME_HANDLING = "volumeHandling"; 50 private static final String KEY_PRESENTATION_DISPLAY_ID = "presentationDisplayId"; 51 private static final String KEY_EXTRAS = "extras"; 52 private static final String KEY_CAN_DISCONNECT = "canDisconnect"; 53 private static final String KEY_SETTINGS_INTENT = "settingsIntent"; 54 55 private final Bundle mBundle; 56 private List<IntentFilter> mControlFilters; 57 58 private MediaRouteDescriptor(Bundle bundle, List<IntentFilter> controlFilters) { 59 mBundle = bundle; 60 mControlFilters = controlFilters; 61 } 62 63 /** 64 * Gets the unique id of the route. 65 * <p> 66 * The route id associated with a route descriptor functions as a stable 67 * identifier for the route and must be unique among all routes offered 68 * by the provider. 69 * </p> 70 */ 71 public String getId() { 72 return mBundle.getString(KEY_ID); 73 } 74 75 /** 76 * Gets the user-visible name of the route. 77 * <p> 78 * The route name identifies the destination represented by the route. 79 * It may be a user-supplied name, an alias, or device serial number. 80 * </p> 81 */ 82 public String getName() { 83 return mBundle.getString(KEY_NAME); 84 } 85 86 /** 87 * Gets the user-visible description of the route. 88 * <p> 89 * The route description describes the kind of destination represented by the route. 90 * It may be a user-supplied string, a model number or brand of device. 91 * </p> 92 */ 93 public String getDescription() { 94 return mBundle.getString(KEY_DESCRIPTION); 95 } 96 97 /** 98 * Gets whether the route is enabled. 99 */ 100 public boolean isEnabled() { 101 return mBundle.getBoolean(KEY_ENABLED, true); 102 } 103 104 /** 105 * Gets whether the route is connecting. 106 */ 107 public boolean isConnecting() { 108 return mBundle.getBoolean(KEY_CONNECTING, false); 109 } 110 111 /** 112 * Gets whether the route can be disconnected without stopping playback. To 113 * specify that the route should disconnect without stopping use 114 * {@link MediaRouter#unselect(int)} with 115 * {@link MediaRouter#UNSELECT_REASON_DISCONNECTED}. 116 */ 117 public boolean canDisconnectAndKeepPlaying() { 118 return mBundle.getBoolean(KEY_CAN_DISCONNECT, false); 119 } 120 121 /** 122 * Gets an {@link IntentSender} for starting a settings activity for this 123 * route. The activity may have specific route settings or general settings 124 * for the connected device or route provider. 125 * 126 * @return An {@link IntentSender} to start a settings activity. 127 */ 128 public IntentSender getSettingsActivity() { 129 return mBundle.getParcelable(KEY_SETTINGS_INTENT); 130 } 131 132 /** 133 * Gets the route's {@link MediaControlIntent media control intent} filters. 134 */ 135 public List<IntentFilter> getControlFilters() { 136 ensureControlFilters(); 137 return mControlFilters; 138 } 139 140 private void ensureControlFilters() { 141 if (mControlFilters == null) { 142 mControlFilters = mBundle.<IntentFilter>getParcelableArrayList(KEY_CONTROL_FILTERS); 143 if (mControlFilters == null) { 144 mControlFilters = Collections.<IntentFilter>emptyList(); 145 } 146 } 147 } 148 149 /** 150 * Gets the route's playback type. 151 */ 152 public int getPlaybackType() { 153 return mBundle.getInt(KEY_PLAYBACK_TYPE, MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE); 154 } 155 156 /** 157 * Gets the route's playback stream. 158 */ 159 public int getPlaybackStream() { 160 return mBundle.getInt(KEY_PLAYBACK_STREAM, -1); 161 } 162 163 /** 164 * Gets the route's current volume, or 0 if unknown. 165 */ 166 public int getVolume() { 167 return mBundle.getInt(KEY_VOLUME); 168 } 169 170 /** 171 * Gets the route's maximum volume, or 0 if unknown. 172 */ 173 public int getVolumeMax() { 174 return mBundle.getInt(KEY_VOLUME_MAX); 175 } 176 177 /** 178 * Gets the route's volume handling. 179 */ 180 public int getVolumeHandling() { 181 return mBundle.getInt(KEY_VOLUME_HANDLING, 182 MediaRouter.RouteInfo.PLAYBACK_VOLUME_FIXED); 183 } 184 185 /** 186 * Gets the route's presentation display id, or -1 if none. 187 */ 188 public int getPresentationDisplayId() { 189 return mBundle.getInt(KEY_PRESENTATION_DISPLAY_ID, -1); 190 } 191 192 /** 193 * Gets a bundle of extras for this route descriptor. 194 * The extras will be ignored by the media router but they may be used 195 * by applications. 196 */ 197 public Bundle getExtras() { 198 return mBundle.getBundle(KEY_EXTRAS); 199 } 200 201 /** 202 * Returns true if the route descriptor has all of the required fields. 203 */ 204 public boolean isValid() { 205 ensureControlFilters(); 206 if (TextUtils.isEmpty(getId()) 207 || TextUtils.isEmpty(getName()) 208 || mControlFilters.contains(null)) { 209 return false; 210 } 211 return true; 212 } 213 214 @Override 215 public String toString() { 216 StringBuilder result = new StringBuilder(); 217 result.append("MediaRouteDescriptor{ "); 218 result.append("id=").append(getId()); 219 result.append(", name=").append(getName()); 220 result.append(", description=").append(getDescription()); 221 result.append(", isEnabled=").append(isEnabled()); 222 result.append(", isConnecting=").append(isConnecting()); 223 result.append(", controlFilters=").append(Arrays.toString(getControlFilters().toArray())); 224 result.append(", playbackType=").append(getPlaybackType()); 225 result.append(", playbackStream=").append(getPlaybackStream()); 226 result.append(", volume=").append(getVolume()); 227 result.append(", volumeMax=").append(getVolumeMax()); 228 result.append(", volumeHandling=").append(getVolumeHandling()); 229 result.append(", presentationDisplayId=").append(getPresentationDisplayId()); 230 result.append(", extras=").append(getExtras()); 231 result.append(", isValid=").append(isValid()); 232 result.append(" }"); 233 return result.toString(); 234 } 235 236 /** 237 * Converts this object to a bundle for serialization. 238 * 239 * @return The contents of the object represented as a bundle. 240 */ 241 public Bundle asBundle() { 242 return mBundle; 243 } 244 245 /** 246 * Creates an instance from a bundle. 247 * 248 * @param bundle The bundle, or null if none. 249 * @return The new instance, or null if the bundle was null. 250 */ 251 public static MediaRouteDescriptor fromBundle(Bundle bundle) { 252 return bundle != null ? new MediaRouteDescriptor(bundle, null) : null; 253 } 254 255 /** 256 * Builder for {@link MediaRouteDescriptor media route descriptors}. 257 */ 258 public static final class Builder { 259 private final Bundle mBundle; 260 private ArrayList<IntentFilter> mControlFilters; 261 262 /** 263 * Creates a media route descriptor builder. 264 * 265 * @param id The unique id of the route. 266 * @param name The user-visible name of the route. 267 */ 268 public Builder(String id, String name) { 269 mBundle = new Bundle(); 270 setId(id); 271 setName(name); 272 } 273 274 /** 275 * Creates a media route descriptor builder whose initial contents are 276 * copied from an existing descriptor. 277 */ 278 public Builder(MediaRouteDescriptor descriptor) { 279 if (descriptor == null) { 280 throw new IllegalArgumentException("descriptor must not be null"); 281 } 282 283 mBundle = new Bundle(descriptor.mBundle); 284 285 descriptor.ensureControlFilters(); 286 if (!descriptor.mControlFilters.isEmpty()) { 287 mControlFilters = new ArrayList<IntentFilter>(descriptor.mControlFilters); 288 } 289 } 290 291 /** 292 * Sets the unique id of the route. 293 * <p> 294 * The route id associated with a route descriptor functions as a stable 295 * identifier for the route and must be unique among all routes offered 296 * by the provider. 297 * </p> 298 */ 299 public Builder setId(String id) { 300 mBundle.putString(KEY_ID, id); 301 return this; 302 } 303 304 /** 305 * Sets the user-visible name of the route. 306 * <p> 307 * The route name identifies the destination represented by the route. 308 * It may be a user-supplied name, an alias, or device serial number. 309 * </p> 310 */ 311 public Builder setName(String name) { 312 mBundle.putString(KEY_NAME, name); 313 return this; 314 } 315 316 /** 317 * Sets the user-visible description of the route. 318 * <p> 319 * The route description describes the kind of destination represented by the route. 320 * It may be a user-supplied string, a model number or brand of device. 321 * </p> 322 */ 323 public Builder setDescription(String description) { 324 mBundle.putString(KEY_DESCRIPTION, description); 325 return this; 326 } 327 328 /** 329 * Sets whether the route is enabled. 330 * <p> 331 * Disabled routes represent routes that a route provider knows about, such as paired 332 * Wifi Display receivers, but that are not currently available for use. 333 * </p> 334 */ 335 public Builder setEnabled(boolean enabled) { 336 mBundle.putBoolean(KEY_ENABLED, enabled); 337 return this; 338 } 339 340 /** 341 * Sets whether the route is in the process of connecting and is not yet 342 * ready for use. 343 */ 344 public Builder setConnecting(boolean connecting) { 345 mBundle.putBoolean(KEY_CONNECTING, connecting); 346 return this; 347 } 348 349 /** 350 * Sets whether the route can be disconnected without stopping playback. 351 */ 352 public Builder setCanDisconnect(boolean canDisconnect) { 353 mBundle.putBoolean(KEY_CAN_DISCONNECT, canDisconnect); 354 return this; 355 } 356 357 /** 358 * Sets an intent sender for launching the settings activity for this 359 * route. 360 */ 361 public Builder setSettingsActivity(IntentSender is) { 362 mBundle.putParcelable(KEY_SETTINGS_INTENT, is); 363 return this; 364 } 365 366 /** 367 * Adds a {@link MediaControlIntent media control intent} filter for the route. 368 */ 369 public Builder addControlFilter(IntentFilter filter) { 370 if (filter == null) { 371 throw new IllegalArgumentException("filter must not be null"); 372 } 373 374 if (mControlFilters == null) { 375 mControlFilters = new ArrayList<IntentFilter>(); 376 } 377 if (!mControlFilters.contains(filter)) { 378 mControlFilters.add(filter); 379 } 380 return this; 381 } 382 383 /** 384 * Adds a list of {@link MediaControlIntent media control intent} filters for the route. 385 */ 386 public Builder addControlFilters(Collection<IntentFilter> filters) { 387 if (filters == null) { 388 throw new IllegalArgumentException("filters must not be null"); 389 } 390 391 if (!filters.isEmpty()) { 392 for (IntentFilter filter : filters) { 393 addControlFilter(filter); 394 } 395 } 396 return this; 397 } 398 399 /** 400 * Sets the route's playback type. 401 */ 402 public Builder setPlaybackType(int playbackType) { 403 mBundle.putInt(KEY_PLAYBACK_TYPE, playbackType); 404 return this; 405 } 406 407 /** 408 * Sets the route's playback stream. 409 */ 410 public Builder setPlaybackStream(int playbackStream) { 411 mBundle.putInt(KEY_PLAYBACK_STREAM, playbackStream); 412 return this; 413 } 414 415 /** 416 * Sets the route's current volume, or 0 if unknown. 417 */ 418 public Builder setVolume(int volume) { 419 mBundle.putInt(KEY_VOLUME, volume); 420 return this; 421 } 422 423 /** 424 * Sets the route's maximum volume, or 0 if unknown. 425 */ 426 public Builder setVolumeMax(int volumeMax) { 427 mBundle.putInt(KEY_VOLUME_MAX, volumeMax); 428 return this; 429 } 430 431 /** 432 * Sets the route's volume handling. 433 */ 434 public Builder setVolumeHandling(int volumeHandling) { 435 mBundle.putInt(KEY_VOLUME_HANDLING, volumeHandling); 436 return this; 437 } 438 439 /** 440 * Sets the route's presentation display id, or -1 if none. 441 */ 442 public Builder setPresentationDisplayId(int presentationDisplayId) { 443 mBundle.putInt(KEY_PRESENTATION_DISPLAY_ID, presentationDisplayId); 444 return this; 445 } 446 447 /** 448 * Sets a bundle of extras for this route descriptor. 449 * The extras will be ignored by the media router but they may be used 450 * by applications. 451 */ 452 public Builder setExtras(Bundle extras) { 453 mBundle.putBundle(KEY_EXTRAS, extras); 454 return this; 455 } 456 457 /** 458 * Builds the {@link MediaRouteDescriptor media route descriptor}. 459 */ 460 public MediaRouteDescriptor build() { 461 if (mControlFilters != null) { 462 mBundle.putParcelableArrayList(KEY_CONTROL_FILTERS, mControlFilters); 463 } 464 return new MediaRouteDescriptor(mBundle, mControlFilters); 465 } 466 } 467}