/* ChromeCastMediaPlayer.java Copyright (c) 2014 NTT DOCOMO,INC. Released under the MIT license http://opensource.org/licenses/mit-license.php */ package org.deviceconnect.android.deviceplugin.chromecast.core; import android.content.Intent; import android.webkit.MimeTypeMap; import com.google.android.gms.cast.Cast; 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.cast.RemoteMediaPlayer.MediaChannelResult; import com.google.android.gms.common.api.ResultCallback; import org.deviceconnect.android.deviceplugin.chromecast.BuildConfig; import java.io.IOException; import java.util.Locale; /** * Chromecast MediaPlayer クラス. * * <p> * MediaPlayer機能を提供する * </p> * @author NTT DOCOMO, INC. */ public class ChromeCastMediaPlayer implements ChromeCastController.Callbacks { /** Chromecast Controller. */ private ChromeCastController mController; /** RemoteMediaPlayer. */ private RemoteMediaPlayer mRemoteMediaPlayer; /** Chromecastの再生状態を受け取るコールバック. */ private Callbacks mCallbacks; /** Chromecastのファイルロード有効フラグ. */ private boolean mIsLoadEnable = false; /** * Chromecastの再生状態を通知するためのコールバックのインターフェース. * @author NTT DOCOMO, INC. */ public interface Callbacks { /** * 再生状態を通知する. * * @param status メディアのステータス */ void onChromeCastMediaPlayerStatusUpdate(final MediaStatus status); /** * 再生処理の結果を通知する. * * @param response レスポンス * @param result 再生処理結果 * @param message 再生処理のステータス */ void onChromeCastMediaPlayerResult(final Intent response, final MediaChannelResult result, final String message); } /** * コールバックを登録する. * * @param callbacks コールバック */ public void setCallbacks(final Callbacks callbacks) { this.mCallbacks = callbacks; } /** * コンストラクタ. * * @param controller ChromeCastApplication */ public ChromeCastMediaPlayer(final ChromeCastController controller) { mController = controller; mController.addCallbacks(this); } /** * デバイスが有効か否かを返す. * @return デバイスが有効か否か(有効: true, 無効: false) */ public boolean isDeviceEnable() { try { return (mController.getGoogleApiClient() != null); } catch (IllegalStateException e) { if (BuildConfig.DEBUG) { e.printStackTrace(); } return false; } } @Override public void onAttach() { mRemoteMediaPlayer = new RemoteMediaPlayer(); mRemoteMediaPlayer .setOnStatusUpdatedListener(new RemoteMediaPlayer.OnStatusUpdatedListener() { @Override public void onStatusUpdated() { if (mRemoteMediaPlayer.getMediaStatus() == null) { return; } mCallbacks.onChromeCastMediaPlayerStatusUpdate(mRemoteMediaPlayer.getMediaStatus()); } }); mRemoteMediaPlayer .setOnMetadataUpdatedListener(new RemoteMediaPlayer.OnMetadataUpdatedListener() { @Override public void onMetadataUpdated() { } }); try { Cast.CastApi.setMessageReceivedCallbacks(mController.getGoogleApiClient(), mRemoteMediaPlayer.getNamespace(), mRemoteMediaPlayer); } catch (IllegalStateException e) { if (BuildConfig.DEBUG) { e.printStackTrace(); } } catch (IOException e) { if (BuildConfig.DEBUG) { e.printStackTrace(); } } } @Override public void onDetach() { if (mRemoteMediaPlayer != null) { try { Cast.CastApi.removeMessageReceivedCallbacks(mController.getGoogleApiClient(), mRemoteMediaPlayer.getNamespace()); } catch (IllegalStateException e) { if (BuildConfig.DEBUG) { e.printStackTrace(); } } catch (IOException e) { if (BuildConfig.DEBUG) { e.printStackTrace(); } } mRemoteMediaPlayer = null; } } /** * メディアをロードする. * * @param response レスポンス * @param url メディアのURL * @param title メディアのタイトル */ public void load(final Intent response, final String url, final String title) { MediaInfo mediaInfo; MediaMetadata mediaMetadata = new MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE); mediaMetadata.putString(MediaMetadata.KEY_TITLE, title); String ext = MimeTypeMap.getFileExtensionFromUrl(url).toLowerCase(Locale.getDefault()); String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(ext); if (mimeType == null || (mimeType != null && mimeType.isEmpty())) { mimeType = "application/octet-stream"; } mediaInfo = new MediaInfo.Builder(url).setContentType(mimeType) .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED) .setMetadata(mediaMetadata).build(); if (mRemoteMediaPlayer != null) { try { mRemoteMediaPlayer .load(mController.getGoogleApiClient(), mediaInfo, false) .setResultCallback( new ResultCallback<RemoteMediaPlayer.MediaChannelResult>() { @Override public void onResult(final MediaChannelResult result) { if (result.getStatus().isSuccess()) { mIsLoadEnable = true; } else { mIsLoadEnable = false; } mCallbacks.onChromeCastMediaPlayerResult(response, result, "load"); } }); } catch (IllegalStateException e) { if (BuildConfig.DEBUG) { e.printStackTrace(); } mCallbacks.onChromeCastMediaPlayerResult(response, null, "load"); } } } /** * メディアをプレイする. * * @param response レスポンス */ public void play(final Intent response) { MediaInfo mediaInfo = null; if (mIsLoadEnable) { mediaInfo = mRemoteMediaPlayer.getMediaInfo(); } try { if (mRemoteMediaPlayer != null) { mRemoteMediaPlayer.load(mController.getGoogleApiClient(), mediaInfo, true).setResultCallback( new ResultCallback<RemoteMediaPlayer.MediaChannelResult>() { @Override public void onResult(final MediaChannelResult result) { mCallbacks.onChromeCastMediaPlayerResult(response, result, "load"); } }); } } catch (IllegalStateException e) { if (BuildConfig.DEBUG) { e.printStackTrace(); } mCallbacks.onChromeCastMediaPlayerResult(response, null, "load"); } } /** * メディアをレジュームする. * * @param response レスポンス */ public void resume(final Intent response) { try { if (mRemoteMediaPlayer != null) { mRemoteMediaPlayer.play(mController.getGoogleApiClient()).setResultCallback( new ResultCallback<RemoteMediaPlayer.MediaChannelResult>() { @Override public void onResult(final MediaChannelResult result) { mCallbacks.onChromeCastMediaPlayerResult(response, result, null); } }); } } catch (IllegalStateException e) { if (BuildConfig.DEBUG) { e.printStackTrace(); } mCallbacks.onChromeCastMediaPlayerResult(response, null, null); } } /** * メディアを停止する. * * @param response レスポンス */ public void stop(final Intent response) { try { if (mRemoteMediaPlayer != null) { mRemoteMediaPlayer.stop(mController.getGoogleApiClient()).setResultCallback( new ResultCallback<RemoteMediaPlayer.MediaChannelResult>() { @Override public void onResult(final MediaChannelResult result) { mCallbacks.onChromeCastMediaPlayerResult(response, result, null); } }); } } catch (IllegalStateException e) { if (BuildConfig.DEBUG) { e.printStackTrace(); } mCallbacks.onChromeCastMediaPlayerResult(response, null, null); } } /** * メディアを一時停止する. * * @param response レスポンス */ public void pause(final Intent response) { try { if (mRemoteMediaPlayer != null) { mRemoteMediaPlayer.pause(mController.getGoogleApiClient()).setResultCallback( new ResultCallback<RemoteMediaPlayer.MediaChannelResult>() { @Override public void onResult(final MediaChannelResult result) { mCallbacks.onChromeCastMediaPlayerResult(response, result, null); } }); } } catch (IllegalStateException e) { if (BuildConfig.DEBUG) { e.printStackTrace(); } mCallbacks.onChromeCastMediaPlayerResult(response, null, null); } } /** * メディアをミュートする. * * @param response レスポンス * @param mute ミュートするか否か (true: ミュートON, false: ミュートOFF) */ public void setMute(final Intent response, final boolean mute) { try { if (mRemoteMediaPlayer != null) { mRemoteMediaPlayer .setStreamMute(mController.getGoogleApiClient(), mute) .setResultCallback( new ResultCallback<RemoteMediaPlayer.MediaChannelResult>() { @Override public void onResult(final MediaChannelResult result) { mCallbacks.onChromeCastMediaPlayerResult(response, result, null); } }); } } catch (IllegalStateException e) { if (BuildConfig.DEBUG) { e.printStackTrace(); } mCallbacks.onChromeCastMediaPlayerResult(response, null, null); } } /** * メディアのミュートの状態を取得する. * * @param response レスポンス * @return ミュート状態 (1:ミュートON, 0: ミュートOFF) */ public int getMute(final Intent response) { if (mRemoteMediaPlayer == null) { return -1; } if (mRemoteMediaPlayer.getMediaStatus().isMute()) { return 1; } else { return 0; } } /** * メディアのボリュームを設定する. * * @param response レスポンス * @param volume ボリューム (0.0 <= volume <= 1.0) */ public void setVolume(final Intent response, final double volume) { try { if (mRemoteMediaPlayer != null) { mRemoteMediaPlayer .setStreamVolume(mController.getGoogleApiClient(), volume) .setResultCallback( new ResultCallback<RemoteMediaPlayer.MediaChannelResult>() { @Override public void onResult(final MediaChannelResult result) { mCallbacks.onChromeCastMediaPlayerResult(response, result, null); } }); } } catch (IllegalStateException e) { if (BuildConfig.DEBUG) { e.printStackTrace(); } mCallbacks.onChromeCastMediaPlayerResult(response, null, null); } } /** * メディアのボリュームを取得する. * * @param response レスポンス * @return volume ボリューム (0 <= volume <= 1.0, -1: エラー) */ public double getVolume(final Intent response) { if (mRemoteMediaPlayer != null) { return mRemoteMediaPlayer.getMediaStatus().getStreamVolume(); } else { return -1; } } /** * メディアのシークを設定する. * * @param response レスポンス * @param pos ポジション */ public void setSeek(final Intent response, final long pos) { try { if (mRemoteMediaPlayer != null) { mRemoteMediaPlayer .seek(mController.getGoogleApiClient(), pos) .setResultCallback( new ResultCallback<RemoteMediaPlayer.MediaChannelResult>() { @Override public void onResult(final MediaChannelResult result) { mCallbacks.onChromeCastMediaPlayerResult(response, result, null); } }); } } catch (IllegalStateException e) { if (BuildConfig.DEBUG) { e.printStackTrace(); } mCallbacks.onChromeCastMediaPlayerResult(response, null, null); } } /** * メディアのシークを取得する. * * @param response レスポンス * @return pos ポジション (-1: エラー) */ public long getSeek(final Intent response) { if (mRemoteMediaPlayer != null) { return mRemoteMediaPlayer.getApproximateStreamPosition(); } else { return -1; } } /** * メディアの状態を取得する. * * @return status メディアの状態 */ public MediaStatus getMediaStatus() { if (mRemoteMediaPlayer != null) { return mRemoteMediaPlayer.getMediaStatus(); } else { return null; } } }