Chromecastにキャストするサンプルアプリ作ってみた for Android

Chromecastがいよいよ日本で発売されました。

スマホやChromeブラウザから、画面出力するだけでしょ？と思っている人が結構多いと思うのですが、それは、Miracastの役割でして、Chromecastは、Chromecastがインターネットにアクセスして、コンテンツを表示するのが、本来の役割です。

Android端末から、Chromecastに命令を出して、Web上のコンテンツを表示するサンプルコードを作ってみました。（エラー処理とか基本無視ですｗｗｗ）

ここでは、自由にカスタマイズできるCustom Receiverを使ったものです。

試す為には、Google Cast SDK Developer契約（5ドル）払わなくてはなりませんのであしからず。

Receiverのアプリケーションは、HTML5(HTML,CSS,Javascript)で作ります。ここでは、下記のような簡単なHTMLをWebサーバにアップロードしておきます。

```html:mini.html
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8"/>
  <style type="text/css">
	body {
      overflow:hidden;
	}
    
    div {
	  height:720PX;
	  width:1280PX;
	  text-align:center;
	  border:0px solid silver;
	  display: table-cell;
	  vertical-align:middle;
	  color:#FFFFFF;
	  background-color:#000000;
	  font-weight:bold;
	  font-family:Verdana, Geneva, sans-serif;
	  font-size:40px;
	}
  </style>
  <title>Chromecastテストページ</title>
  <script src="//www.gstatic.com/cast/sdk/libs/receiver/2.0.0/cast_receiver.js"></script>
  <script type="text/javascript">
      window.onload = function() {
        cast.receiver.logger.setLevelValue(0);
        window.castReceiverManager = cast.receiver.CastReceiverManager.getInstance();
        console.log('Starting Receiver Manager');
        
        // handler for the 'ready' event
        castReceiverManager.onReady = function(event) {
          console.log('Received Ready event: ' + JSON.stringify(event.data));
          window.castReceiverManager.setApplicationState("Application status is ready...");
        };
        
        // handler for 'senderconnected' event
        castReceiverManager.onSenderConnected = function(event) {
          console.log('Received Sender Connected event: ' + event.data);
          console.log(window.castReceiverManager.getSender(event.data).userAgent);
        };
        
        // handler for 'senderdisconnected' event
        castReceiverManager.onSenderDisconnected = function(event) {
          console.log('Received Sender Disconnected event: ' + event.data);
          if (window.castReceiverManager.getSenders().length == 0) {
	        window.close();
	      }
        };
        
        // handler for 'systemvolumechanged' event
        castReceiverManager.onSystemVolumeChanged = function(event) {
          console.log('Received System Volume Changed event: ' + event.data['level'] + ' ' +
              event.data['muted']);
        };

        // initialize the CastReceiverManager with an application status message
        window.castReceiverManager.start({statusText: "Application is starting"});
        console.log('Receiver Manager started');
      };
  </script>
</head>
<body>
<div>Chromecastテストぺーじだよ〜〜〜ん</div>
</body>
</html>
```

[Google Cast SDK Developer Console](https://cast.google.com/publish/)上では、mini.htmlを置いたサイトのURLを記述します。

![140605-0001.png](https://qiita-image-store.s3.amazonaws.com/0/33220/47b5c658-4d47-37ea-62db-b11b1b69a3f1.png)

ここで、Custom Receiverを選択して

![140605-0002.png](https://qiita-image-store.s3.amazonaws.com/0/33220/25279625-d388-cc5a-5fe9-0a991ec6ebf8.png)

Applecation IDが発行されるので、Android側のソースコードにこのApplication IDを記述することになります。

コンテンツの準備ができたということで、次は、Android側の準備をすることになります。

Android SDK Managerで、ライブラリを追加します。

必要なのは
Android Support Library
Google Play Services
の２つです。

![140605-0003.png](https://qiita-image-store.s3.amazonaws.com/0/33220/f5dcc756-57fd-2f0c-cefe-70e18973c305.png)

インストールすると、Android SDKのフォルダのextrasにインストールされるので、Eclipseでインポートします！

importするライブラリは２つです。

android-support-v7-mediarouter
google-play-services_lib

![140605-0004.png](https://qiita-image-store.s3.amazonaws.com/0/33220/137e0c61-1c9d-fc43-5af4-88337852f9f3.png)

![140605-0005.png](https://qiita-image-store.s3.amazonaws.com/0/33220/4e236f2c-ca9c-ca07-f364-c3983c642f58.png)

![140605-0006.png](https://qiita-image-store.s3.amazonaws.com/0/33220/64cbec0b-cdd4-b143-6e23-059bcd81d4eb.png)

android-support-v7-mediarouterをインポートした際にエラーが出る場合がありますので、その時は、プロジェクトのプロバティで、app_compact_v7のライブラリのパスを修正してください。

![140605-0007.png](https://qiita-image-store.s3.amazonaws.com/0/33220/276234ff-cf27-4ccd-9c50-0bea842d421c.png)

これで、新規にプロジェクトを作って開発を始められます。

新規プロジェクトを作ったら、そのプロジェクトにライブラリーを追加します。

![140605-0008.png](https://qiita-image-store.s3.amazonaws.com/0/33220/8295dbbe-c1cb-5dd5-c565-d9490e4257a5.png)

これが終わったら、今度は、Manifestファイルの設定を変更します。

Applicationタグで、Google ServiceのAPIを使うので、metaタグを追加します。

![140605-0009.png](https://qiita-image-store.s3.amazonaws.com/0/33220/cc7e5497-4240-757b-52a9-f95c58eec14a.png)

次に、Parmissionです。
Wifiで通信しますので、INTERNETのパーミッションを追加します。

![140605-0010.png](https://qiita-image-store.s3.amazonaws.com/0/33220/6c32a870-522a-9851-64b8-3b49185190b4.png)

Google Playにアップするなら、もっと必要ですが、動作確認には、これで十分でしょう。

```java:MainActivity.java
package com.example.castcast;

import com.google.android.gms.cast.CastMediaControlIntent;
import android.app.Activity;
import android.os.Bundle;
import android.support.v7.media.MediaRouteSelector;
import android.support.v7.media.MediaRouter;
import android.support.v7.media.MediaRouter.RouteInfo;
import android.util.Log;

public class MainActivity extends Activity
{
	private String TAG = "test";
	
	// Google Cast SDK Developer Consoleから取得した Application IDを設定してください
	private static String APPLICATION_ID = "hogehoge";
		
	// Chromecastを探索するのに使う変数類
	private MediaRouter mRouter;
	private MediaRouteSelector mMediaRouteSelector;
	private MediaRouter.Callback mCB;
	
	@Override
	protected void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		// Chromecastを探す準備
		mRouter = MediaRouter.getInstance(this);
		mMediaRouteSelector = new MediaRouteSelector.Builder().addControlCategory(CastMediaControlIntent.categoryForCast(APPLICATION_ID)).build();
		mCB = new MediaRouter.Callback()
		{
			@Override
			public void onRouteAdded(MediaRouter router, RouteInfo route)
			{
				Log.d(TAG,"onRouteAdded=" + route.getName());
			}

			@Override
			public void onRouteRemoved(MediaRouter router, RouteInfo route)
			{
				Log.d(TAG,"onRouteRemoved" + route.getName());
			}
		};
	}
	
	@Override
	protected void onResume()
	{
		super.onResume();
		// Chromecastを探す
		mRouter.addCallback(mMediaRouteSelector, mCB,MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);
	}

	@Override
	protected void onPause()
	{
		if (isFinishing())
		{
		    mRouter.removeCallback(mCB);
		}
		super.onPause();
	}
}

```

これだけのソースで、同一ネットワークに接続されているChromecastのリストを取得することができます。

実行例Logcat出力
![140605-0011.png](https://qiita-image-store.s3.amazonaws.com/0/33220/1356bdbf-0202-b11b-20ed-7fdc5c257340.png)

デバイスが見つかったら、接続を行います。

```java:MainActivity.javaの抜粋
// 最初に見つけたデバイスに接続を行う。
if(mSelectedDevice == null)
{
    mSelectedDevice = CastDevice.getFromBundle(route.getExtras());
    
    Cast.CastOptions.Builder apiOptionsBuilder = Cast.CastOptions.builder(mSelectedDevice, mCastClientListener);
    apiOptionsBuilder.setVerboseLoggingEnabled(true);
    
    mApiClient = new GoogleApiClient.Builder(getBaseContext())
    .addApi(Cast.API, apiOptionsBuilder.build())
    .addConnectionCallbacks(mConnectionCallbacks)
    .addOnConnectionFailedListener(mConnectionFailedListener)
    .build();
    
    mApiClient.connect();
}
```

その他必要なコードは下記の通りです。

```java:MainActivity.java抜粋
	// Chromecastに接続確認用コールバック
	private GoogleApiClient.ConnectionCallbacks mConnectionCallbacks = new GoogleApiClient.ConnectionCallbacks()
	{
		@Override
		public void onConnected(Bundle connectionHint)
		{
			Log.d(TAG,"onConnected");
			
			try
			{
				Cast.CastApi.launchApplication(mApiClient, APPLICATION_ID, false)
				 .setResultCallback(new ResultCallback<Cast.ApplicationConnectionResult>()
				{
					@Override
					public void onResult(ApplicationConnectionResult result)
					{
						Status status = result.getStatus();
		                if (status.isSuccess())
		                {
		                	mSessionId = result.getSessionId();
		                	Log.d(TAG,"mSessionID=" + mSessionId);
		                }
					}
				});
			}
			catch(Exception ex)
			{
				Log.d(TAG,ex.toString());
			}			
		}

		@Override
		public void onConnectionSuspended(int cause)
		{
		}
	};

	// Chromecastに接続失敗した場合に呼ばれる
	private GoogleApiClient.OnConnectionFailedListener mConnectionFailedListener = new GoogleApiClient.OnConnectionFailedListener()
	{
		@Override
		public void onConnectionFailed(ConnectionResult result)
		{
		}
	};
	
	// Chromecastとの接続を解除する
	private void peardown()
	{		
		if (mApiClient != null)
		{
			if (mApiClient.isConnected())
			{
				try
				{
		            Cast.CastApi.stopApplication(mApiClient, mSessionId);
		        }
				catch (Exception e)
		        {
		        	Log.e(TAG, "Exception while removing channel", e);
		        }
				
				mApiClient.disconnect();
		    }
			
		    mApiClient = null;
		    mSelectedDevice = null;
		    mSessionId = null;
		}
	}
```

GitHubに動くプロジェクトファイル一式（[SampleCast](https://github.com/KazuyukiEguchi/SampleCast)）をアップデートしてありますので、必要に応じてご利用ください。



以上、ハッピーキャスティング！