Home | History | Annotate | Download | only in media
      1 page.title=Media Router
      2 page.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>
    232 public 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
    258 public 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>
    333 private 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>
    405 public 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>
    483 private 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>
    591 public 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>
    630 private 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