Google Cast SDK Tutorial for Android

Written by: Aaron Dominick | January 29th, 2018

Category: Android

Enabling your android app to cast media to other devices such as a google home speaker or a chromecast enabled TV isn’t that scary but it does require a fair amount of setting up.

Steps:

  1. 1 Configure Android Studio
  2. Add the Cast SDK to the project
  3. Create the CastOptionsProvider and ExpandedControls
  4. Add the Media Route button
  5. Set up the Session Manager
  6. Set up the RemoteClient

Step one Configuring your IDE:

Update your android studio and SDK to the latest versions available.

Next install (if you don’t already have it) the Google Play Services SDK

Screen 1  

Step two Adding the Cast SDK to your app:

Open your android studio project and add the following libraries to your build.gradle file

implementation 'com.android.support:mediarouter-v7:27.0.2'
implementation 'com.google.android.gms:play-services-cast-framework:11.6.2'
 

Step three Create the CastOptionsProvider and the Expanded controls

Next create a new class that implements the OptionsProvider interface

public class CastOptionsProvider implements OptionsProvider {
    @Override
    public CastOptions getCastOptions(Context appContext) {
        List<String> buttonActions = new ArrayList<>();
        buttonActions.add(MediaIntentReceiver.ACTION_REWIND);
        buttonActions.add(MediaIntentReceiver.ACTION_TOGGLE_PLAYBACK);
        buttonActions.add(MediaIntentReceiver.ACTION_FORWARD);
        buttonActions.add(MediaIntentReceiver.ACTION_STOP_CASTING);
// Showing "play/pause" and "stop casting" in the compat view of the notification.
        int[] compatButtonActionsIndicies = new int[]{ 1, 3 };
// Builds a notification with the above actions. Each tap on the "rewind" and
// "forward" buttons skips 30 seconds.
        NotificationOptions notificationOptions = new NotificationOptions.Builder()
                .setTargetActivityClassName(ExpandedControlsActivity.class.getName())
                .setActions(buttonActions, compatButtonActionsIndicies)
                .setSkipStepMs(30 * DateUtils.SECOND_IN_MILLIS)
                .build();
        CastMediaOptions mediaOptions = new CastMediaOptions.Builder()
                .setNotificationOptions(notificationOptions)
                .setExpandedControllerActivityClassName(ExpandedControlsActivity.class.getName())
                .build();
        return new CastOptions.Builder()
                .setReceiverApplicationId(CastMediaControlIntent.DEFAULT_MEDIA_RECEIVER_APPLICATION_ID)
                .setCastMediaOptions(mediaOptions)
                .build();
    }
    @Override
    public List<SessionProvider> getAdditionalSessionProviders(Context context) {
        return null;
    }
}
 

The Cast Options Provider handles the options for the media control widgets such as the expanded controls, the notification controls and the mini controller.

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

The Expanded Controls Activity starts when casting is enabled. It contains the playback and seek controls

Step four Add the Media route button

Next add a Media route button. This is also provided by the cast sdk. If you are using an AppCompat activity it can be added as part of the menu:

<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>
 

Step five Manage the Cast Sessions

Next we need a Cast Context and a Session Manager that automatically manages our device sessions and can handle the network operations. In the activity that displays your video, create a Cast Context object and get its Session Manager:

public class VideoActivity extends AppCompatActivity {
private CastContext mCastContext;
private CastSession mCastSession;
private SessionManagerListener<CastSession> mSessionManagerListener;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.video__video);
    myToolbar = findViewById(R.id.app_toolbar);
    fallbackView = findViewById(R.id.fallback_video);
    controller = new MediaController(this);
    controller.setAnchorView(fallbackView);
    fallbackView.setMediaController(controller);
    fallbackView.setVisibility(View.GONE);
    progressDialog = new ProgressDialog(this);
    setSupportActionBar(myToolbar);
    getSupportActionBar().setDisplayHomeAsUpEnabled(true);
    getSupportActionBar().setDisplayShowHomeEnabled(true);
    getSupportActionBar().setHomeAsUpIndicator(R.drawable.back_white);
    getSupportActionBar().setDisplayShowTitleEnabled(false);
    Paper.init(this);
    ButterKnife.bind(this);
    mCastContext = CastContext.getSharedInstance(this);
    mCastStateListener = new CastStateListener() {
        @Override
        public void onCastStateChanged(int newState) {
            if (newState != CastState.NO_DEVICES_AVAILABLE) {
                showIntroductoryOverlay();
            }
        }
    };
    mCastSession = mCastContext.getSessionManager().getCurrentCastSession();
mSessionManagerListener = new SessionManagerListener<CastSession>() {
    @Override
    public void onSessionEnded(CastSession session, int error) {
        onApplicationDisconnected(session);
    }
    @Override
    public void onSessionResumed(CastSession session, boolean wasSuspended) {
        onApplicationConnected(session);
    }
    @Override
    public void onSessionResumeFailed(CastSession session, int error) {
        onApplicationDisconnected(session);
    }
    @Override
    public void onSessionStarted(CastSession session, String sessionId) {
        onApplicationConnected(session);
    }
    @Override
    public void onSessionStartFailed(CastSession session, int error) {
        onApplicationDisconnected(session);
    }
    @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) {
    }
};
mCastContext.getSessionManager().addSessionManagerListener(
        mSessionManagerListener, CastSession.class);
}
}
 

Step six Set up the Remote Media Client

Media playback on the cast device is handled by a Remote Media Client object. This contains listeners that track status updates and progress information of the media

 remoteMediaClient = mCastSession.getRemoteMediaClient();
        if (remoteMediaClient == null) {
            return;
        }
        remoteMediaClient.addProgressListener(new RemoteMediaClient.ProgressListener() {
            @Override
            public void onProgressUpdated(long l, long l1) {
                playerPosition = l;
//                Log.e("PLAYER_POS_PROG",String.valueOf(playerPosition));
            }
        },1000);
        remoteMediaClient.addListener(new RemoteMediaClient.Listener() {
            @Override
            public void onStatusUpdated() {
                Intent intent = new Intent(VideoActivity.this, ExpandedControlsActivity.class);
                startActivity(intent);
                remoteMediaClient.removeListener(this);
            }
            @Override
            public void onMetadataUpdated() {
            }
            @Override
            public void onQueueStatusUpdated() {
            }
            @Override
            public void onPreloadStatusUpdated() {
            }
            @Override
            public void onSendingRemoteMediaRequest() {
            }
            @Override
            public void onAdBreakStatusUpdated() {
            }
        });
 

The Cast SDK requires some additional metadata for the proper presentation of the cast UI elements. This is handled by the Media Info and Media Metadata objects.

private MediaInfo buildMediaInfo() {
    MediaMetadata movieMetadata = new MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE);
    movieMetadata.putString(MediaMetadata.KEY_SUBTITLE, VIDEO_TITLE);
    movieMetadata.putString(MediaMetadata.KEY_TITLE, VIDEO_SUBTITLE);
    movieMetadata.addImage(new WebImage(Uri.parse(VIDEO_THUMBNAIL_URL)));
    return new MediaInfo.Builder(VIDEO_URL)
            .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
            .setContentType("videos/mp4")
            .setMetadata(movieMetadata)
            .build();
}
 

To start the casting all we now need to do is load the media and it’s metadata into the Remote Client

remoteMediaClient.load(buildMediaInfo(), true,playerPosition);
 

This starts casting the media to a compatible device at the position set by the Player Position variable. Tracking the play progress to resume where the media was paused between devices is done by the Progress Listener.

Adding cast functionality to your app may seem daunting at first but it is pretty straightforward and can be implemented on top of either the standard android media framework or with a custom video playback solution.

Comments

comments

Leave Comment

We would love to hear from you and we appreciate the good and the bad.

Comments