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>  &mdash; 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> &mdash; 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&lt;?xml version="1.0" encoding="utf-8"?&gt;
159&lt;menu xmlns:android="http://schemas.android.com/apk/res/android"
160      xmlns:app="http://schemas.android.com/apk/res-auto"
161      &gt;
162
163    &lt;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    /&gt;
168&lt;/menu&gt;
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&mdash;Remote Playback, Secondary
205  Output or others&mdash;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} &mdash; 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} &mdash; 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} &mdash; 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    &#64;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&#64;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    &mdash; Called when the user connects to a media router output device.</li>
319  <li>{@link android.support.v7.media.MediaRouter.Callback#onRouteUnselected
320    onRouteUnselected()} &mdash; Called when the user disconnects from a media router output device.</li>
321  <li>{@link android.support.v7.media.MediaRouter.Callback#onRoutePresentationDisplayChanged
322    onRoutePresentationDisplayChanged()} &mdash; 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    &#64;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    &#64;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    &#64;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    &#64;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    &#64;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    &#64;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            &#64;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            &#64;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()} &mdash; Play a specific
523    media file, specified by a {@link android.net.Uri}.</li>
524  <li>{@link android.support.v7.media.RemotePlaybackClient#pause pause()} &mdash; Pause the
525    currently playing media track.</li>
526  <li>{@link android.support.v7.media.RemotePlaybackClient#resume resume()} &mdash; Continue
527    playing the current track after a pause command.</li>
528  <li>{@link android.support.v7.media.RemotePlaybackClient#seek seek()} &mdash; Move to a specific
529    position in the current track.</li>
530  <li>{@link android.support.v7.media.RemotePlaybackClient#release release()} &mdash; 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    &#64;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                    &#64;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