Engage SDK for TV の統合ガイド

[続きを見る] では、連続性クラスタを利用して、複数のアプリの未視聴の動画と、同じテレビシーズンの次回のエピソードを 1 つの UI グループに表示します。この連続性クラスタでエンティティを掲載できます。このガイドでは、Engage SDK を使用して「続きを見る」エクスペリエンスを通じてユーザー エンゲージメントを高める方法について説明します。

ステップ 1: 準備作業

始める前に、次の手順を完了します。

この統合では、アプリが API レベル 19 以降をターゲットにしていることを確認してください。

  1. アプリに com.google.android.engage ライブラリを追加します。

    統合に使用する SDK は、モバイルアプリ用とテレビアプリ用の 2 つあります。

    モバイル

    
      dependencies {
        implementation 'com.google.android.engage:engage-core:1.5.5
      }
    

    テレビ

    
      dependencies {
        implementation 'com.google.android.engage:engage-tv:1.0.2
      }
    
  2. AndroidManifest.xml ファイルで Engage サービス環境を本番環境に設定します。

    モバイル

    
    <meta-data
          android:name="com.google.android.engage.service.ENV"
          android:value="PRODUCTION">
    </meta-data>
    

    テレビ

    
    <meta-data
        android:name="com.google.android.engage.service.ENV"
        android:value="PRODUCTION">
    </meta-data>
    
  3. tv apk の WRITE_EPG_DATA の権限を追加

      <uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
    
  4. androidx.work などのバックグラウンド サービスを使用してスケジュール設定することで、コンテンツの信頼性の高いパブリッシュを確保します。

  5. シームレスな視聴体験を提供するには、次のイベントが発生したときに連続視聴データを公開します。

    1. 初回ログイン: ユーザーが初めてログインしたときにデータを公開すると、視聴履歴をすぐに利用できるようになります。
    2. プロファイルの作成または切り替え(マルチプロファイル アプリ): アプリが複数のプロファイルをサポートしている場合は、ユーザーがプロファイルを作成または切り替えたときにデータを公開します。これにより、各ユーザーにパーソナライズされたエクスペリエンスを提供できます。
    3. 動画再生の中断: ユーザーが中断したところから再生を再開できるように、動画を一時停止または停止したとき、または再生中にアプリを終了したときにデータを公開します。
    4. [続きを見る] トレイの更新(サポートされている場合): ユーザーが [続きを見る] トレイからアイテムを削除した場合は、更新されたデータを公開してその変更を反映します。これにより、トレイの関連性とパーソナライズが維持されます。
    5. 動画の視聴完了:
      1. 映画の場合は、視聴を完了した映画を [続きを見る] トレイから削除します。映画がシリーズの一部である場合は、次の映画を追加してユーザーを引き付けます。
      2. エピソードの場合は、視聴が完了したエピソードを削除し、シリーズの次のエピソード(利用可能な場合)を追加して、視聴を継続できるようにします。

統合

AccountProfile

Google TV でパーソナライズされた「続きを見る」機能を利用できるようにするには、アカウントとプロフィールの情報を指定します。AccountProfile を使用して、以下を提供します。

  1. アカウント ID: アプリケーション内のユーザーのアカウントを表す一意の識別子。これは実際のアカウント ID でも、適切に難読化されたバージョンでもかまいません。

  2. プロファイル ID(省略可): アプリが 1 つのアカウント内の複数のプロファイルをサポートしている場合は、特定のユーザー プロファイルの一意の識別子(実際のものまたは難読化されたもの)を指定します。

// If your app only supports account
val accountProfile = AccountProfile.Builder()
      .setAccountId("your_users_account_id")
      .build()
// If your app supports both account and profile
val accountProfile = AccountProfile.Builder()
      .setAccountId("your_users_account_id")
      .setProfileId("your_users_profile_id")
.build()

エンティティを作成する

この SDK では、各アイテムタイプを表すさまざまなエンティティを定義しています。継続性クラスタは、次のエンティティをサポートしています。

  1. MovieEntity
  2. TvEpisodeEntity
  3. LiveStreamingVideoEntity
  4. VideoClipEntity

これらのエンティティのプラットフォーム固有の URI とポスター画像を指定します。

また、まだ作成していない場合は、Android TV、Android、iOS などの各プラットフォームの再生 URI を作成します。そのため、ユーザーが各プラットフォームで視聴を続けると、アプリはターゲット設定された再生 URI を使用して動画コンテンツを再生します。

// Required. Set this when you want continue watching entities to show up on
// Google TV
val playbackUriTv =
          PlatformSpecificUri.Builder()
              .setPlatformType(PlatformType.TYPE_ANDROID_TV)
              .setActionUri(Uri.parse("https://www.example.com/entity_uri_for_tv"))
              .build()

// Required. Set this when you want continue watching entities to show up on
// Google TV Android app, Entertainment Space, Playstore Widget
val playbackUriAndroid =
          PlatformSpecificUri.Builder()
              .setPlatformType(PlatformType.TYPE_ANDROID_MOBILE)
              .setActionUri(Uri.parse("https://www.example.com/entity_uri_for_android"))
              .build()
// Optional. Set this when you want continue watching entities to show up on
// Google TV iOS app
val playbackUriIos =
          PlatformSpecificUri.Builder()
              .setPlatformType(PlatformType.TYPE_IOS)
              .setActionUri(Uri.parse("https://www.example.com/entity_uri_for_ios"))
              .build()
 val platformSpecificPlaybackUris =
          Arrays.asList(playbackUriTv, playbackUriAndroid, playbackUriIos)

ポスター画像には URI とピクセル単位のサイズ(高さと幅)が必要です。複数のポスター画像を指定して、さまざまなフォーム ファクタをターゲットに設定します。ただし、すべての画像でアスペクト比が 16:9 に保たれ、高さが 200 ピクセル以上であることを確認してください。特に Google の エンターテイメント スペースでは、「続きを見る」エンティティが正しく表示されるようにする必要があります。高さが 200 ピクセル未満の画像は表示されない場合があります。


Image image1 = new Image.Builder()
            .setImageUri(Uri.parse("http://www.example.com/entity_image1.png");)
            .setImageHeightInPixel(300)
            .setImageWidthInPixel(169)
            .build()
Image image2 = new Image.Builder()
            .setImageUri(Uri.parse("http://www.example.com/entity_image2.png");)
            .setImageHeightInPixel(640)
            .setImageWidthInPixel(360)
            .build()
// And other images for different form factors.
val images = Arrays.asList(image1, image2)
MovieEntity

次の例は、必要なすべてのフィールドを含む MovieEntity を作成する方法を示しています。

val movieEntity = MovieEntity.Builder()
   .setWatchNextType(WatchNextType.TYPE_CONTINUE)
   .setName("Movie name")
   .addPlatformSpecificPlaybackUri(platformSpecificPlaybackUris)
   .addPosterImages(images)
   // Timestamp in millis for sample last engagement time 12/1/2023 00:00:00
   .setLastEngagementTimeMillis(1701388800000)
   // Suppose the duration is 2 hours, it is 72000000 in milliseconds
   .setDurationMills(72000000)
   // Suppose last playback offset is 1 hour, 36000000 in milliseconds
   .setLastPlayBackPositionTimeMillis(36000000)
   .build()

ジャンルやコンテンツの評価などの詳細情報を提供することで、Google TV でコンテンツをよりダイナミックに紹介し、適切な視聴者にコンテンツを届けることができます。

val genres = Arrays.asList("Action", "Science fiction");
val rating1 = RatingSystem.Builder().setAgencyName("MPAA").setRating("PG-13").build();
val contentRatings = Arrays.asList(rating1);
val movieEntity = MovieEntity.Builder()
    ...
    .addGenres(genres)
    .addContentRatings(contentRatings)
    .build()

有効期限を短く指定しない限り、エンティティは 60 日間自動的に使用可能になります。カスタムの有効期限は、このデフォルトの期間より前にエンティティを削除する必要がある場合にのみ設定してください。

// Set the expiration time to be now plus 30 days in milliseconds
val expirationTime = new DisplayTimeWindow.Builder()
             .setEndTimestampMillis(now().toMillis()+2592000000).build()
val movieEntity = MovieEntity.Builder()
    ...
    .addAvailabilityTimeWindow(expirationTime)
    .build()
TvEpisodeEntity

この例は、必要なすべてのフィールドを使用して TvEpisodeEntity を作成する方法を示しています。

val tvEpisodeEntity = TvEpisodeEntity.Builder()
    .setWatchNextType(WatchNextType.TYPE_CONTINUE)
    .setName("Episode name")
    .addPlatformSpecificPlaybackUri(platformSpecificPlaybackUris)
    .addPosterImages(images)
    // Timestamp in millis for sample last engagement time 12/1/2023 00:00:00
    .setLastEngagementTimeMillis(1701388800000)
    .setDurationMills(72000000) // 2 hours in milliseconds
    // 45 minutes and 15 seconds in milliseconds is 2715000
    .setLastPlayBackPositionTimeMillis(2715000)
    .setEpisodeNumber("2")
    .setSeasonNumber("1")
    .setShowTitle("Title of the show")
    .build();

エピソード番号の文字列("2" など)とシーズン番号の文字列("1" など)は、連続視聴カードに表示される前に適切な形式に展開されます。数値文字列にする必要があります。たとえば、「e2」、「episode 2」、「s1」、「season 1」は使用できません。

特定のテレビ番組にシーズンが 1 つしかない場合は、シーズン番号を 1 に設定します。

視聴者が Google TV でコンテンツを見つける可能性を最大限に高めるには、ジャンル、コンテンツの評価、公開期間などの追加データを提供することを検討してください。これらの詳細情報は、表示とフィルタリング オプションを強化できます。

val genres = Arrays.asList("Action", "Science fiction")
val rating1 = RatingSystem.Builder().setAgencyName("MPAA").setRating("PG-13").build()
val contentRatings = Arrays.asList(rating1)
val tvEpisodeEntity = TvEpisodeEntity.Builder()
    ...
    .addGenres(genres)
    .addContentRatings(contentRatings)
    .setSeasonTitle("Season Title")
    .setShowTitle("Show Title)
    .build();
VideoClipEntity

必須フィールドをすべて含む VideoClipEntity を作成する例を次に示します。

VideoClipEntity は、YouTube 動画などのユーザー作成クリップを表します。

val videoClipEntity = VideoClipEntity.Builder()
    .setPlaybackUri(Uri.parse("https://www.example.com/uri_for_current_platform")
    .setWatchNextType(WatchNextType.TYPE_CONTINUE)
    .setName("Video clip name")
    .addPlatformSpecificPlaybackUri(platformSpecificPlaybackUris)
    .addPosterImages(images)
    // Timestamp in millis for sample last engagement time 12/1/2023 00:00:00
    .setLastEngagementTimeMillis(1701388800000)
    .setDurationMills(600000) //10 minutes in milliseconds
    .setLastPlayBackPositionTimeMillis(300000) //5 minutes in milliseconds
    .addContentRating(contentRating)
    .build();

必要に応じて、作成者、作成者画像、作成日時(ミリ秒単位)、利用可能時間帯を設定できます。

LiveStreamingVideoEntity

必須フィールドをすべて含む LiveStreamingVideoEntity を作成する例を次に示します。

val liveStreamingVideoEntity = LiveStreamingVideoEntity.Builder()
    .setPlaybackUri(Uri.parse("https://www.example.com/uri_for_current_platform")
    .setWatchNextType(WatchNextType.TYPE_CONTINUE)
    .setName("Live streaming name")
    .addPlatformSpecificPlaybackUri(platformSpecificPlaybackUris)
    .addPosterImages(images)
    // Timestamp in millis for sample last engagement time 12/1/2023 00:00:00
    .setLastEngagementTimeMillis(1701388800000)
    .setDurationMills(72000000) //2 hours in milliseconds
    .setLastPlayBackPositionTimeMillis(36000000) //1 hour in milliseconds
    .addContentRating(contentRating)
    .build();

必要に応じて、ライブ配信エンティティの開始時間、ブロードキャスター、ブロードキャスター アイコン、配信時間帯を設定できます。

属性と要件の詳細については、API リファレンスをご覧ください。

連続性クラスタデータを提供する

AppEngagePublishClient は、連続性クラスタの公開を行います。publishContinuationCluster() メソッドを使用して ContinuationCluster オブジェクトを公開します。

まず、isServiceAvailable() を使用して、サービスを統合に使用できるかどうかを確認する必要があります。

client.publishContinuationCluster(
    PublishContinuationClusterRequest
        .Builder()
        .setContinuationCluster(
            ContinuationCluster
                .Builder()
                .setAccountProfile(accountProfile)
                .addEntity(movieEntity1)
                .addEntity(movieEntity2)
                .addEntity(tvEpisodeEntity1)
                .addEntity(tvEpisodeEntity2)
                .setSyncAcrossDevices(true)
                .build()
        )
        .build();
)

サービスがリクエストを受信すると、1 つのトランザクション内で次のアクションが行われます。

  • デベロッパー パートナーが提供した既存の ContinuationCluster データが削除されます。
  • リクエストのデータが解析されて、更新された ContinuationCluster に保存されます。

エラーが発生した場合は、リクエスト全体が拒否され、それまでの状態が維持されます。

公開 API は既存のコンテンツを置き換える upsert API です。ContinuationCluster 内の特定のエンティティを更新する必要がある場合は、すべてのエンティティを再度公開する必要があります。

ContinuationCluster データは、成人アカウントにのみ提供する必要があります。AccountProfile が成人に属している場合にのみ公開します。

クロスデバイスの同期

SyncAcrossDevices フラグ

このフラグは、ユーザーの ContinuationCluster データがデバイス(テレビ、スマートフォン、タブレットなど)間で同期されるかどうかを制御します。デフォルトは false です。つまり、クロスデバイス同期はデフォルトで無効になっています。

値:

  • true: ContinuationCluster データは、シームレスな視聴体験のためにユーザーのすべてのデバイス間で共有されます。クロスデバイス エクスペリエンスを最大限に活用するには、このオプションを強くおすすめします。
  • false: ContinuationCluster データは現在のデバイスに限定されます。

メディア アプリには、クロスデバイス同期を有効または無効にするための明確な設定が必要です。ユーザーにメリットを説明し、ユーザーの設定を 1 回保存して、それに応じて publishContinuationCluster に適用します。

// Example to allow cross device syncing.
client.publishContinuationCluster(
    PublishContinuationClusterRequest
        .Builder()
        .setContinuationCluster(
            ContinuationCluster
                .Builder()
                .setAccountProfile(accountProfile)
                .setSyncAcrossDevices(true)
                .build();
        )
        .build();
)

クロスデバイス機能を最大限に活用するには、アプリでユーザーの同意を得て、SyncAcrossDevices を true に設定してください。これにより、コンテンツをデバイス間でシームレスに同期できるため、ユーザー エクスペリエンスが向上し、エンゲージメントが向上します。たとえば、この機能を実装したパートナーは、コンテンツが複数のデバイスに表示されたため、[続きを見る] のクリック数が 40% 増加しました。

動画検出データを削除する

標準の 60 日間の保持期間の前に Google TV サーバーからユーザーのデータを手動で削除するには、client.deleteClusters() メソッドを使用します。リクエストを受信すると、アカウント プロフィールまたはアカウント全体の既存の動画検出データがすべて削除されます。

DeleteReason 列挙型は、データ削除の理由を定義します。次のコードは、ログアウト時に連続視聴データを削除します。


// If the user logs out from your media app, you must make the following call
// to remove continue watching data from the current google TV device,
// otherwise, the continue watching data will persist on the current
// google TV device until 60 days later.
client.deleteClusters(
  new DeleteClustersRequest.Builder()
        .setAccountProfile(AccountProfile())
        .setReason(DeleteReason.DELETE_REASON_USER_LOG_OUT)
        .setSyncAcrossDevices(true)
        .build()
)

テスト

検証用アプリを使用して、Engage SDK の統合が正しく機能していることを確認します。この Android アプリには、データを検証し、ブロードキャスト インテントの適切な処理を確認するためのツールが用意されています。

publish API を呼び出した後、検証アプリをチェックして、データが正しく公開されていることを確認します。連続性クラスタは、アプリのインターフェース内に個別の行として表示されます。

  • アプリの Android マニフェスト ファイルで、Engage Service Flag が本番環境に設定されていないことを確認します。
  • Engage Verify アプリをインストールして開く
  • isServiceAvailablefalse の場合は、[切り替え] ボタンをクリックして有効にします。
  • アプリのパッケージ名を入力すると、公開を開始すると公開データが自動的に表示されます。
  • アプリで次のアクションをテストします。
    • ログインします。
    • プロファイルを切り替える(該当する場合)。
    • 動画の再生を開始、一時停止、またはホーム画面に戻ります。
    • 動画の再生中にアプリを閉じる。
    • [続きを見る] 行からアイテムを削除します(サポートされている場合)。
  • 各アクションの後、アプリが publishContinuationClusters API を呼び出し、データが検証アプリに正しく表示されることを確認します。
  • 正しく実装されたエンティティには、検証アプリに緑色の「All Good」チェックが表示されます。

    検証アプリの成功スクリーンショット
    図 1. 検証用アプリの成功
  • 問題のあるエンティティは検証アプリによって報告されます。

    検証アプリのエラーのスクリーンショット
    図 2. 確認アプリのエラー
  • エラーのあるエンティティのトラブルシューティングを行うには、テレビのリモコンを使用して、検証アプリでエンティティを選択してクリックします。特定の問題が表示され、確認のために赤色でハイライト表示されます(以下の例を参照)。

    確認アプリのエラーの詳細
    図 3. 確認アプリのエラーの詳細