Google Cast Sender App In Android

You must have heard about the chrome cast, a product launched by Google to share your small phone or tablet or laptop screen with TV. Google has sold millions of Chromecasts and Android TV until now. Knowing how to build an app that supports casting should be the dream/aim of every Android developer. Today in this post we will see how making an Android sender app to cast videos to a receiver app for displaying content on a television.

Read more about Casting you content to Big screen at google developers site.

Now, let’s get started with our Android application. Follow the steps as I have described below and at the end you should be able to cast your app on the big screen.

Here in this example we will cast a video on the big screen from our mobile.

Step1: Create a new Android application with minimum kitkat version in your android IDE.

Step2: The first thing you need to do after creating new app is adding the libraries required for casting to build. gradle file. (Assuming you are working on Android Studio.)

 

compile 'com.android.support:appcompat-v7:21.0.0'
compile 'com.android.support:mediarouter-v7:19.0.+'
compile 'com.google.android.gms:play-services:6.1.11'

 

Step3: Write below in your manifest file:

 

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.googlecastsenderapp" >

<uses-permission android:name="android.permission.INTERNET" />

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >

<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />

<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>

 

Step4: Add following code for button in you menu file:

 

<item android:id=&quot;@+id/media_route_menu_item&quot;
android:title=&quot;Chromecast&quot;
app:actionProviderClass=&quot;android.support.v7.app.MediaRouteActionProvider&quot;
app:showAsAction=&quot;always&quot;/>

 

Step5: Write following into you main layout and strings.xml:

 

<RelativeLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
xmlns:tools=&quot;http://schemas.android.com/tools&quot;
android:layout_width=&quot;match_parent&quot;
android:layout_height=&quot;match_parent&quot;
android:paddingLeft=&quot;@dimen/activity_horizontal_margin&quot;
android:paddingRight=&quot;@dimen/activity_horizontal_margin&quot;
android:paddingTop=&quot;@dimen/activity_vertical_margin&quot;
android:paddingBottom=&quot;@dimen/activity_vertical_margin&quot;
tools:context=&quot;.MainActivity&quot;>

<Button
android:id=&quot;@+id/button&quot;
android:text=&quot;Play Video&quot;
android:layout_width=&quot;wrap_content&quot;
android:layout_height=&quot;wrap_content&quot; />

</RelativeLayout>

 

Strings.xml

 

<resources>
<string name=&quot;app_name&quot;>GoogleCastSenderApp</string>

<string name=&quot;hello_world&quot;>Hello world!</string>
<string name=&quot;action_settings&quot;>Settings</string>
<string name=&quot;video_url&quot;></string>
<string name=&quot;app_id&quot;></string>
<string name=&quot;play_video&quot;>Play Video</string>
<string name=&quot;pause_video&quot;>Pause Video</string>
<string name=&quot;resume_video&quot;>Resume Video</string>
<string name=&quot;video_title&quot;>Paul's Chromecast Video Stream</string>
<string name=&quot;content_type_mp4&quot;>video/mp4</string>
</resources>

 

Enter the url of your video you wanted to cast into “video_url”.

Step6: Write below into your activity file:

 

package com.example.googlecastsenderapp;

import android.os.Bundle;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.MediaRouteActionProvider;
import android.support.v7.media.MediaRouteSelector;
import android.support.v7.media.MediaRouter;
import android.support.v7.media.MediaRouter.RouteInfo;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

import com.google.android.gms.cast.ApplicationMetadata;
import com.google.android.gms.cast.Cast;
import com.google.android.gms.cast.CastDevice;
import com.google.android.gms.cast.CastMediaControlIntent;
import com.google.android.gms.cast.MediaInfo;
import com.google.android.gms.cast.MediaMetadata;
import com.google.android.gms.cast.MediaStatus;
import com.google.android.gms.cast.RemoteMediaPlayer;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.common.api.Status;

import java.io.IOException;

public class MainActivity extends ActionBarActivity {

private Button mButton;

private MediaRouter mMediaRouter;
private MediaRouteSelector mMediaRouteSelector;
private MediaRouter.Callback mMediaRouterCallback;
private CastDevice mSelectedDevice;
private GoogleApiClient mApiClient;
private RemoteMediaPlayer mRemoteMediaPlayer;
private Cast.Listener mCastClientListener;
private boolean mWaitingForReconnect = false;
private boolean mApplicationStarted = false;
private boolean mVideoIsLoaded;
private boolean mIsPlaying;

@Override
protected void onCreate( Bundle savedInstanceState ) {
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_main );

mButton = (Button) findViewById( R.id.button );
mButton.setOnClickListener( new OnClickListener() {
@Override
public void onClick( View v ) {
if( !mVideoIsLoaded )
startVideo();
else
controlVideo();
}
});

initMediaRouter();
}

private void initMediaRouter() {

mMediaRouter = MediaRouter.getInstance( getApplicationContext() );
mMediaRouteSelector = new MediaRouteSelector.Builder()
.addControlCategory( CastMediaControlIntent.categoryForCast( getString( R.string.app_id ) ) )
.build();
mMediaRouterCallback = new MediaRouterCallback();
}

private void initCastClientListener() {
mCastClientListener = new Cast.Listener() {
@Override
public void onApplicationStatusChanged() {
}

@Override
public void onVolumeChanged() {
}

@Override
public void onApplicationDisconnected( int statusCode ) {
teardown();
}
};
}

private void initRemoteMediaPlayer() {
mRemoteMediaPlayer = new RemoteMediaPlayer();
mRemoteMediaPlayer.setOnStatusUpdatedListener( new RemoteMediaPlayer.OnStatusUpdatedListener() {
@Override
public void onStatusUpdated() {
MediaStatus mediaStatus = mRemoteMediaPlayer.getMediaStatus();
mIsPlaying = mediaStatus.getPlayerState() == MediaStatus.PLAYER_STATE_PLAYING;
}
});

mRemoteMediaPlayer.setOnMetadataUpdatedListener( new RemoteMediaPlayer.OnMetadataUpdatedListener() {
@Override
public void onMetadataUpdated() {
}
});
}

private void controlVideo() {
if( mRemoteMediaPlayer == null || !mVideoIsLoaded )
return;

if( mIsPlaying ) {
mRemoteMediaPlayer.pause( mApiClient );
mButton.setText( getString( R.string.resume_video ) );
} else {
mRemoteMediaPlayer.play( mApiClient );
mButton.setText( getString( R.string.pause_video ) );
}
}

private void startVideo() {
MediaMetadata mediaMetadata = new MediaMetadata( MediaMetadata.MEDIA_TYPE_MOVIE );
mediaMetadata.putString( MediaMetadata.KEY_TITLE, getString( R.string.video_title ) );

MediaInfo mediaInfo = new MediaInfo.Builder( getString( R.string.video_url ) )
.setContentType( getString( R.string.content_type_mp4 ) )
.setStreamType( MediaInfo.STREAM_TYPE_BUFFERED )
.setMetadata( mediaMetadata )
.build();
try {
mRemoteMediaPlayer.load( mApiClient, mediaInfo, true )
.setResultCallback( new ResultCallback&lt;RemoteMediaPlayer.MediaChannelResult&gt;() {
@Override
public void onResult( RemoteMediaPlayer.MediaChannelResult mediaChannelResult ) {
if( mediaChannelResult.getStatus().isSuccess() ) {
mVideoIsLoaded = true;
mButton.setText( getString( R.string.pause_video ) );
}
}
} );
} catch( Exception e ) {
}
}

@Override
protected void onResume() {
super.onResume();
mMediaRouter.addCallback( mMediaRouteSelector, mMediaRouterCallback, MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN );
}

@Override
protected void onPause() {
if ( isFinishing() ) {
mMediaRouter.removeCallback( mMediaRouterCallback );
}
super.onPause();
}

private class MediaRouterCallback extends MediaRouter.Callback {

@Override
public void onRouteSelected(MediaRouter router, RouteInfo info) {
initCastClientListener();
initRemoteMediaPlayer();

mSelectedDevice = CastDevice.getFromBundle( info.getExtras() );

launchReceiver();
}

@Override
public void onRouteUnselected( MediaRouter router, RouteInfo info ) {
teardown();
mSelectedDevice = null;
mButton.setText( getString( R.string.play_video ) );
mVideoIsLoaded = false;
}
}

private void launchReceiver() {
Cast.CastOptions.Builder apiOptionsBuilder = Cast.CastOptions
.builder( mSelectedDevice, mCastClientListener );

ConnectionCallbacks mConnectionCallbacks = new ConnectionCallbacks();
ConnectionFailedListener mConnectionFailedListener = new ConnectionFailedListener();
mApiClient = new GoogleApiClient.Builder( this )
.addApi( Cast.API, apiOptionsBuilder.build() )
.addConnectionCallbacks( mConnectionCallbacks )
.addOnConnectionFailedListener( mConnectionFailedListener )
.build();

mApiClient.connect();
}

private class ConnectionCallbacks implements GoogleApiClient.ConnectionCallbacks {

@Override
public void onConnected( Bundle hint ) {
if( mWaitingForReconnect ) {
mWaitingForReconnect = false;
reconnectChannels( hint );
} else {
try {
Cast.CastApi.launchApplication( mApiClient, getString( R.string.app_id ), false )
.setResultCallback( new ResultCallback&lt;Cast.ApplicationConnectionResult&gt;() {
@Override
public void onResult(Cast.ApplicationConnectionResult applicationConnectionResult) {
Status status = applicationConnectionResult.getStatus();
if( status.isSuccess() ) {
ApplicationMetadata applicationMetadata = applicationConnectionResult.getApplicationMetadata();
String sessionId = applicationConnectionResult.getSessionId();
String applicationStatus = applicationConnectionResult.getApplicationStatus();
boolean wasLaunched = applicationConnectionResult.getWasLaunched();

mApplicationStarted = true;
reconnectChannels( null );
}
}
}
);
} catch ( Exception e ) {

}
}
}

@Override
public void onConnectionSuspended(int i) {
mWaitingForReconnect = true;
}
}

private void reconnectChannels( Bundle hint ) {
if( ( hint != null ) &amp;&amp; hint.getBoolean( Cast.EXTRA_APP_NO_LONGER_RUNNING ) ) {
teardown();
} else {
try {
Cast.CastApi.setMessageReceivedCallbacks( mApiClient, mRemoteMediaPlayer.getNamespace(), mRemoteMediaPlayer );
} catch( IOException e ) {
} catch( NullPointerException e ) {
}
}
}

private class ConnectionFailedListener implements GoogleApiClient.OnConnectionFailedListener {
@Override
public void onConnectionFailed( ConnectionResult connectionResult ) {
teardown();
}
}

@Override
public boolean onCreateOptionsMenu( Menu menu ) {
super.onCreateOptionsMenu( menu );
getMenuInflater().inflate( R.menu.menu_main, menu );
MenuItem mediaRouteMenuItem = menu.findItem( R.id.media_route_menu_item );
MediaRouteActionProvider mediaRouteActionProvider = (MediaRouteActionProvider) MenuItemCompat.getActionProvider( mediaRouteMenuItem );
mediaRouteActionProvider.setRouteSelector( mMediaRouteSelector );
return true;
}

private void teardown() {
if( mApiClient != null ) {
if( mApplicationStarted ) {
try {
Cast.CastApi.stopApplication( mApiClient );
if( mRemoteMediaPlayer != null ) {
Cast.CastApi.removeMessageReceivedCallbacks( mApiClient, mRemoteMediaPlayer.getNamespace() );
mRemoteMediaPlayer = null;
}
} catch( IOException e ) {
}
mApplicationStarted = false;
}
if( mApiClient.isConnected() )
mApiClient.disconnect();
mApiClient = null;
}
mSelectedDevice = null;
mVideoIsLoaded = false;
}

@Override
public void onDestroy() {
teardown();
super.onDestroy();
}
}

 

Step7: Run on your device and try to cast the video to big screen. You should be able to connect and cast when you “Play Video” as below:

cast1

 

cast2

 

I will add some more features to this cast sender app in my next blog. Check my previous blog on Updating progress bar with countdown timer

Leave a Comment: