This codelab will teach you how to modify an existing Android video app to play content on a Google Cast device.

What is Google Cast?

Google Cast allows users to cast content from a mobile device to a TV. Users can then use their mobile device as a remote control for media playback on the TV.

The Google Cast SDK lets you extend your app to control a TV or sound system. The Cast SDK allows you to add the necessary UI components based on the Google Cast Design Checklist.

The Google Cast Design Checklist is provided to make the Cast user experience simple and predictable across all supported platforms.

What are we going to be building?

When you have completed this codelab, you will have an Android video app that will be able to cast videos to a Google Cast device.

What you'll learn

What you'll need

Experience

How will you use this tutorial?

Read it through only Read it and complete the exercises

How would you rate your experience with building Android apps?

Novice Intermediate Proficient

How would you rate your experience with watching TV?

Novice Intermediate Proficient

You can either download all the sample code to your computer...

Download Source Code

and unpack the downloaded zip file.

...or clone the GitHub repository from the command line.

$ git clone https://github.com/googlecodelabs/cast-videos-android.git

First, let's see what the completed sample app looks like. The app is a basic video player. The user can select a video from a list and can then play the video locally on the device or Cast it to a Google Cast device.

With the code downloaded, the following instructions describe how to open and run the completed sample app in Android Studio:

Select the Import Project on the welcome screen or the File > New > Import Project... menu options.

Select the app-done directory from the sample code folder and click OK.

Click the Gradle sync button.

Enable USB debugging on your Android device -- on Android 4.2 and higher, the Developer options screen is hidden by default. To make it visible, go to Settings > About phone and tap Build number seven times. Return to the previous screen, tap on Developer options near the bottom, then tap on USB debugging to turn it on.

Choose Tools > Android > SDK Manager in Android Studio to make sure you have the necessary libraries installed for a Cast enabled app:

Plug in your Android device and click the Run button in Android Studio. You should see the video app named Cast Videos appear after a few seconds.

Click the Cast button in the video app and select your Google Cast device.

Select a video and click on the play button.

The video will start playing on your Google Cast device.

The expanded controller will be displayed. You can use the play/pause button to control the playback.

Navigate back to the list of videos.

A mini controller is now visible at the bottom of the screen.

Click on the pause button in the mini controller to pause the video on the receiver. Click on the play button in the mini controller to continue playing the video again.

Click on the mobile device home button. Pull down notifications and you should now see a notification for the Cast session.

Lock your phone and when you unlock it, you should see a notification on the lock screen to control the media playback or stop casting.

Return to the video app and click on the Cast button to stop casting on the Google Cast device.

Frequently Asked Questions

We need to add support for Google Cast to the start app you downloaded. Here is some Google Cast terminology that we will be using in this codelab:

Now you're ready to build on top of the starter project using Android Studio:

  1. Select the app-start directory from your sample code download (Import Project on the welcome screen or the File > New > Import Project... menu option).
  2. Click the Gradle sync button.
  3. Click the Run button to run the app and explore the UI.

App Design

The app fetches a list of videos from a remote web server and provides a list for the user to browse. Users can select a video to see the details or play the video locally on the mobile device.

The app consists of two main activities: VideoBrowserActivity and LocalPlayerActivity. In order to integrate Google Cast functionality, the Activities need to inherit from either the ActionBarActivity or its parent the FragmentActivity. This limitation exists since we would need to add the MediaRouteButton (provided in the MediaRouter v7 support library) as an ActionProvider and this will only work if the activity is inheriting from the above-mentioned classes. The MediaRouter support library depends on the AppCompat support library which provides the required classes.

VideoBrowserActivity

This activity contains a ListFragment (VideoBrowserFragment). This list is backed by an ArrayAdapter (VideoListAdapter). The list of videos and their associated metadata are hosted on a remote server as a JSON file. An AsyncTaskLoader (VideoItemLoader) fetches this JSON and processes it to build a list of MediaItem objects.

A MediaItem object models a video and its associated metadata, such as its title, description, URL for the stream, URL for the supporting images, and associated Text Tracks (for closed captions) if any. The MediaItem object is passed between activities, so MediaItem has utility methods to convert it to a Bundle and vice versa.

When the loader builds the list of MediaItems, it passes that list to the VideoListAdapter which then presents the MediaItems list in the VideoBrowserFragment. The user is presented with a list of video thumbnails with a short description for each video. When an item is selected, the corresponding MediaItem is converted into a Bundle and is passed to the LocalPlayerActivity.

LocalPlayerActivity

This activity displays the metadata about a particular video and allows the user to play the video locally on the mobile device.

The activity hosts a VideoView, some media controls, and a text area to show the description of the selected video. The player covers the top portion of screen, leaving room for the detailed description of the video beneath. The user can play/pause or seek the local playback of videos.

Dependencies

Since we are using ActionBarActivity, we need the AppCompat support library. For managing the list of videos and asynchronously getting the images for the list, we are using the android-query library.

Frequently Asked Questions

A Cast-enabled application displays the Cast button in each of its activities. Clicking on the Cast button displays a list of Cast devices which a user can select. If the user was playing content locally on the sender device, selecting a Cast device starts or resumes playback on that Cast device. At any time during a Cast session, the user can click on the Cast button and stop casting your application to the Cast device. The user must be able to connect to or disconnect from the Cast device while in any activity of your application, as described in the Google Cast Design Checklist.

Dependencies

Update the app build.gradle file to include the necessary library dependencies:

dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    compile 'com.android.support:appcompat-v7:25.0.0'
    compile 'com.android.support:mediarouter-v7:25.0.0'
    compile 'com.android.support:recyclerview-v7:25.0.0'
    compile 'com.google.android.gms:play-services-cast-framework:9.8.0'
    compile 'com.googlecode.android-query:android-query:0.25.9'
}

Sync the project to confirm the project builds without errors.

Initialization

The Cast framework has a global singleton object, the CastContext, which coordinates all the Cast interactions.

You must implement the OptionsProvider interface to supply CastOptions needed to initialize the CastContext singleton. The most important option is the receiver application ID, which is used to filter Cast device discovery results and to launch the receiver application when a Cast session is started.

When you develop your own Cast-enabled app, you have to register as a Cast developer and then obtain an application ID for your app. For this codelab, we will be using a sample app ID.

Add the following new CastOptionsProvider class to the com.google.sample.cast.refplayer package of the project:

package com.google.sample.cast.refplayer;

import com.google.android.gms.cast.framework.CastOptions;
import com.google.android.gms.cast.framework.OptionsProvider;
import com.google.android.gms.cast.framework.SessionProvider;

import android.content.Context;

import java.util.List;


public class CastOptionsProvider implements OptionsProvider {

    @Override
    public CastOptions getCastOptions(Context context) {
        return new CastOptions.Builder()
                .setReceiverApplicationId(context.getString(R.string.app_id))
                .build();
    }

    @Override
    public List<SessionProvider> getAdditionalSessionProviders(Context context) {
        return null;
    }
}

Now declare the OptionsProvider within the "application" tag of the app AndroidManifest.xml file:

<meta-data
android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
android:value="com.google.sample.cast.refplayer.CastOptionsProvider" />

Lazily initialize the CastContext in the VideoBrowserActivity onCreate method:

private CastContext mCastContext;

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.video_browser);
    setupActionBar();

    mCastContext = CastContext.getSharedInstance(this);
}

Add the same initialization logic to the LocalPlayerActivity.

Cast Button

Now that the CastContext is initialized, we need to add the Cast button to allow the user to select a Cast device. The Cast button is implemented by the MediaRouteButton from the MediaRouter v7 support library. Like any action icon that you can add to your activity (using either an ActionBar or a Toolbar), you first need to add the corresponding menu item to your menu.

Edit the res/menu/browse.xml file and add the MediaRouteActionProvider item in the menu before the settings item:

<item
    android:id="@+id/media_route_menu_item"
    android:title="@string/media_route_menu_title"
    app:actionProviderClass="android.support.v7.app.MediaRouteActionProvider"
    app:showAsAction="always"/>

Override the onCreateOptionMenu() method of VideoBrowserActivity by using CastButtonFactory to wire up the MediaRouteButton to the Cast framework:

private MenuItem mediaRouteMenuItem;

public boolean onCreateOptionsMenu(Menu menu) {
    super.onCreateOptionsMenu(menu);
    getMenuInflater().inflate(R.menu.browse, menu);
    mediaRouteMenuItem = CastButtonFactory.setUpMediaRouteButton(getApplicationContext(), menu, R.id.media_route_menu_item);
    return true;
}

Override onCreateOptionMenu in LocalPlayerActivity in a similar way.

Now run the app on your mobile device. You should see a Cast button in the app's actionbar and when you click on it, it will list the Cast devices on your local network. Device discovery is managed automatically by the CastContext. Select your Cast device and the sample receiver app will load on the Cast device. You can navigate between the browse activity and the local player activity and the Cast button state is kept in sync.

We haven't hooked up any support for media playback, so you can't play videos on the Cast device yet. Click on the Cast button to disconnect.

We will extend the sample app to also play videos remotely on a Cast device. To do that we need to listen to the various events generated by the Cast framework.

Casting Media

At a high level, if you want to play a media on a Cast device, you need to do these things:

  1. Create a MediaInfo object that models a media item.
  2. Connect to the Cast device and launch your receiver application.
  3. Load the MediaInfo object into your receiver and play the content.
  4. Track the media status.
  5. Send playback commands to the receiver based on user interactions.

We have already done the Step 2 in the previous section. Step 3 is easy to do with the Cast framework. Step 1 amounts to mapping one object to another; MediaInfo is something that the Cast framework understands and MediaItem is our app's encapsulation for a media item; we can easily map a MediaItem to a MediaInfo.

The sample app LocalPlayerActivity already distinguishes between local vs remote playback by using this enum:

private PlaybackLocation mLocation;

public enum PlaybackLocation {
    LOCAL,
    REMOTE
}

It's not important in this codelab for you to understand exactly how all the sample player logic works. It is important to understand that your app's media player will have to be modified to be aware of the two playback locations in a similar way.

At the moment the local player is always in the local playback state since it doesn't know anything about the Casting states yet. We need to update the UI based on state transitions that happen in the Cast framework. For example, if we start casting, we need to stop the local playback and disable some controls. Similarly, if we stop casting when we are in this activity, we need to transition to local playback. To handle that we need to listen to the various events generated by the Cast framework.

Cast Session Management

For the Cast framework a Cast session combines the steps of connecting to a device, launching (or joining), connecting to a receiver application, and initializing a media control channel if appropriate. The media control channel is how the Cast framework sends and receives messages from the receiver media player.

The Cast session will be started automatically when user select a device from the Cast button, and will be stopped automatically when user disconnects. Reconnecting to a receiver session due to networking issues is also automatically handled by the Cast SDK.

Cast sessions are managed by the SessionManager, which can be accessed via CastContext.getSessionManager(). The SessionManagerListener callbacks can be used to monitor session events, such as creation, suspension, resumption, and termination.

Let's add a SessionManagerListener to the LocalPlayerActivity:

private CastSession mCastSession;
private SessionManagerListener<CastSession> mSessionManagerListener;

private void setupCastListener() {
    mSessionManagerListener = new SessionManagerListener<CastSession>() {

        @Override
        public void onSessionEnded(CastSession session, int error) {
            onApplicationDisconnected();
        }

        @Override
        public void onSessionResumed(CastSession session, boolean wasSuspended) {
            onApplicationConnected(session);
        }

        @Override
        public void onSessionResumeFailed(CastSession session, int error) {
            onApplicationDisconnected();
        }

        @Override
        public void onSessionStarted(CastSession session, String sessionId) {
            onApplicationConnected(session);
        }

        @Override
        public void onSessionStartFailed(CastSession session, int error) {
            onApplicationDisconnected();
        }

        @Override
        public void onSessionStarting(CastSession session) {}

        @Override
        public void onSessionEnding(CastSession session) {}

        @Override
        public void onSessionResuming(CastSession session, String sessionId) {}

        @Override
        public void onSessionSuspended(CastSession session, int reason) {}

        private void onApplicationConnected(CastSession castSession) {
            mCastSession = castSession;
            if (null != mSelectedMedia) {

                if (mPlaybackState == PlaybackState.PLAYING) {
                    mVideoView.pause();
                    loadRemoteMedia(mSeekbar.getProgress(), true);
                    finish();
                    return;
                } else {
                    mPlaybackState = PlaybackState.IDLE;
                    updatePlaybackLocation(PlaybackLocation.REMOTE);
                }
            }
            updatePlayButton(mPlaybackState);
            supportInvalidateOptionsMenu();
        }

        private void onApplicationDisconnected() {
            updatePlaybackLocation(PlaybackLocation.LOCAL);
            mPlaybackState = PlaybackState.IDLE;
            mLocation = PlaybackLocation.LOCAL;
            updatePlayButton(mPlaybackState);
            supportInvalidateOptionsMenu();
        }
    };
}

In LocalPlayerActivity activity, we are interested to be informed when we get connected or disconnected from the Cast device so we can switch to or from the local player. Note that connectivity can be disrupted not only by the instance of your application running on your mobile device, but it can also be disrupted by another instance of your (or another) application running on a different mobile device.

The currently active session is accessible as SessionManager.getCurrentSession(). Sessions are created and torn down automatically in response to user interactions with the Cast dialogs.

We need to register our session listener and initialize some variables that we will use in the activity. Change the LocalPlayerActivity onCreate method to:

...
setupControlsCallbacks();
setupCastListener();
mCastContext = CastContext.getSharedInstance(this);
mCastContext.registerLifecycleCallbacksBeforeIceCreamSandwich(this, savedInstanceState);
mCastSession = mCastContext.getSessionManager().getCurrentCastSession();
Bundle bundle = getIntent().getExtras();
...
} else {
    if (mCastSession != null && mCastSession.isConnected()) {
        updatePlaybackLocation(PlaybackLocation.REMOTE);
    } else {
        updatePlaybackLocation(PlaybackLocation.LOCAL);
    }
    mPlaybackState = PlaybackState.IDLE;
    updatePlayButton(mPlaybackState);
}
...

Loading Media

In the Cast SDK, the RemoteMediaClient provides a set of convenient APIs for managing the remote media playback on the receiver. For a CastSession that supports media playback, an instance of RemoteMediaClient will be created automatically by the SDK. It can be accessed by calling getRemoteMediaClient() method on the CastSession instance. Add the following methods to LocalPlayerActivity to load the currently selected video on the receiver:

private void loadRemoteMedia(int position, boolean autoPlay) {
    if (mCastSession == null) {
        return;
    }
    RemoteMediaClient remoteMediaClient = mCastSession.getRemoteMediaClient();
    if (remoteMediaClient == null) {
        return;
    }
    remoteMediaClient.load(buildMediaInfo(), autoPlay, position);
}

private MediaInfo buildMediaInfo() {
    MediaMetadata movieMetadata = new MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE);

    movieMetadata.putString(MediaMetadata.KEY_SUBTITLE, mSelectedMedia.getStudio());
    movieMetadata.putString(MediaMetadata.KEY_TITLE, mSelectedMedia.getTitle());
    movieMetadata.addImage(new WebImage(Uri.parse(mSelectedMedia.getImage(0))));
    movieMetadata.addImage(new WebImage(Uri.parse(mSelectedMedia.getImage(1))));

    return new MediaInfo.Builder(mSelectedMedia.getUrl())
            .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
            .setContentType("videos/mp4")
            .setMetadata(movieMetadata)
            .setStreamDuration(mSelectedMedia.getDuration() * 1000)
            .build();
}

Now update various existing methods to use the Cast session logic to support remote playback:

private void play(int position) {
    startControllersTimer();
    switch (mLocation) {
        case LOCAL:
            mVideoView.seekTo(position);
            mVideoView.start();
            break;
        case REMOTE:
            mPlaybackState = PlaybackState.BUFFERING;
            updatePlayButton(mPlaybackState);
            mCastSession.getRemoteMediaClient().seek(position);
            break;
        default:
            break;
    }
    restartTrickplayTimer();
}
private void togglePlayback() {
...
case IDLE:
...
    case REMOTE:
        if (mCastSession != null && mCastSession.isConnected()) {
            loadRemoteMedia(mSeekbar.getProgress(), true);
        }
        break;
     default:
...
}
private void onPause() {
...
    mCastContext.getSessionManager().removeSessionManagerListener(
                mSessionManagerListener, CastSession.class);
}
protected void onResume() {
    Log.d(TAG, "onResume() was called");
    mCastContext.getSessionManager().addSessionManagerListener(
            mSessionManagerListener, CastSession.class);
    if (mCastSession != null && mCastSession.isConnected()) {
        updatePlaybackLocation(PlaybackLocation.REMOTE);
    } else {
        updatePlaybackLocation(PlaybackLocation.LOCAL);
    }
    super.onResume();
}

For the updatePlayButton method, change the value of the isConnected variable:

private void updatePlayButton(PlaybackState state) {
    ...
    boolean isConnected = (mCastSession != null)
                && (mCastSession.isConnected() || 
                    mCastSession.isConnecting());
    ...
}

Now, run the app on your mobile device. Connect to your Cast device and start playing a video. You should see the video playing on the receiver.

The Cast Design Checklist requires that all Cast app provide mini controller that appear when the user navigates away from the current content page. The mini controller provide instant access and a visible reminder for the current Cast session.

The Cast SDK provides a custom view, MiniControllerFragment, which can be added to the app layout file of the activities in which you want to show the mini controller.

Add the following fragment definition to the bottom of both res/layout/player_activity.xml and res/layout/video_browser.xml:

<fragment
    android:id="@+id/castMiniController"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:visibility="gone"
    class="com.google.android.gms.cast.framework.media.widget.MiniControllerFragment"/>

Run the app and cast a video. When playback starts on the receiver you should see the mini controller appear at the bottom of each activity. You can control the remote playback using the mini controller. If you navigate between the browse activity and the local player activity, the mini controller state should stay in sync with the receiver media playback status.

The Google Cast design checklist requires a sender app to implement media controls from a notification and the lock screen.

The Cast SDK provides a MediaNotificationService to help the sender app build media controls for the notification and lock screen. The service is automatically merged into your app's manifest by gradle.

The MediaNotificationService will run in the background when sender is casting, and will show a notification with an image thumbnail and metadata about the current casting item, a play/pause button, and a stop button.

The notification and lock screen controls can be enabled with the CastOptions when initializing the CastContext. Media controls for the notification and lock screen are turned on by default. The lock screen feature is turned on as long as notification is turned on.

Edit the CastOptionsProvider and change the getCastOptions implementation to match this code:

public CastOptions getCastOptions(Context context) {
    NotificationOptions notificationOptions = new NotificationOptions.Builder()
            .setTargetActivityClassName(VideoBrowserActivity.class.getName())
            .build();
    CastMediaOptions mediaOptions = new CastMediaOptions.Builder()
            .setNotificationOptions(notificationOptions)
            .build();

    return new CastOptions.Builder()
            .setReceiverApplicationId(context.getString(R.string.app_id))
            .setCastMediaOptions(mediaOptions)
            .build();
}

Run the app on your mobile device. Cast a video and navigate away from the sample app. There should be a notification for the video currently playing on the receiver. Lock your mobile device and the lock screen should now display controls for the media playback on the Cast device.

The Google Cast design checklist requires a sender app to introduce the Cast button to existing users to let them know that the sender app now supports casting and also helps users new to Google Cast.

The Cast SDK provides a custom view, IntroductoryOverlay, that can be used to highlight the Cast button when it is first shown to users. Add the following code to VideoBrowserActivity:

private IntroductoryOverlay mIntroductoryOverlay;

private void showIntroductoryOverlay() {
    if (mIntroductoryOverlay != null) {
        mIntroductoryOverlay.remove();
    }
    if ((mediaRouteMenuItem != null) && mediaRouteMenuItem.isVisible()) {
        new Handler().post(new Runnable() {
            @Override
            public void run() {
                mIntroductoryOverlay = new IntroductoryOverlay.Builder(
                        VideoBrowserActivity.this, mediaRouteMenuItem)
                        .setTitleText("Introducing Cast")
                        .setSingleTime()
                        .setOnOverlayDismissedListener(
                                new IntroductoryOverlay.OnOverlayDismissedListener() {
                                    @Override
                                    public void onOverlayDismissed() {
                                        mIntroductoryOverlay = null;
                                    }
                                })
                        .build();
                mIntroductoryOverlay.show();
            }
        });
    }
}

Now, add a CastStateListener and call the showIntroductoryOverlay method when a Cast device is available by modifying the onCreate method to match the following :

private CastStateListener mCastStateListener;

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.video_browser);
    setupActionBar();

    mCastStateListener = new CastStateListener() {
        @Override
        public void onCastStateChanged(int newState) {
            if (newState != CastState.NO_DEVICES_AVAILABLE) {
                showIntroductoryOverlay();
            }
        }
    };
    mCastContext = CastContext.getSharedInstance(this);
}

@Override
protected void onResume() {
    mCastContext.addCastStateListener(mCastStateListener);
    super.onResume();
}

@Override
protected void onPause() {
    mCastContext.removeCastStateListener(mCastStateListener);
    super.onPause();
}

Run the app on your mobile device and you should seen the introductory overlay (clear the app data if the overlay does not display).

The Google Cast design checklist requires a sender app to provide expanded controller for the media being cast. The expanded controller is a full screen version of the mini controller.

The Cast SDK provides a widget for the expanded controller called ExpandedControllerActivity. This is an abstract class you have to subclass to add a Cast button.

Firstly, create a new menu resource file, called expanded_controller.xml, for the expanded controller to provide the Cast button:

<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto">

    <item
            android:id="@+id/media_route_menu_item"
            android:title="@string/media_route_menu_title"
            app:actionProviderClass="android.support.v7.app.MediaRouteActionProvider"
            app:showAsAction="always"/>

</menu>

Create a new class called ExpandedControlsActivity in the com.google.sample.cast.refplayer.expandedcontrols package.

public class ExpandedControlsActivity extends ExpandedControllerActivity {

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        super.onCreateOptionsMenu(menu);
        getMenuInflater().inflate(R.menu.expanded_controller, menu);
        CastButtonFactory.setUpMediaRouteButton(this, menu, R.id.media_route_menu_item);
        return true;
    }
}

Now declare the ExpandedControlsActivity in the app manifest within the application tag:

<application>
...
<activity
        android:name=".expandedcontrols.ExpandedControlsActivity"
        android:label="@string/app_name"
        android:launchMode="singleTask"
        android:theme="@style/Theme.CastVideosDark"
        android:screenOrientation="portrait">
    <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
    </intent-filter>
    <meta-data
            android:name="android.support.PARENT_ACTIVITY"
            android:value="com.google.sample.cast.refplayer.VideoBrowserActivity"/>
</activity>
...
</application>

Edit the CastOptionsProvider and change NotificationOptions and CastMediaOptions to set the target activity to the ExpandedControlsActivity:

public CastOptions getCastOptions(Context context) {
    NotificationOptions notificationOptions = new NotificationOptions.Builder()
            .setTargetActivityClassName(ExpandedControlsActivity.class.getName())
            .build();
    CastMediaOptions mediaOptions = new CastMediaOptions.Builder()
            .setNotificationOptions(notificationOptions)
            .setExpandedControllerActivityClassName(ExpandedControlsActivity.class.getName())
            .build();

    return new CastOptions.Builder()
            .setReceiverApplicationId(context.getString(R.string.app_id))
            .setCastMediaOptions(mediaOptions)
            .build();
}

Update the LocalPlayerActivity loadRemoteMedia method to display the ExpandedControlsActivity when the remote media is loaded:

private void loadRemoteMedia(int position, boolean autoPlay) {
    if (mCastSession == null) {
        return;
    }
    final RemoteMediaClient remoteMediaClient = mCastSession.getRemoteMediaClient();
    if (remoteMediaClient == null) {
        return;
    }
    remoteMediaClient.addListener(new RemoteMediaClient.Listener() {
        @Override
        public void onStatusUpdated() {
            Intent intent = new Intent(LocalPlayerActivity.this, ExpandedControlsActivity.class);
            startActivity(intent);
            remoteMediaClient.removeListener(this);
        }

        @Override
        public void onMetadataUpdated() {
        }

        @Override
        public void onQueueStatusUpdated() {
        }

        @Override
        public void onPreloadStatusUpdated() {
        }

        @Override
        public void onSendingRemoteMediaRequest() {
        }
    });
    remoteMediaClient.load(buildMediaInfo(), autoPlay, position);
}

Run the app on your mobile device and cast a video. You should see the expanded controller. Navigate back to the list of videos and when you click on the mini controller, the expanded controller will be loaded again. Navigate away from the app to see the notification. Click on the notification image to load the expanded controller.

Congratulations

You now know how to Cast-enable a video app using the Cast SDK widgets.

Take a look at our sample apps on GitHub: github.com/googlecast and join our Google Cast Developer community: g.co/googlecastdev