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="@+id/media_route_menu_item" android:title="Chromecast" app:actionProviderClass="android.support.v7.app.MediaRouteActionProvider" app:showAsAction="always"/>
Step5: Write following into you main layout and strings.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity"> <Button android:id="@+id/button" android:text="Play Video" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </RelativeLayout>
Strings.xml
<resources> <string name="app_name">GoogleCastSenderApp</string> <string name="hello_world">Hello world!</string> <string name="action_settings">Settings</string> <string name="video_url"></string> <string name="app_id"></string> <string name="play_video">Play Video</string> <string name="pause_video">Pause Video</string> <string name="resume_video">Resume Video</string> <string name="video_title">Paul's Chromecast Video Stream</string> <string name="content_type_mp4">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<RemoteMediaPlayer.MediaChannelResult>() { @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<Cast.ApplicationConnectionResult>() { @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 ) && 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:
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
Top 10 Android App Development Trends | 2020 Guide
9 Popular Cross-Platform Tools for App Development in 2019
20 Best iOS App Development Tutorials and Online Learning Resources
Top 15 Best Android Apps For C Programming | 2018 Exclusive
8 Excellent Websites for Checking Google Keyword Rankings
50 Useful and Reasonably Popular Linux Applications
Android Studio Introduction
Applying MediaCodec On An Open Source Android Audio Player