ホーム画面のチャンネル

Android TV ホーム画面(あるいは単にホーム画面)では、おすすめのコンテンツをチャンネルとプログラムの表として表示する UI が提供されます。各行がチャンネルです。チャンネルには、そのチャンネルで利用可能なすべてのプログラムのカードが含まれています。

TV ホーム画面

このドキュメントでは、ユーザーに最高のエクスペリエンスを提供できるように、ホーム画面へのチャンネルとプログラムの追加、コンテンツの更新、ユーザー アクションの処理をする方法を示します。(API の詳細を知りたい方は、ホーム画面のコードラボI/O 2017 Android TV セッションをご覧ください。)

注: おすすめのチャンネルは Android 8.0(API レベル 26)以降でのみ利用可能です。Android 8.0(API レベル 26)以降で動作しているアプリにおすすめを提供する場合は、これを使用する必要があります。これより前のバージョンの Android で動作しているアプリにおすすめを提供する場合は、代わりにおすすめの行を使用する必要があります。

ホーム画面の UI

アプリでは、新しいチャンネルの作成、チャンネル内のプログラムの追加、削除、更新、チャンネル内のプログラムの順序の制御が可能です。たとえば、「新作」というチャンネルを作成して、新たに利用可能になったプログラムのカードを表示できます。

アプリでは、ホーム画面に表示されるチャンネルの順序を制御できません。アプリが新しいチャンネルを作成すると、それをホーム画面がチャンネル リストの下部に追加します。ユーザーは、チャンネルの並べ替えと、表示非表示の切り替えができます。

Watch Next チャンネル

Watch Next チャンネルは、ホーム画面のアプリの次の行(2 行目)です。このチャンネルはシステムが作成し管理します。アプリでは、ユーザーによって興味があるとマークされたプログラム、視聴途中のプログラム、視聴中のプログラムに関連するプログラム(次のエピソードやシーズンなど)などを、Watch Next チャンネルに追加できます。

Watch Next チャンネルにはいくつかの制限があります。アプリは Watch Next チャンネルの行の移動すること、削除すること、非表示にすることができません。

アプリ チャンネル

アプリが作成するチャンネルは、すべて次のライフサイクルに従います。

  1. ユーザーがアプリでチャンネルを発見し、それをホーム画面に追加するよう要求する。
  2. アプリがチャンネルを作成し、TvProvider に追加する(この時点ではチャンネルは表示されない)。
  3. アプリがシステムにチャンネルを表示するよう要求する。
  4. システムが、新しいチャンネルを承認するようユーザーに要求する。
  5. ホーム画面の最後の行に新しいチャンネルが表示される。

デフォルト チャンネル

アプリは、ユーザーがホーム画面に追加するチャンネルをいくつでも提供できます。ユーザーは通常、ホーム画面に表示される前に各チャンネルを選択して承認する必要があります。すべてのアプリはデフォルト チャンネルを 1 つ作成できます。デフォルト チャンネルは特別で、自動的にホーム画面に表示されます。ユーザーが明示的に要求する必要はありません。

要件

Android TV のホーム画面は、Android の TvProvider API を使用して、アプリが作成するチャンネルとプログラムを管理します。プロバイダのデータにアクセスするには、アプリのマニフェストに次の権限を追加します。

<uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
    

TvProvider サポート ライブラリの使用により、プロバイダの使用が簡単になります。次のように、build.gradle ファイルの依存関係に追加してください。

compile 'com.android.support:support-tv-provider:27.0.0'
    

チャンネルとプログラムを操作するには、次に示すサポート ライブラリのインポートをソースコードに含めます。

Kotlin

    import android.support.media.tv.Channel
    import android.support.media.tv.TvContractCompat
    import android.support.media.tv.ChannelLogoUtils
    import android.support.media.tv.PreviewProgram
    import android.support.media.tv.WatchNextProgram
    

Java

    import android.support.media.tv.Channel;
    import android.support.media.tv.TvContractCompat;
    import android.support.media.tv.ChannelLogoUtils;
    import android.support.media.tv.PreviewProgram;
    import android.support.media.tv.WatchNextProgram;
    

チャンネル

アプリが作成した最初のチャンネルが、デフォルト チャンネルになります。デフォルト チャンネルは、ホーム画面に自動的に表示されます。作成した他のすべてのチャンネルは、ユーザーが選択し承認するまでホーム画面に表示されません。

チャンネルを作成する

アプリが新しく追加されたチャンネルの表示をシステムに要求するのは、フォアグラウンドで動作している場合だけにする必要があります。こうすることで、ユーザーが別のアプリを実行しているときに、チャンネルを追加するための承認を要求するダイアログをアプリが表示することがなくなります。バックグラウンドでの動作中にチャンネルを追加しようとすると、アクティビティの onActivityResult() メソッドがステータス コード RESULT_CANCELED を返します。

チャンネルを作成する手順は次のとおりです。

  1. チャンネル ビルダーを作成し、その属性を設定します。チャンネル タイプは TYPE_PREVIEW でなければならないことに注意してください。必要に応じて属性を追加します。

    Kotlin

        val builder = Channel.Builder()
        // Every channel you create must have the type TYPE_PREVIEW
        builder.setType(TvContractCompat.Channels.TYPE_PREVIEW)
                .setDisplayName("Channel Name")
                .setAppLinkIntentUri(uri)
        

    Java

        Channel.Builder builder = new Channel.Builder();
        // Every channel you create must have the type TYPE_PREVIEW
        builder.setType(TvContractCompat.Channels.TYPE_PREVIEW)
                .setDisplayName("Channel Name")
                .setAppLinkIntentUri(uri);
        
  2. チャンネルをプロバイダに挿入します。

    Kotlin

        var channelUri = context.contentResolver.insert(
                TvContractCompat.Channels.CONTENT_URI, builder.build().toContentValues())
        

    Java

        Uri channelUri = context.getContentResolver().insert(
                TvContractCompat.Channels.CONTENT_URI, builder.build().toContentValues());
        
  3. 後でチャンネルにプログラムを追加するために、チャンネル ID を保存する必要があります。返された URI からチャンネル ID を抽出します。

    Kotlin

        var channelId = ContentUris.parseId(channelUri)
        

    Java

        long channelId = ContentUris.parseId(channelUri);
        
  4. チャンネルのロゴを追加する必要があります。Uri または Bitmap を使用します。ロゴアイコンは、縦 80 dp、横 80 dp、不透明でなければなりません。円形にマスクされて表示されます。

    TV ホーム画面アイコンマスク

    Kotlin

        // Choose one or the other
        storeChannelLogo(context: Context, channelId: Long, logoUri: Uri) // also works if logoUri is a URL
        storeChannelLogo(context: Context, channelId: Long, logo: Bitmap)
        

    Java

        // Choose one or the other
        storeChannelLogo(Context context, long channelId, Uri logoUri); // also works if logoUri is a URL
        storeChannelLogo(Context context, long channelId, Bitmap logo);
        
  5. デフォルト チャンネルを追加します(省略可)。アプリが最初のチャンネルを作成するとき、ユーザーの操作なしで即座にホーム画面に表示されるように、デフォルト チャンネルにすることができます。作成した他のチャンネルは、ユーザーが明示的に選択するまで表示されません。

    Kotlin

        TvContractCompat.requestChannelBrowsable(context, channelId)
        

    Java

        TvContractCompat.requestChannelBrowsable(context, channelId);
        

  6. アプリを開く前にデフォルト チャンネルを表示します。この動作を実現するには、BroadcastReceiver を追加します。このレシーバには、android.media.tv.action.INITIALIZE_PROGRAMS アクションをリッスンさせます。このアクションは、アプリがインストールされた後でホーム画面が送信します。
        <receiver
          android:name=".RunOnInstallReceiver"
          android:exported="true">
            <intent-filter>
              <action android:name="android.media.tv.action.INITIALIZE_PROGRAMS" />
              <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </receiver>
        
    開発中にアプリをサイドローディングする場合は、次のように adb からインテントをトリガーすることにより、このステップのテストができます。your.package.name/.YourReceiverName は、アプリの BroadcastReceiver です。

        adb shell am broadcast -a android.media.tv.action.INITIALIZE_PROGRAMS -n \
            your.package.name/.YourReceiverName
        

    まれに、ユーザーがアプリを起動するのと同時に、アプリがブロードキャストを受信する場合があります。コードの中でデフォルト チャンネルの追加を 2 回以上していないか確認してください。

チャンネルを更新する

チャンネルの更新は、チャンネルの作成とよく似ています。

別の Channel.Builder を使用して、変更する必要がある属性を設定します。

ContentResolver を使用して、チャンネルを更新します。最初にチャンネルを追加したときに保存したチャンネル ID を使用します。

Kotlin

    context.contentResolver.update(
            TvContractCompat.buildChannelUri(channelId),
            builder.build().toContentValues(),
            null,
            null
    )
    

Java

    context.getContentResolver().update(TvContractCompat.buildChannelUri(channelId),
        builder.build().toContentValues(), null, null);
    

チャンネルのロゴを更新するには、storeChannelLogo() を使用します。

チャンネルを削除する

Kotlin

    context.contentResolver.delete(TvContractCompat.buildChannelUri(channelId), null, null)
    

Java

    context.getContentResolver().delete(TvContractCompat.buildChannelUri(channelId), null, null);
    

プログラム

アプリ チャンネルにプログラムを追加する

PreviewProgram.Builder を作成し、その属性を設定します。

Kotlin

    val builder = PreviewProgram.Builder()
    builder.setChannelId(channelId)
            .setType(TvContractCompat.PreviewPrograms.TYPE_CLIP)
            .setTitle("Title")
            .setDescription("Program description")
            .setPosterArtUri(uri)
            .setIntentUri(uri)
            .setInternalProviderId(appProgramId)
    

Java

    PreviewProgram.Builder builder = new PreviewProgram.Builder();
    builder.setChannelId(channelId)
            .setType(TvContractCompat.PreviewPrograms.TYPE_CLIP)
            .setTitle("Title")
            .setDescription("Program description")
            .setPosterArtUri(uri)
            .setIntentUri(uri)
            .setInternalProviderId(appProgramId);
    

プログラムのタイプに応じて、さらに属性を追加します(プログラムの各タイプで使用可能な属性を確認するには、後で示すをご覧ください)。

プログラムをプロバイダに挿入します。

Kotlin

    var programUri = context.contentResolver.insert(TvContractCompat.PreviewPrograms.CONTENT_URI,
            builder.build().toContentValues())
    

Java

    Uri programUri = context.getContentResolver().insert(TvContractCompat.PreviewPrograms.CONTENT_URI,
          builder.build().toContentValues());
    

後で参照できるようにプログラム ID を取得します。

Kotlin

    val programId = ContentUris.parseId(programUri)
    

Java

    long programId = ContentUris.parseId(programUri);
    

プログラムを Watch Next チャンネルに追加する

Watch Next チャンネルにプログラムを挿入するのは、独自のチャンネルにプログラムを挿入するのと同じです。

プログラムには 4 つのタイプがあります。適切なタイプを選択してください。

タイプ備考
WATCH_NEXT_TYPE_CONTINUEユーザーがコンテンツの視聴を止めたもの。
WATCH_NEXT_TYPE_NEXTユーザーが視聴中のシリーズのうち、視聴可能な次のプログラム。たとえば、シリーズのエピソード 3 を視聴している場合、その次としてエピソード 4 をおすすめできます。
WATCH_NEXT_TYPE_NEWユーザーが視聴しているコンテンツの続きで、新しく視聴可能になったコンテンツ。たとえば、ユーザーがシリーズのエピソード 5 を視聴していて、エピソード 6 が視聴可能になった場合。
WATCH_NEXT_TYPE_WATCHLISTユーザーがプログラムを保存するときに、システムまたはアプリによって挿入されたもの。

WatchNextProgram.Builder を使用します。

Kotlin

    val builder = WatchNextProgram.Builder()
    builder.setType(TvContractCompat.WatchNextPrograms.TYPE_CLIP)
            .setWatchNextType(TvContractCompat.WatchNextPrograms.WATCH_NEXT_TYPE_CONTINUE)
            .setLastEngagementTimeUtcMillis(time)
            .setTitle("Title")
            .setDescription("Program description")
            .setPosterArtUri(uri)
            .setIntentUri(uri)
            .setInternalProviderId(appProgramId)

    val watchNextProgramUri = context.contentResolver
            .insert(TvContractCompat.WatchNextPrograms.CONTENT_URI,
                    builder.build().toContentValues())
    

Java

    WatchNextProgram.Builder builder = new WatchNextProgram.Builder();
    builder.setType(TvContractCompat.WatchNextPrograms.TYPE_CLIP)
            .setWatchNextType(TvContractCompat.WatchNextPrograms.WATCH_NEXT_TYPE_CONTINUE)
            .setLastEngagementTimeUtcMillis(time)
            .setTitle("Title")
            .setDescription("Program description")
            .setPosterArtUri(uri)
            .setIntentUri(uri)
            .setInternalProviderId(appProgramId);

    Uri watchNextProgramUri = context.getContentResolver()
            .insert(TvContractCompat.WatchNextPrograms.CONTENT_URI, builder.build().toContentValues());
    

TvContractCompat.buildWatchNextProgramUri(long watchNextProgramId) を使用して、Watch Next プログラムの更新に必要な Uri を作成します。

ユーザーがプログラムを Watch Next チャンネルに追加すると、システムはプログラムを行にコピーします。インテント TvContractCompat.ACTION_PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT を送信して、プログラムが追加されたことをアプリに通知します。このインテントには、コピーされたプログラム ID と、Watch Next チャンネル内のプログラムのために作成されたプログラム ID の 2 つの補足情報が含まれます。

プログラムを更新する

プログラムの情報は変更できます。たとえば、映画のレンタル価格を更新したい場合や、ユーザーが視聴したプログラムの量を示す進行状況バーを更新したい場合があります。

PreviewProgram.Builder を使用して、変更する必要のある属性を設定してから、getContentResolver().update を呼び出してプログラムを更新します。プログラムが最初に追加されたときに保存したプログラム ID を指定します。

Kotlin

    context.contentResolver.update(
            TvContractCompat.buildPreviewProgramUri(programId),
                    builder.build().toContentValues(), null, null
    )
    

Java

    context.getContentResolver().update(TvContractCompat.buildPreviewProgramUri(programId),
        builder.build().toContentValues(), null, null);
    

プログラムを削除する

Kotlin

    context.contentResolver
            .delete(TvContractCompat.buildPreviewProgramUri(programId), null, null)
    

Java

    context.getContentResolver().delete(TvContractCompat.buildPreviewProgramUri(programId), null, null);
    

ユーザー操作の処理

アプリにチャンネルの表示や追加を行う UI を用意することで、コンテンツを発見しやすくできます。また、チャンネルがホーム画面に表示された後、アプリでチャンネルに対する操作を処理する必要があります。

チャンネルの発見と追加

アプリでは、ユーザーがチャンネルの選択と追加を行える UI 要素(たとえば、チャンネルの追加を要求するボタン)を提供できます。

ユーザーが特定のチャンネルを要求した後、次のコードを実行して、ホーム画面 UI に追加する許可をユーザーから得ます。

Kotlin

    val intent = Intent(TvContractCompat.ACTION_REQUEST_CHANNEL_BROWSABLE)
    intent.putExtra(TvContractCompat.EXTRA_CHANNEL_ID, channelId)
    try {
      activity.startActivityForResult(intent, 0)
    } catch (e: ActivityNotFoundException) {
      // handle error
    }
    

Java

    Intent intent = new Intent(TvContractCompat.ACTION_REQUEST_CHANNEL_BROWSABLE);
    intent.putExtra(TvContractCompat.EXTRA_CHANNEL_ID, channelId);
    try {
       activity.startActivityForResult(intent, 0);
    } catch (ActivityNotFoundException e) {
      // handle error
    }
    

システムが、ユーザーにチャンネルの承認を求めるダイアログを表示します。次のアクティビティの onActivityResult メソッド内でこの要求の結果を処理します: Activity.RESULT_CANCELED あるいは Activity.RESULT_OK

Android TV ホーム画面イベント

ユーザーがアプリによって公開されたプログラム / チャンネルを操作すると、ホーム画面はアプリにインテントを送信します。

  • ユーザーがチャンネルのロゴを選択すると、ホーム画面はチャンネルの APP_LINK_INTENT_URI 属性に保存されている Uri をアプリに送信します。アプリは、メイン UI または選択したチャンネルに関連するビューを起動します。
  • ユーザーがプログラムを選択すると、ホーム画面はプログラムの INTENT_URI 属性に保存されている Uri をアプリに送信します。アプリは、選択されたコンテンツを再生しなければなりません。
  • ユーザーは、興味がなくなったプログラムをホーム画面の UI から削除することを要求できます。システムが UI からプログラムを削除し、プログラムを所有するアプリにプログラム ID を指定してインテント(android.media.tv.ACTION_PREVIEW_PROGRAM_BROWSABLE_DISABLED あるいは android.media.tv.ACTION_WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED)を送信します。受け取ったアプリは、プロバイダからアプリを削除しなければならず、再度挿入することも行わないでください。

ホーム画面がユーザーの操作に対して送信する Uris すべてにインテント フィルタを作成してください。

<receiver
       android:name=".WatchNextProgramRemoved"
       android:enabled="true"
       android:exported="true">
       <intent-filter>
           <action android:name="android.media.tv.ACTION_WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED" />
       </intent-filter>
    </receiver>
    

おすすめの方法

  • 多くの TV アプリがユーザーのログインを必要とします。この場合、BroadcastReceiver(ただし、android.media.tv.action.INITIALIZE_PROGRAMS をリッスンするもの)が認証されていないユーザーにチャンネル コンテンツをおすすめする必要があります。たとえば、アプリで最初に傑作や人気のコンテンツを表示できます。ユーザーがログインした後では、好みに合ったコンテンツを表示できます。これは、ログイン前にアプリでアップセルを行う絶好の機会です。サンプルアプリ leanback-homescreen-channels は、アプリのインストール後(プリインストールの場合はデバイスのセットアップ後)にチャンネルを読み込む方法を示しています。
  • アプリがフォアグラウンドにないときに、チャンネルあるいはプログラムを更新する必要がある場合、JobScheduler を使用して処理をスケジューリングします(JobSchedulerJobService をご覧ください)。
  • プロバイダにデータを送り続けるなどの不正な動作をアプリが行った場合に、アプリのプロバイダの許可を取り消せます。プロバイダにアクセスするコードは必ず try-catch 句で囲んで、セキュリティ例外を処理するようにしてください。
  • プログラムあるいはチャンネルを更新する前には、更新する必要があるデータについてプロバイダに問い合わせ、データを調整します。たとえば、UI から削除するプログラムは、更新する必要がありません。既存のデータを照会した後で、データの更新、プロバイダへの追加を行うバックグラウンド ジョブを使用し、チャンネルの承認を要求します。このジョブは、アプリの起動時とアプリがデータを更新する必要があるときにいつでも実行できます。

    Kotlin

        context.contentResolver
          .query(
              TvContractCompat.buildChannelUri(channelId),
                  null, null, null, null).use({
                      cursor-> if (cursor != null and cursor.moveToNext()) {
                                   val channel = Channel.fromCursor(cursor)
                                   if (channel.isBrowsable()) {
                                       //update channel's programs
                                   }
                               }
                  })
        

    Java

        try (Cursor cursor = context.getContentResolver()
              .query(
                  TvContractCompat.buildChannelUri(channelId),
                  null,
                  null,
                  null,
                  null)) {
                      if (cursor != null && cursor.moveToNext()) {
                          Channel channel = Channel.fromCursor(cursor);
                          if (channel.isBrowsable()) {
                              //update channel's programs
                          }
                      }
                  }
        
  • すべての画像(ロゴ、アイコン、コンテンツ画像)に一意な URI を使用します。画像を更新するときには、必ず別の URI を使用してください。画像はすべてキャッシュに保存されます。画像を変更したときに URI を変更しないと、古い画像が表示されたままになります。

  • WHERE 句は許可されておらず、WHERE 句を使用してプロバイダを呼び出すとセキュリティ例外がスローされることに注意してください。

属性

このセクションでは、チャンネルとプログラムの属性について個別に説明します。

チャンネルの属性

すべてのチャンネルに次の属性を指定する必要があります。

属性 備考
TYPE TYPE_PREVIEW に設定します。
DISPLAY_NAME チャンネルの名前を設定します。
APP_LINK_INTENT_URI ユーザーがチャンネルのロゴを選択すると、システムはチャンネルに関連するコンテンツを提示するアクティビティを開始するインテントを送信します。この属性に、そのアクティビティのインテント フィルタで使用される URI を設定します。

さらに、チャンネルには、アプリの内部使用のために予約された 6 つのフィールドもあります。これらのフィールドにキーやその他の値を保存して、チャンネルからアプリの内部データ構造への対応付けに使用できます。

  • INTERNAL_PROVIDER_ID
  • INTERNAL_PROVIDER_DATA
  • INTERNAL_PROVIDER_FLAG1
  • INTERNAL_PROVIDER_FLAG2
  • INTERNAL_PROVIDER_FLAG3
  • INTERNAL_PROVIDER_FLAG4

プログラムの属性

プログラムの属性については、タイプごとの個々のページをご覧ください。

サンプルコード

ホーム画面の操作を処理し、Android TV ホーム画面にチャンネルとプログラムを追加するアプリを作成する方法について詳しくは、ホーム画面コードラボgithub のサンプルをご覧ください。