CCL 送信者アプリをキャスト アプリケーション フレームワーク(CAF)に移行する

Android 送信者アプリを CCL を含む Cast SDK v2 から CAF に変換する手順は次のとおりです。CCL のすべての機能は CAF に実装されているため、移行後は CCL を使用する必要はありません。

Cast CAF Sender SDK は CastContext を使用して GoogleAPIClient を管理します。CastContext はライフサイクル、エラー、コールバックを管理し、Cast アプリの開発を大幅に簡素化します。

はじめに

  • CAF Sender の設計は Cast コンパニオン ライブラリの影響を受けたため、CCF から CAF Sender に移行する場合、ほとんどの場合、クラスとそのメソッドを 1 対 1 でマッピングします。
  • CAF Sender は引き続き Android SDK Manager を使用して Google Play 開発者サービスの一部として配布されます。
  • CCL と同様の機能を備えた CAF Sender に追加された新しいパッケージ(com.google.android.gms.cast.framework.*)は、Google Cast Design チェックリストを遵守する責任があります。
  • CAF センダーは、キャスト UX の要件を遵守するウィジェットを提供します。これらのウィジェットは CCL のウィジェットに似ています。
  • CAF 送信者は、状態を追跡してデータを取得するために、CCL に似た非同期コールバックを提供します。CCL とは異なり、CAF Sender は、さまざまなインターフェース メソッドの no-op 実装を提供していません。

以降のセクションでは、主に CCL の VideoCastManager に基づく動画中心のアプリケーションに焦点を当てていますが、多くの場合、同じ概念が DataCastManager にも適用されます。

依存関係

CCL と CAF の依存関係は、AppCompat サポート ライブラリ、MediaRouter v7 サポート ライブラリ、Google Play 開発者サービスで同じです。ただし、CAF は、Google Play 開発者サービス 9.2.0 以降で利用可能な新しいキャスト フレームワークに依存する点が異なります。

build.gradle ファイルで、com.google.android.gms:play-services-castcom.google.android.libraries.cast.companionlibrary:ccl への依存関係を削除してから、新しいキャスト フレームワークを追加します。

dependencies {
    compile 'com.android.support:appcompat-v7:23.4.0'
    compile 'com.android.support:mediarouter-v7:23.4.0'
    compile 'com.google.android.gms:play-services-cast-framework:9.4.0'
}

Google Play 開発者サービスのメタデータを削除することもできます。

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

CAF に含まれるサービス、アクティビティ、リソースは、アプリのマニフェストとリソースと自動的に結合されます。

CAF がサポートする Android SDK の最小バージョンは 9(Gingerbread)です。CCL の最小 Android SDK バージョンは 10 です。

CCL には、デバイスで Google Play 開発者サービスの互換バージョンを利用できることを確認するためのコンビニエンス メソッド BaseCastManager.checkGooglePlayServices(activity) が用意されています。CAF は Cast SDK の一部としてこれを提供していません。デバイスに Google Play 開発者サービスの APK があることを確認するの手順に沿って、アップデートがすべてのユーザーにすぐに届くわけではないため、ユーザーのデバイスに正しい Google Play 開発者サービス APK がインストールされていることを確認します。

アプリのテーマには、Theme.AppCompat のバリアントを使用する必要があります。

初期化

CCL の場合、VideoCastManager.initialize() をアプリケーション インスタンスの onCreate() メソッドで呼び出す必要がありました。このロジックは、アプリケーション クラスコードから削除する必要があります。

CAF では、Cast フレームワークにも明示的な初期化手順が必要です。これには、適切な OptionsProvider を使用してレシーバ アプリケーション ID とその他のグローバル オプションを指定する CastContext シングルトンを初期化する必要があります。CastContext は、クライアントが操作するシングルトンを提供することで、CCL の VideoCastManager と同様の役割を果たします。OptionsProvider は CCL の CastConfiguration に類似しており、Cast フレームワーク機能を構成できます。

現在の CCL が CastConfiguration.Builder の場合:

VideoCastManager.initialize(
   getApplicationContext(),
   new CastConfiguration.Builder(context.getString(R.string.app_id))
       .enableWifiReconnection()
       .enableAutoReconnect()
       .build());

CAF では、CastOptions.Builder を使用する次の CastOptionsProvider は次のようになります。

public class CastOptionsProvider implements OptionsProvider {

    @Override
    public CastOptions getCastOptions(Context context) {
        return new CastOptions.Builder()
                .setReceiverApplicationId(context.getString(R.string.app_id))
                .build();
    }

    @Override
    public List<SessionProvider> getAdditionalSessionProviders(
            Context context) {
        return null;
    }
}

OptionsProvider の完全な実装については、サンプルアプリをご覧ください。

AndroidManifest.xml ファイルの「application」要素で OptionsProvider を宣言します。

<application>
...
    <meta-data
        android:name=
          "com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
        android:value="com.google.sample.cast.refplayer.CastOptionsProvider"    
    />
</application>

ActivityonCreate メソッド(Application インスタンスではない)で CastContext を遅延初期化します。

private CastContext mCastContext;

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.video_browser);
    setupActionBar();

    mCastContext = CastContext.getSharedInstance(this);
}

CastContext シングルトンにアクセスするには、次のようにします。

mCastContext = CastContext.getSharedInstance(this);

デバイス検出

CCL の VideoCastManager incrementUiCounterdecrementUiCounter は、ActivitiesonResume メソッドと onPause メソッドから削除する必要があります。

CAF では、アプリがフォアグラウンドに移行してバックグラウンドに移行すると、フレームワークによって検出プロセスが自動的に開始、停止されます。

キャスト ボタンとキャスト ダイアログ

CCL と同様に、これらのコンポーネントは MediaRouter v7 サポート ライブラリによって提供されます。

キャスト アイコンは引き続き MediaRouteButton によって実装され、メニューのメニュー項目として(ActionBar または Toolbar を使用して)アクティビティに追加できます。

メニュー xml 内の MediaRouteActionProvider の宣言は、CCL の場合と同じです。

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

CCL と同様に、各アクティビティの onCreateOptionMenu() メソッドをオーバーライドします。ただし、CastManager.addMediaRouterButton を使用する代わりに、CAF の CastButtonFactory を使用して MediaRouteButton をキャスト フレームワークに接続します。

public boolean onCreateOptionsMenu(Menu menu) {
    super.onCreateOptionsMenu(menu);
    getMenuInflater().inflate(R.menu.browse, menu);
    CastButtonFactory.setUpMediaRouteButton(getApplicationContext(),
                                                menu,
                                                R.id.media_route_menu_item);
    return true;
}

デバイス コントロール

CCL と同様に、CAF ではデバイス制御の大部分がフレームワークによって処理されます。送信者アプリは、GoogleApiClient を使用してデバイスに接続してレシーバ アプリを起動する必要はなく、処理を試みる必要はありません。

送信者と受信者間のやり取りは「セッション」として表されるようになりました。SessionManager クラスは、セッションのライフサイクルを処理し、ユーザーの操作に応じてセッションを自動的に開始および停止します。ユーザーがキャスト ダイアログでキャスト デバイスを選択したときにセッションが開始され、ユーザーがキャスト ダイアログで [キャストを停止] ボタンをタップしたか、送信アプリ自体が終了したときに終了します。

CCL でキャスト セッションのステータスを追跡するには、VideoCastConsumerImpl クラスを拡張する必要があります。

private final VideoCastConsumer mCastConsumer = new VideoCastConsumerImpl() {
  public void onApplicationConnected(ApplicationMetadata appMetadata, 
                                     String sessionId,
                                     boolean wasLaunched) {}
  public void onDisconnectionReason(int reason) {}
  public void onDisconnected() {}
}

CAF では、SessionManagerSessionManagerListener を登録することで、送信側アプリにセッション ライフサイクル イベントの通知を受け取ることができます。SessionManagerListener コールバックは、すべてのセッション ライフサイクル イベントのコールバック メソッドを定義します。

次の SessionManagerListener メソッドは、CCL の VideoCastConsumer インターフェースからマッピングされます。

  • VideoCastConsumer.onApplicationConnected -> SessionManagerListener.onSessionStarted
  • VideoCastConsumer.onDisconnected -> SessionManagerListener.onSessionEnded

SessionManagerListener インターフェースを実装するクラスを宣言し、VideoCastConsumerImpl ロジックを、対応するメソッドに移動します。

private class CastSessionManagerListener implements SessionManagerListener<CastSession> {
  public void onSessionEnded(CastSession session, int error) {}
  public void onSessionStarted(CastSession session, String sessionId) {}
  public void onSessionEnding(CastSession session) {}
  ...
}

CastSession クラスは、キャスト デバイスとのセッションを表します。このクラスには、BaseCastManager で CCL が行うように、デバイスの音量とミュート状態を制御するメソッドが用意されています。

CCL の VideoCastManager を使用してコンシューマを追加する代わりに、次のことを行います。

VideoCastManager.getInstance().addVideoCastConsumer(mCastConsumer);

SessionManagerListener を登録します。

mCastSessionManager = 
    CastContext.getSharedInstance(this).getSessionManager();
mCastSessionManagerListener = new CastSessionManagerListener();
mCastSessionManager.addSessionManagerListener(mCastSessionManagerListener,
                  CastSession.class);

CCL でイベントのリッスンを停止するには:

VideoCastManager.getInstance().removeVideoCastConsumer(mCastConsumer);

次に、SessionManager を使用してセッション イベントのリッスンを停止します。

mCastSessionManager.removeSessionManagerListener(mCastSessionManagerListener,
                    CastSession.class);

キャスト デバイスとの接続を明示的に解除するには、CCL を使用しました。

VideoCastManager.disconnectDevice(boolean stopAppOnExit, 
            boolean clearPersistedConnectionData,
            boolean setDefaultRoute)

CAF の場合は、SessionManager を使用します。

CastContext.getSharedInstance(this).getSessionManager()
                                   .endCurrentSession(true);

送信者が受信者に接続されているかどうかを判断するために、CCL は VideoCastManager.getInstance().isConnected() を提供しますが、CAF では SessionManager を使用します。

public boolean isConnected() {
    CastSession castSession = CastContext.getSharedInstance(mAppContext)
                                  .getSessionManager()
                                  .getCurrentCastSession();
    return (castSession != null && castSession.isConnected());
}

CAF では、ボリューム / ミュート状態変更通知は、Cast.Listener のコールバック メソッドを介して引き続き配信されます。これらのリスナーは CastSession に登録されます。残りのデバイス状態通知はすべて、CastStateListener コールバックを介して配信されます。これらのリスナーは CastSession に登録されます。関連付けられているフラグメント、アクティビティ、またはアプリがバックグラウンドに移動したときに、リスナーの登録を解除してください。

再接続ロジック

CAF は、一時的な Wi-Fi 信号の喪失やその他のネットワーク エラーによって失われたネットワーク接続の再確立を試みます。これはセッション レベルで行われるようになりました。接続が失われた場合はセッションが「一時停止」状態になり、接続が復元されると「接続済み」状態に戻ります。フレームワークはこのプロセスでレシーバー アプリに再接続し、キャスト チャンネルを再接続します。

CAF には独自の再接続サービスがあるため、マニフェストから CCL ReconnectionService を削除できます。

<service android:name="com.google.android.libraries.cast.companionlibrary.cast.reconnection.ReconnectionService"/>

また、再接続ロジックのマニフェストに次の権限は必要ありません。

<uses‐permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses‐permission android:name="android.permission.ACCESS_WIFI_STATE"/>

CAF 再接続サービスはデフォルトで有効になっていますが、CastOptions を使用して無効にできます。

さらに、CAF は自動セッション再開も有効にします。これはデフォルトで有効になっています(CastOptions で無効にできます)。キャスト セッションの進行中に送信者アプリケーションがバックグラウンドに送信されるか、(スワイプによって、またはクラッシュして)終了した場合、送信側アプリがフォアグラウンドに戻るか、再起動されると、フレームワークはそのセッションを再開しようとします。これは SessionManager によって自動的に処理されます。

カスタム チャンネル登録

CCL には、受信者へのカスタム メッセージ チャネルを作成するための 2 つの方法が用意されています。

  • CastConfiguration を使用すると複数の名前空間を指定すると、CCL によってチャンネルが作成されます。
  • DataCastManager は VideoCastManager に似ていますが、メディア以外のユースケースに焦点を当てています。

カスタム チャネルを作成する方法はどちらも CAF でサポートされておらず、送信者のアプリのカスタム チャネルの追加の手順を行う必要があります。

CCL と同様に、メディアアプリの場合、メディア コントロール チャンネルを明示的に登録する必要はありません。

メディア コントロール

CAF では、RemoteMediaClient クラスは VideoCastManager メディア メソッドと同等です。RemoteMediaClient.ListenerVideoCastConsumer メソッドと同等です。特に、VideoCastConsumeronRemoteMediaPlayerMetadataUpdated メソッドと onRemoteMediaPlayerStatusUpdated メソッドは、それぞれ RemoteMediaClient.ListeneronMetadataUpdated メソッドと onStatusUpdated メソッドにマッピングされます。

private class CastMediaClientListener implements RemoteMediaClient.Listener {

    @Override
    public void onMetadataUpdated() {
        setMetadataFromRemote();
    }

    @Override
    public void onStatusUpdated() {
        updatePlaybackState();
    }

    @Override
    public void onSendingRemoteMediaRequest() {
    }

    @Override
    public void onQueueStatusUpdated() {
    }

    @Override
    public void onPreloadStatusUpdated() {
    }
}

RemoteMediaClient オブジェクトを明示的に初期化または登録する必要はありません。レシーバー アプリが接続されていて、メディア名前空間をサポートしている場合、フレームワークは自動的にオブジェクトをインスタンス化し、基盤となるメディア チャンネルを登録します。

RemoteMediaClient には、CastSession オブジェクトの getRemoteMediaClient メソッドとしてアクセスできます。

CastSession castSession = CastContext.getSharedInstance(mAppContext)
                                     .getSessionManager()
                                     .getCurrentCastSession();
mRemoteMediaClient = castSession.getRemoteMediaClient();
mRemoteMediaClientListener = new CastMediaClientListener();

CCL の代わりに:

VideoCastManager.getInstance().addVideoCastConsumer(mCastConsumer);

次に、CAF を使用します。

mRemoteMediaClient.addListener(mRemoteMediaClientListener);

任意の数のリスナーを RemoteMediaClient に登録できます。これにより、複数の送信者コンポーネントが、セッションに関連付けられている RemoteMediaClient の単一インスタンスを共有できます。

CCL の VideoCastManager は、メディア再生を処理するメソッドを提供します。

VideoCastManager manager = VideoCastManager.getInstance();
if (manager.isRemoteMediaLoaded()) {
    manager.pause();
    mCurrentPosition = (int) manager.getCurrentMediaPosition();
}

これらは Remote MediaClient によって CAF で実装されます。

if (mRemoteMediaClient.hasMediaSession()) {
    mRemoteMediaClient.pause();
    mCurrentPosition = 
        (int)mRemoteMediaClient.getApproximateStreamPosition();
}

CAF では、RemoteMediaClient で発行されたすべてのメディア リクエストは、PendingResult コールバックを介して RemoteMediaClient.MediaChannelResult を返します。これを使用して、リクエストの進行状況と最終的な結果を追跡できます。

CCL と CAF はどちらも MediaInfo クラスと MediaMetadata クラスを使用して、メディア アイテムを表し、メディアを読み込みます。

CCL でメディアを読み込むには、VideoCastManager を使用します。

VideoCastManager.getInstance().loadMedia(media, autoPlay, mCurrentPosition, customData);

CAF では、RemoteMediaClient を使用してメディアを読み込みます。

mRemoteMediaClient.load(media, autoPlay, mCurrentPosition, customData);

CCL は、レシーバ上の現在のメディア セッションの Media 情報とステータスを取得するために、VideoCastManager を使用します。

MediaInfo mediaInfo = VideoCastManager.getInstance()
                                      .getRemoteMediaInformation();
int status = VideoCastManager.getInstance().getPlaybackStatus();
int idleReason = VideoCastManager.getInstance().getIdleReason();

CAF で、RemoteMediaClient を使用して同じ情報を取得します。

MediaInfo mediaInfo = mRemoteMediaClient.getMediaInfo();
int status = mRemoteMediaClient.getPlayerState();
int idleReason = mRemoteMediaClient.getIdleReason();

案内用のオーバーレイ

CCL と同様に、CAF には、ユーザーに初めて表示されるときにキャスト アイコンをハイライト表示するカスタムビュー IntroductoryOverlay が用意されています。

CCL の VideoCastConsumer onCastAvailabilityChanged メソッドを使用してオーバーレイを表示するタイミングを判断するのではなく、CastStateListener を宣言し、MediaRouter によってローカル ネットワークでキャスト デバイスが検出されるとキャスト アイコンが表示されるタイミングを決定します。

private IntroductoryOverlay mIntroductoryOverlay;
private MenuItem mMediaRouteMenuItem;

protected void onCreate(Bundle savedInstanceState) {
    ...
    mCastStateListener = new CastStateListener() {
        @Override
        public void onCastStateChanged(int newState) {
            if (newState != CastState.NO_DEVICES_AVAILABLE) {
                showIntroductoryOverlay();
            }
        }
    };
    mCastContext = CastContext.getSharedInstance(this);
    mCastContext.registerLifecycleCallbacksBeforeIceCreamSandwich(this, 
        savedInstanceState);
}

protected void onResume() {
    mCastContext.addCastStateListener(mCastStateListener);
    ...
}

protected void onPause() {
    mCastContext.removeCastStateListener(mCastStateListener);
    ...
}

MediaRouteMenuItem インスタンスを追跡します。

public boolean onCreateOptionsMenu(Menu menu) {
   super.onCreateOptionsMenu(menu);
    getMenuInflater().inflate(R.menu.browse, menu);
    mMediaRouteMenuItem = CastButtonFactory.setUpMediaRouteButton(
            getApplicationContext(), menu,
            R.id.media_route_menu_item);
    showIntroductoryOverlay();
    return true;
}

紹介オーバーレイを表示できるように MediaRouteButton が表示されているかどうかを確認します。

private void showIntroductoryOverlay() {
    if (mIntroductoryOverlay != null) {
        mIntroductoryOverlay.remove();
    }
    if ((mMediaRouteMenuItem != null) && mMediaRouteMenuItem.isVisible()) {
        new Handler().post(new Runnable() {
            @Override
            public void run() {
                mIntroductoryOverlay = new IntroductoryOverlay.Builder(
                        VideoBrowserActivity.this, mMediaRouteMenuItem)
                        .setTitleText(getString(R.string.introducing_cast))
                        .setOverlayColor(R.color.primary)
                        .setSingleTime()
                        .setOnOverlayDismissedListener(
                                new IntroductoryOverlay
                                    .OnOverlayDismissedListener() {
                                        @Override
                                        public void onOverlayDismissed() {
                                            mIntroductoryOverlay = null;
                                        }
                                })
                        .build();
                mIntroductoryOverlay.show();
            }
        });
    }
}

サンプルアプリで、紹介オーバーレイを表示するための完全な動作コードをご確認ください。

紹介オーバーレイのスタイルをカスタマイズするには、紹介オーバーレイをカスタマイズする手順を行ってください。

ミニ コントローラ

CCL の MiniController の代わりに、ミニ コントローラを表示するアクティビティのアプリ レイアウト ファイルで CAF の MiniControllerFragment を使用します。

<fragment
        android:id="@+id/cast_mini_controller"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        app:castShowImageThumbnail="true"
        android:visibility="gone"
        class="com.google.android.gms.cast.framework.media.widget.MiniControllerFragment" />

CAF は、CCL の MiniController でサポートされている手動構成と、Autoplay 機能もサポートしていません。

ミニ コントローラのスタイルとボタンをカスタマイズするには、ミニ コントローラのカスタマイズの手順で操作します。

通知とロック画面

CCL の VideoCastNotificationService と同様に、CAF には、キャスト時のメディア通知の表示を管理するための MediaNotificationService が用意されています。

マニフェストから以下を削除する必要があります。

  • VideoIntentReceiver
  • VideoCastNotificationService

CCL は、CastConfiguration.Builder によるカスタム通知サービスの提供をサポートしています。これは CAF ではサポートされていません。

CCL を使用した次の CastManager 初期化について考えてみます。

VideoCastManager.initialize(
   getApplicationContext(),
   new CastConfiguration.Builder(
           context.getString(R.string.app_id))
       .addNotificationAction(
           CastConfiguration.NOTIFICATION_ACTION_PLAY_PAUSE,true)
       .addNotificationAction(
           CastConfiguration.NOTIFICATION_ACTION_DISCONNECT,true)
       .build());

CAF の同等の構成については、通知とロック画面のメディア コントロールを送信側アプリに組み込むための SDK が SDK に用意されています。通知とロック画面のコントロールは、CastContext を初期化する際に CastOptions で有効にできます。

public CastOptions getCastOptions(Context context) {
    NotificationOptions notificationOptions = 
        new NotificationOptions.Builder()
            .setActions(Arrays.asList(
                MediaIntentReceiver.ACTION_TOGGLE_PLAYBACK,
                MediaIntentReceiver.ACTION_STOP_CASTING), new int[]{0, 1})
            .build();
    CastMediaOptions mediaOptions = new CastMediaOptions.Builder()
             .setNotificationOptions(notificationOptions)
             .build();
    return new CastOptions.Builder()
             .setReceiverApplicationId(context.getString(R.string.app_id))
             .setCastMediaOptions(mediaOptions)
             .build();
}

CAF での通知とロック画面のコントロールは常に有効になっています。また、キャストの再生/一時停止ボタンと停止ボタンはデフォルトで用意されています。CAF は、Gingerbread を除き、メディア通知を表示するタイミングを決定するためのアクティビティの公開設定を自動的に追跡します。(Gingerbread では、registerLifecycleCallbacksBeforeIceCreamSandwich() の使用に関する以前の注をご覧ください。CCL の VideoCastManager incrementUiCounter 呼び出しと decrementUiCounter 呼び出しは削除する必要があります)。

通知に表示されるボタンをカスタマイズするには、通知とロック画面にメディア コントロールを追加するの手順を行います。

拡張コントローラ

CCL には、メディアをキャストする際に拡張コントローラを表示する VideoCastControllerActivityVideoCastControllerFragment が用意されています。

VideoCastControllerActivity 宣言はマニフェストで削除できます。

CAF では、ExpandedControllerActivity を拡張してキャストボタンを追加する必要があります。

拡張コントローラに表示されるスタイルとボタンをカスタマイズするには、拡張コントローラをカスタマイズするの手順を行います。

音声フォーカス

CCL と同様、音声フォーカスは自動的に管理されます。

音量調節

Gingerbread では、CCL と同様、dispatchKeyEvent が必要です。ICS 以降では、CCL と CAF の両方の音量コントロールが自動的に処理されます。

CAF を使用すると、アプリのアクティビティ内でスマートフォンのハード ボリューム ボタンを使用してキャスト ボリュームを制御できます。また、サポートされているバージョンでキャストする際に視覚的な音量バーが表示されます。CAF は、アプリが前面にない場合でも、ロックされていても、画面がオフの場合でも、ハード ボリュームを介して音量の変更を処理します。

字幕

Android KitKat 以降では、[設定] > [ユーザー補助] にある [字幕設定] で字幕をカスタマイズできます。ただし、以前のバージョンの Android にはこの機能はありません。CCL は、以前のバージョンにカスタム設定を行い、KitKat 以降のシステム設定に委任することで、この処理を行います。

CAF には、字幕設定を変更するためのカスタム設定はありません。マニフェストと設定 XML から CaptionsPreferenceActivity 参照を削除する必要があります。

字幕トラックの変更は拡張コントローラ UI で処理されるため、CCL の TracksChooserDialog は不要になりました。

CAF のクローズド キャプション API は v2 に似ています。

デバッグ ロギング

CAF にはデバッグ ロギングの設定が提供されません。

その他

CAF では、次の CCL 機能はサポートされていません。

  • MediaAuthService を提供して再生前に承認を取得する
  • 設定可能な UI メッセージ

サンプルアプリ

Android 用ユニバーサル ミュージック プレーヤー(uamp)サンプルアプリを CCL から CAF に移行するための差分を確認する。

CAF を使用する Codelab チュートリアルサンプルアプリもあります。