1page.title=Media Router 2page.tags="mediarouter","cast","chromecast","wireless display","miracast" 3@jd:body 4 5<div id="qv-wrapper"> 6 <div id="qv"> 7 <h2>In this document</h2> 8 <ol> 9 <li><a href="#overview">Overview</a> 10 <ol> 11 <li><a href="#mr-packages">Media router packages</a></li> 12 </ol> 13 </li> 14 <li><a href="#cast-ui">Cast User Interface</a> 15 <ol> 16 <li><a href="#cast-button">Cast button</a></li> 17 <li><a href="#selector">Media route selector</a></li> 18 </ol> 19 </li> 20 <li><a href="#media-routes">Connecting to Media Routes</a> 21 <ol> 22 <li><a href="#create-mr-callback">Creating a MediaRouter callback</a></li> 23 <li><a href="#attach-mr-callback">Attaching a callback to MediaRouter</a></li> 24 </ol> 25 <li><a href="#remote-playback">Remote Playback</a></li> 26 <li><a href="#secondary-output">Secondary Output</a> 27 <ol> 28 <li><a href="#pres-obj">Creating a Presentation object</a></li> 29 <li><a href="#pres-cntrlr">Creating a Presentation controller</a></li> 30 </ol> 31 </li> 32 </ol> 33 <h2>Key Classes</h2> 34 <ol> 35 <li>{@link android.support.v7.media.MediaRouter}</li> 36 <li>{@link android.support.v7.media.MediaRouter.Callback}</li> 37 <li>{@link android.support.v7.media.MediaRouteProvider}</li> 38 </ol> 39 <h2>Related Samples</h2> 40 <ol> 41 <li><a href="{@docRoot}guide/topics/media/mediarouter.html">MediaRouter</a></li> 42 </ol> 43 </div> 44</div> 45 46<p>As users connect their televisions, home theater systems and music players with wireless 47 technologies, they want to be able to play content from Android apps on these larger, 48 louder devices. Enabling this kind of playback can turn your one-device, one-user app 49 into a shared experience that delights and inspires multiple users.</p> 50 51<p>The Android media router APIs are designed to enable media display and playback on these 52 secondary devices. There are two main approaches you can use to play content using these 53 APIs:</p> 54 55<ul> 56 <li><strong>Remote Playback</strong> — This approach uses the receiving device to handle 57 the content data retrieval, decoding, and playback, while an Android device in the user's hand 58 is used as a remote control. This approach is used by Android apps that support 59 <a href="https://developers.google.com/cast/">Google Cast</a>.</li> 60 <li><strong>Secondary Output</strong> — With this approach, your app retrieves, renders 61 and streams video or music directly to the receiving device. This approach is used to support 62 Wireless Display output 63 on Android.</li> 64</ul> 65 66<p>This guide explains how your app can deliver media to secondary playback devices using either 67 of these approaches.</p> 68 69 70<h2 id="overview">Overview</h2> 71 72<p>The media router APIs enable a broad range of media output to playback equipment connected to 73 Android devices through wireless and wired means. To enable these connections, 74 the media router framework abstracts the logical paths for audio and video output for an Android 75 device. This architecture allows your app to quickly channel media content to 76 connected playback devices such as home theaters and sound systems that provide Android media 77 route support.</p> 78 79<p>In order to use this framework within your app, you must get an instance 80 of the {@link android.support.v7.media.MediaRouter} framework object and attach a {@link 81 android.support.v7.media.MediaRouter.Callback} object to listen for events in 82 available media routes. Content channelled through a media route passes through the route's 83 associated {@link android.support.v7.media.MediaRouteProvider} (except in a few special cases, 84 such as a Bluetooth output device). The following diagram provides a high-level view of the 85 classes your app can use to play content with the media router framework. 86</p> 87 88<img src="{@docRoot}images/mediarouter/mediarouter-framework.png" alt="" id="figure1"/> 89<p class="img-caption"> 90 <strong>Figure 1.</strong> Overview of key media router classes used by apps. 91</p> 92 93<p>Manufacturers of media playback hardware that is not supported by the media router framework 94 can add support for their devices by implementing a 95 {@link android.support.v7.media.MediaRouteProvider} and distributing it as an application. 96 For more information on implementing a media route provider, see the {@link 97 android.support.v7.media.MediaRouteProvider} reference documentation and the v7-mediarouter 98 support library sample {@code <sdk>/extras/android/compatibility/v7/mediarouter}. 99</p> 100 101 102<h3 id="mr-packages">Media router packages</h3> 103 104<p>The media router APIs are provided as part of the Android Support Library version 18 and 105 higher, in the 106 <a href="{@docRoot}tools/support-library/features.html#v7-mediarouter">v7-mediarouter support 107 library</a>. Specifically, you should use the classes in the {@link android.support.v7.media} 108 package for media router functions. These APIs are compatible with devices running Android 2.1 109 (API level 7) and higher. 110</p> 111 112<p class="caution"> 113 <strong>Caution:</strong> There is another set of media router APIs provided in the 114 {@link android.media} that have been superseded by the v7-mediarouter support library. 115 You <em>should not</em> use the {@link android.media} classes for media router functions. 116</p> 117 118<p>In order to use the {@link android.support.v7.media} media router classes, you must add 119 the <a href="{@docRoot}tools/support-library/features.html#v7-mediarouter">v7-mediarouter 120 support library package</a> to your app development project. For more 121 information on adding support libraries to your app development project, see 122 <a href="{@docRoot}tools/support-library/setup.html">Support Library Setup</a>. 123</p> 124 125 126<h2 id="cast-ui">Cast User Interface</h2> 127 128<p> 129 Android apps that implement the media router API should include a Cast button 130 as part of their user interface, to allow users to select a media route to play media on 131 a secondary output device. The media router framework provides a standard interface for 132 the button, which you should use to help users recognize and use the feature in your app. 133 Figure 2 illustrates how the Cast button should appear in an app. 134</p> 135 136<img src="{@docRoot}images/mediarouter/mediarouter-actionbar.png" alt="" width="428" id="figure2"/> 137<p class="img-caption"> 138 <strong>Figure 2.</strong> A Cast button shown on the right side of the action bar. 139</p> 140 141<p class="caution"> 142 <strong>Caution:</strong> When implementing an activity that provides a media router interface 143 you <em>must</em> extend either {@link android.support.v7.app.ActionBarActivity} 144 or {@link android.support.v4.app.FragmentActivity} from the Android Support Library, even if 145 your {@code android:minSdkVersion} is API 11 or higher. 146</p> 147 148 149<h3 id="cast-button">Cast button</h3> 150 151<p>The recommended way to implement the Cast button user interface is to extend your activity 152 from {@link android.support.v7.app.ActionBarActivity} and use the {@link 153 android.app.Activity#onCreateOptionsMenu onCreateOptionsMenu()} method to add an options menu. 154 The Cast button must use the {@link android.support.v7.app.MediaRouteActionProvider} class 155 as its action:</p> 156 157<pre> 158<?xml version="1.0" encoding="utf-8"?> 159<menu xmlns:android="http://schemas.android.com/apk/res/android" 160 xmlns:app="http://schemas.android.com/apk/res-auto" 161 > 162 163 <item android:id="@+id/media_route_menu_item" 164 android:title="@string/media_route_menu_title" 165 <strong>app:actionProviderClass="android.support.v7.app.MediaRouteActionProvider"</strong> 166 app:showAsAction="always" 167 /> 168</menu> 169</pre> 170 171<p>For more information about implementing the action bar in your app, 172 see the <a href="{@docRoot}guide/topics/ui/actionbar.html">Action Bar</a> 173 developer guide. 174</p> 175 176<p>Once you have added the Cast button to your user interface, you must attach a media 177 route selector object. Building a selector is discussed in the next section. 178</p> 179 180<p>If you do not want a menu in your action bar, you can also add a Cast button to your app using 181 {@link android.support.v7.app.MediaRouteButton}. If you choose this approach, you should add 182 this button to your app's action bar according to the 183 <a href="https://developers.google.com/cast/docs/design_checklist">Google Cast Design 184 Checklist</a>. You must also attach a media route selector to the button using the 185 {@link android.support.v7.app.MediaRouteButton#setRouteSelector setRouteSelector()} method. 186</p> 187 188<p>For guidelines on incorporating the Cast button into your application, review the 189 <a href="https://developers.google.com/cast/docs/design_checklist">Google Cast Design 190 Checklist</a>.</p> 191 192 193<h3 id="selector">Media route selector</h3> 194 195<p>When a user presses the Cast button, the media router framework looks for available media 196 routes and presents a list of choices to the user, as shown in figure 3.</p> 197 198<img src="{@docRoot}images/mediarouter/mediarouter-selector-ui.png" alt="" width="500" id="figure3"/> 199<p class="img-caption"> 200 <strong>Figure 3.</strong> A list of available media routes, shown after pressing the Cast button. 201</p> 202 203 204<p>The <em>types</em> of media routes that appear on this list—Remote Playback, Secondary 205 Output or others—are defined by your app.You define these type by creating a {@link 206 android.support.v7.media.MediaRouteSelector}, which accepts {@link 207 android.support.v7.media.MediaControlIntent} objects provided by the framework and other media 208 route providers created by you or other developers. The framework-provided route categories are as 209 follows: 210</p> 211 212<ul> 213 <li>{@link android.support.v7.media.MediaControlIntent#CATEGORY_LIVE_AUDIO 214 CATEGORY_LIVE_AUDIO} — Output of audio to a secondary output device, such as a 215 wireless-enabled music system.</li> 216 <li>{@link android.support.v7.media.MediaControlIntent#CATEGORY_LIVE_VIDEO 217 CATEGORY_LIVE_VIDEO} — Output of video to a secondary output device, such as Wireless 218 Display devices.</li> 219 <li>{@link android.support.v7.media.MediaControlIntent#CATEGORY_REMOTE_PLAYBACK 220 CATEGORY_REMOTE_PLAYBACK} — Play video or audio on a separate device that handles media 221 retrieval, decoding, and playback, such as 222 <a href="https://www.google.com/url?q=http://www.google.com/chromecast">Chromecast</a> devices. 223 </li> 224</ul> 225 226<p>When creating a {@link android.support.v7.media.MediaRouteSelector} object, use the 227 {@link android.support.v7.media.MediaRouteSelector.Builder} class to create the object and set 228 the media playback categories (control categories), as shown 229 in the following code sample:</p> 230 231<pre> 232public class MediaRouterPlaybackActivity extends ActionBarActivity { 233 private MediaRouteSelector mSelector; 234 235 @Override 236 protected void onCreate(Bundle savedInstanceState) { 237 super.onCreate(savedInstanceState); 238 setContentView(R.layout.activity_main); 239 240 // Create a route selector for the type of routes your app supports. 241 <strong>mSelector = new MediaRouteSelector.Builder() 242 // These are the framework-supported intents 243 .addControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO) 244 .addControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO) 245 .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)</strong> 246 .build(); 247 } 248} 249</pre> 250 251<p>The media router framework uses this selector object to provide an interface for selecting 252 media routes that your app supports, as shown in figure 3. Once you have defined this selector, 253 you attach it to the {@link android.support.v7.app.MediaRouteActionProvider} object associated 254 with the Cast menu item, as shown in the following code sample:</p> 255 256<pre> 257@Override 258public boolean onCreateOptionsMenu(Menu menu) { 259 super.onCreateOptionsMenu(menu); 260 261 // Inflate the menu and configure the media router action provider. 262 getMenuInflater().inflate(R.menu.sample_media_router_menu, menu); 263 264 // Attach the MediaRouteSelector to the menu item 265 MenuItem mediaRouteMenuItem = menu.findItem(R.id.media_route_menu_item); 266 MediaRouteActionProvider mediaRouteActionProvider = 267 (MediaRouteActionProvider)MenuItemCompat.getActionProvider( 268 mediaRouteMenuItem); 269 <strong>mediaRouteActionProvider.setRouteSelector(mSelector);</strong> 270 271 // Return true to show the menu. 272 return true; 273} 274</pre> 275 276<p>Once you have made these changes to your app, you might expect the Cast button to appear in your 277 activity. Alas, it does not (unless your device is already paired with a Wireless Display). In 278 most cases, you must also connect with the media route framework, which is discussed in the next 279 section. 280</p> 281 282 283<h2 id="media-routes">Connecting to Media Routes</h2> 284 285<p>In order to connect to a media route selected by the user, your app must obtain the {@link 286 android.support.v7.media.MediaRouter} framework object and then attach a {@link 287 android.support.v7.media.MediaRouter.Callback} object. The callback object receives messages 288 from the media router framework when a route is selected, changed, or disconnected by the user.</p> 289 290<p>To obtain an instance of the {@link android.support.v7.media.MediaRouter} framework object, 291 call {@link android.support.v7.media.MediaRouter#getInstance MediaRouter.getInstance()} 292 from the {@link android.app.Activity#onCreate onCreate()} method of an activity that supports 293 the media router API.</p> 294 295<p class="note"> 296 <strong>Note:</strong> The {@link android.support.v7.media.MediaRouter} object is a singleton 297 that is maintained by the framework. However, once your application obtains an instance of the 298 object you must retain that instance until your application terminates to prevent it from being 299 garbage collected. 300</p> 301 302 303<h3 id="create-mr-callback">Creating a MediaRouter callback</h3> 304 305<p>The media router framework communicates with an app through a callback object that 306 you attach to the {@link android.support.v7.media.MediaRouter} framework object. An app 307 that uses the media router framework must extend the {@link 308 android.support.v7.media.MediaRouter.Callback} object in order to receive messages when a 309 media route is connected.</p> 310 311<p>There are several methods in the callback that you can override to receive information about 312 media router events. At minimum, your implementation of the {@link 313 android.support.v7.media.MediaRouter.Callback} class should override the following 314 methods:</p> 315 316<ul> 317 <li>{@link android.support.v7.media.MediaRouter.Callback#onRouteSelected onRouteSelected()} 318 — Called when the user connects to a media router output device.</li> 319 <li>{@link android.support.v7.media.MediaRouter.Callback#onRouteUnselected 320 onRouteUnselected()} — Called when the user disconnects from a media router output device.</li> 321 <li>{@link android.support.v7.media.MediaRouter.Callback#onRoutePresentationDisplayChanged 322 onRoutePresentationDisplayChanged()} — Called when the presentation display changes its 323 display metrics, such as changing from 720 pixel to 1080 pixel resolution.</li> 324</ul> 325 326<p>The methods of your {@link android.support.v7.media.MediaRouter.Callback} 327 implementation are the first opportunity to determine if the connected route is a remote playback 328 device, such as Chromecast, or a secondary output device, such as a Wireless Display device. 329 If your app supports both device types, then your implementation should branch here, as 330 shown in this sample code:</p> 331 332<pre> 333private final MediaRouter.Callback mMediaRouterCallback = 334 new MediaRouter.Callback() { 335 336 @Override 337 public void onRouteSelected(MediaRouter router, RouteInfo route) { 338 Log.d(TAG, "onRouteSelected: route=" + route); 339 340 if (route.supportsControlCategory( 341 MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)){ 342 // remote playback device 343 updateRemotePlayer(route); 344 } else { 345 // secondary output device 346 updatePresentation(route); 347 } 348 } 349 350 @Override 351 public void onRouteUnselected(MediaRouter router, RouteInfo route) { 352 Log.d(TAG, "onRouteUnselected: route=" + route); 353 354 if (route.supportsControlCategory( 355 MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)){ 356 // remote playback device 357 updateRemotePlayer(route); 358 } else { 359 // secondary output device 360 updatePresentation(route); 361 } 362 } 363 364 @Override 365 public void onRoutePresentationDisplayChanged( 366 MediaRouter router, RouteInfo route) { 367 Log.d(TAG, "onRoutePresentationDisplayChanged: route=" + route); 368 369 if (route.supportsControlCategory( 370 MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)){ 371 // remote playback device 372 updateRemotePlayer(route); 373 } else { 374 // secondary output device 375 updatePresentation(route); 376 } 377 } 378} 379</pre> 380 381<p>After defining your callback object for the media router, you still need to attach it to 382 the main media router framework object. The next section discusses the appropriate way to attach 383 your callbacks for media routes.</p> 384 385 386<h3 id="attach-mr-callback">Attaching a callback to MediaRouter</h3> 387 388<p>Since media routes are a shared interface, your app must attach and detach your 389 {@link android.support.v7.media.MediaRouter.Callback} object as your app starts up and shuts 390 down. To accomplish this, you must add and remove your app's 391 callback object from the media router framework as part of your app's activity lifecycle. This 392 approach allows other apps to make use of media route outputs while your app 393 is in the background or not running.</p> 394 395<p class="note"> 396 <strong>Note:</strong> If you are writing a music playback app and want to allow music to play 397 while your app is in the background, you must build a {@link android.app.Service} for playback 398 and connect that service and it's lifecycle to the media router framework. 399</p> 400 401<p>The following code sample demonstrates how to use the lifecycle methods to appropriately 402 add and remove your app's media router callback object:</p> 403 404<pre> 405public class MediaRouterPlaybackActivity extends ActionBarActivity { 406 private MediaRouter mMediaRouter; 407 private MediaRouteSelector mSelector; 408 private Callback mMediaRouterCallback; 409 410 // your app works with so the framework can discover them. 411 @Override 412 protected void onCreate(Bundle savedInstanceState) { 413 super.onCreate(savedInstanceState); 414 setContentView(R.layout.activity_main); 415 416 // Get the media router service. 417 mMediaRouter = MediaRouter.getInstance(this); 418 ... 419 } 420 421 // Add the callback on start to tell the media router what kinds of routes 422 // your app works with so the framework can discover them. 423 @Override 424 public void onStart() { 425 mMediaRouter.addCallback(mSelector, mMediaRouterCallback, 426 MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY); 427 super.onStart(); 428 } 429 430 // Remove the selector on stop to tell the media router that it no longer 431 // needs to discover routes for your app. 432 @Override 433 public void onStop() { 434 mMediaRouter.removeCallback(mMediaRouterCallback); 435 super.onStop(); 436 } 437 ... 438} 439</pre> 440 441<p>You should add and remove the media router callback only in the {@link 442 android.app.Activity#onStart onStart()} and {@link android.app.Activity#onStop onStop()} 443 lifecycle methods. Do not include these calls in the {@link android.app.Activity#onResume 444 onResume()} or {@link android.app.Activity#onPause onPause()} methods. 445</p> 446 447<p class="note"> 448 <strong>Note:</strong> The media route framework also provides a 449 {@link android.support.v7.app.MediaRouteDiscoveryFragment} class, which takes care of adding and 450 removing the callback for an activity. 451</p> 452 453<p>Now when you run your application, you should see a Cast button appear in your activity. 454 When you touch the button, a route selection dialog appears as shown 455 in figure 3, allowing your user to select an available media route. Make sure you have a 456 supported device available on your local network when testing this interface.</p> 457 458<p class="note"> 459 <strong>Note:</strong> In order for Wireless Display routes to show up in the media route 460 selection dialog, users must enable this option in the Settings app. The option is under 461 the <em>Display</em> category and is called <em>Cast screen</em> on Android 4.4 (KitKat) 462 and higher devices. For more information 463 on enabling this feature see this 464 <a href="https://support.google.com/nexus/answer/2865484">Wireless display</a> support page. 465</p> 466 467 468<h2 id="remote-playback">Remote Playback</h2> 469 470<p>The remote playback approach sends control commands to a secondary device to initiate playback 471 and to control playback that is in progress (pause, rewind, fast-forward, volume up and down). 472 Using this approach, the receiving device (such as a Chromecast) is responsible for retrieving 473 and rendering content.</p> 474 475<p>When your app supports this type of media route, you must create a {@link 476 android.support.v7.media.RemotePlaybackClient} object using a remote playback {@link 477 android.support.v7.media.MediaRouter.RouteInfo} object received through your app's 478 {@link android.support.v7.media.MediaRouter.Callback} object. The following sample 479 code demonstrates a controller method that creates a new remote playback client and sends it a 480 video for playback:</p> 481 482<pre> 483private void updateRemotePlayer(RouteInfo route) { 484 // Changed route: tear down previous client 485 if (mRoute != null && mRemotePlaybackClient != null) { 486 mRemotePlaybackClient.release(); 487 mRemotePlaybackClient = null; 488 } 489 490 // Save new route 491 mRoute = route; 492 493 // Attach new playback client 494 mRemotePlaybackClient = new RemotePlaybackClient(this, mRoute); 495 496 // Send file for playback 497 mRemotePlaybackClient.play(Uri.parse( 498 "http://archive.org/download/Sintel/sintel-2048-stereo_512kb.mp4"), 499 "video/mp4", null, 0, null, new ItemActionCallback() { 500 501 @Override 502 public void onResult(Bundle data, String sessionId, 503 MediaSessionStatus sessionStatus, 504 String itemId, MediaItemStatus itemStatus) { 505 logStatus("play: succeeded for item " + itemId); 506 } 507 508 @Override 509 public void onError(String error, int code, Bundle data) { 510 logStatus("play: failed - error:"+ code +" - "+ error); 511 } 512 }); 513 } 514} 515</pre> 516 517<p>The {@link android.support.v7.media.RemotePlaybackClient} class provides additional methods 518 for managing content playback. Here are a few of the key playback methods from the {@link 519 android.support.v7.media.RemotePlaybackClient} class:</p> 520 521<ul> 522 <li>{@link android.support.v7.media.RemotePlaybackClient#play play()} — Play a specific 523 media file, specified by a {@link android.net.Uri}.</li> 524 <li>{@link android.support.v7.media.RemotePlaybackClient#pause pause()} — Pause the 525 currently playing media track.</li> 526 <li>{@link android.support.v7.media.RemotePlaybackClient#resume resume()} — Continue 527 playing the current track after a pause command.</li> 528 <li>{@link android.support.v7.media.RemotePlaybackClient#seek seek()} — Move to a specific 529 position in the current track.</li> 530 <li>{@link android.support.v7.media.RemotePlaybackClient#release release()} — Tear down the 531 connection from your app to the remote playback device.</li> 532</ul> 533 534<p>You can use these methods to attach actions to playback controls you provide in your 535 app. Most of these methods also allow you to include a callback object so you can monitor 536 the progress of the playback task or control request.</p> 537 538<p> 539 The {@link android.support.v7.media.RemotePlaybackClient} class also supports queueing of 540 multiple media items for playback and management of the media queue. For a comprehensive sample 541 implementation of these features, see {@code SampleMediaRouterActivity} and its associated 542 classes in the v7 mediarouter support library sample 543 {@code <sdk>/extras/android/compatibility/v7/mediarouter}. 544</p> 545 546<p> 547 For additional information on using the Google Cast API for Chromecast devices, see the 548 <a href="http://developers.google.com/cast/">Google Cast</a> developer documentation. 549</p> 550 551 552<h2 id="secondary-output">Secondary Output</h2> 553 554<p>The secondary output approach sends prepared media content to a connected secondary device 555 for playback. Secondary devices can include televisions or wireless sound systems and can be 556 attached through wireless protocols or wires, such as an HDMI cable. With this approach, your 557 app is responsible for processing media content for playback (downloading, decoding, 558 synchronization of audio and video tracks), while the secondary device only outputs the content 559 in its final form.</p> 560 561<p class="note"> 562 <strong>Note:</strong> Using the secondary output display routes with the media router framework 563 requires classes that are available only in Android 4.2 (API level 17) and higher, specifically the 564 {@link android.app.Presentation} class. If you are building an app that supports both 565 remote playback and secondary output devices, you must include checks that disable this code 566 below the supported Android version level. 567</p> 568 569 570<h3 id="pres-obj">Creating a Presentation object</h3> 571 572<p>When using a secondary output display with the media router framework, you create a {@link 573 android.app.Presentation} object that contains the content you want to show on that display. The 574 {@link android.app.Presentation} is extended from the {@link android.app.Dialog} class, so can 575 add layouts and views to a {@link android.app.Presentation}.</p> 576 577<p>You should be aware that the {@link android.app.Presentation} object has its own 578 {@link android.content.Context} and 579 {@link android.content.res.Resources}, 580 separate from the app activity that created the object. Having a secondary 581 context is required, because the content of the {@link android.app.Presentation} is drawn on a 582 display that is separate from your app's display on the local Android device. 583 Specifically, the secondary display needs a separate context because it may need to load 584 resources based on its specific screen metrics.</p> 585 586<p>The following code sample shows a minimal implementation of a 587 {@link android.app.Presentation} object, including a {@link android.opengl.GLSurfaceView} 588 object.</p> 589 590<pre> 591public class SamplePresentation extends Presentation { 592 public SamplePresentation(Context outerContext, Display display) { 593 super(outerContext, display); 594 } 595 596 @Override 597 protected void onCreate(Bundle savedInstanceState) { 598 super.onCreate(savedInstanceState); 599 // Notice that we get resources from the context of the Presentation 600 Resources resources = getContext().getResources(); 601 602 // Inflate a layout. 603 setContentView(R.layout.presentation_with_media_router_content); 604 605 // Add presentation content here: 606 // Set up a surface view for visual interest 607 mSurfaceView = (GLSurfaceView)findViewById(R.id.surface_view); 608 mSurfaceView.setRenderer(new CubeRenderer(false)); 609 } 610} 611</pre> 612 613 614<h3 id="pres-cntrlr">Creating a Presentation controller</h3> 615 616<p>In order to display a {@link android.app.Presentation} object, you should write a 617 controller layer that handles responses to the messages received by the {@link 618 android.support.v7.media.MediaRouter.Callback} object and manages the creation and 619 removal of the {@link android.app.Presentation} object. The controller layer should also handle 620 attaching presentations to a selected {@link android.view.Display} object, which represents the 621 separate physical display device chosen by the user. The controller layer can simply be a method 622 in the activity that supports a secondary display.</p> 623 624<p>The following code sample shows a controller layer for a {@link android.app.Presentation} 625 implemented as a single method. This method handles dismissing invalid presentations when a 626 {@link android.view.Display} is unselected or disconnected, and creates the {@link 627 android.app.Presentation} object when a display device is connected.</p> 628 629<pre> 630private void updatePresentation(RouteInfo route) { 631 // Get its Display if a valid route has been selected 632 Display selectedDisplay = null; 633 if (route != null) { 634 selectedDisplay = route.getPresentationDisplay(); 635 } 636 637 // Dismiss the current presentation if the display has changed or no new 638 // route has been selected 639 if (mPresentation != null && mPresentation.getDisplay() != selectedDisplay) { 640 mPresentation.dismiss(); 641 mPresentation = null; 642 } 643 644 // Show a new presentation if the previous one has been dismissed and a 645 // route has been selected. 646 if (mPresentation == null && selectedDisplay != null) { 647 // Initialize a new Presentation for the Display 648 mPresentation = new SamplePresentation(this, selectedDisplay); 649 mPresentation.setOnDismissListener( 650 new DialogInterface.OnDismissListener() { 651 // Listen for presentation dismissal and then remove it 652 @Override 653 public void onDismiss(DialogInterface dialog) { 654 if (dialog == mPresentation) { 655 mPresentation = null; 656 } 657 } 658 }); 659 660 // Try to show the presentation, this might fail if the display has 661 // gone away in the meantime 662 try { 663 mPresentation.show(); 664 } catch (WindowManager.InvalidDisplayException ex) { 665 // Couldn't show presentation - display was already removed 666 mPresentation = null; 667 } 668 } 669} 670</pre> 671 672<p class="note"> 673 <strong>Note:</strong> When the a user connects to a Wireless Display, the media router 674 framework automatically provides a notification that it is displaying screen content on a 675 connected device. 676</p> 677