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