Android TV のホーム画面には、おすすめのコンテンツをチャンネルと番組の表として表示する UI が用意されています。各行がチャンネルです。チャンネルには、そのチャンネルで利用可能なすべての番組のカードが含まれます。
このドキュメントでは、チャンネルとプログラムをホーム画面に追加する方法、コンテンツを更新する方法、ユーザー アクションを処理する方法、ユーザーに最適なエクスペリエンスを提供する方法について説明します。(API についてさらに詳しく知りたい場合は、ホーム画面の Codelab を試すか、I/O 2017 の Android TV セッションをご覧ください)。
ホーム画面の UI
アプリは、新しいチャンネルの作成、チャンネル内の番組の追加、削除、更新、チャンネル内の番組の順序の制御を行うことができます。たとえば、アプリで「新着情報」というチャンネルを作成し、新しく利用可能になった番組のカードを表示できます。
アプリでは、ホーム画面に表示されるチャンネルの順序を制御できません。アプリが新しいチャンネルを作成すると、ホーム画面のチャンネル リストの一番下にそのチャンネルが追加されます。ユーザーは、チャンネルの並べ替えと、表示非表示の切り替えができます。
Watch Next チャンネル
「次のおすすめ」チャンネルは、ホーム画面のアプリ行の後に表示される 2 番目の行です。このチャンネルはシステムが作成し管理します。アプリは、次のおすすめ チャンネルに番組を追加できます。詳しくは、次のおすすめ チャンネルに番組を追加するをご覧ください。
アプリ チャンネル
アプリが作成するチャンネルはすべて、次のライフサイクルに従います。
- ユーザーがアプリでチャンネルを見つけ、ホーム画面に追加するようリクエストします。
- アプリがチャンネルを作成し、
TvProviderに追加します(この時点ではチャンネルは表示されません)。 - アプリがシステムにチャンネルを表示するよう要求する。
- システムが、新しいチャンネルを承認するようユーザーに要求する。
- ホーム画面の最後の行に新しいチャンネルが表示される。
デフォルト チャンネル
アプリは、ユーザーがホーム画面に追加できるチャンネルをいくつでも提供できます。通常、ユーザーは各チャンネルを選択して承認してから、ホーム画面に表示する必要があります。すべてのアプリには、1 つのデフォルト チャンネルを作成するオプションがあります。デフォルト チャンネルは、ホーム画面に自動的に表示されるため、特別なチャンネルです。ユーザーが明示的にリクエストする必要はありません。
前提条件
Android TV のホーム画面では、Android の TvProvider API を使用して、アプリが作成するチャンネルと番組を管理します。プロバイダのデータにアクセスするには、アプリのマニフェストに次の権限を追加します。
<uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
TvProvider サポート ライブラリの使用により、プロバイダの使用が簡単になります。build.gradle ファイルの依存関係に追加します。
Groovy
implementation 'androidx.tvprovider:tvprovider:1.0.0'
Kotlin
implementation("androidx.tvprovider:tvprovider:1.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 を返します。
チャンネルを作成する手順は次のとおりです。
チャンネル ビルダーを作成し、その属性を設定します。チャンネル タイプは
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);チャンネルをプロバイダに挿入します。
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());後でチャンネルに番組を追加するには、チャンネル ID を保存する必要があります。返された URI からチャンネル ID を抽出します。
Kotlin
var channelId = ContentUris.parseId(channelUri)Java
long channelId = ContentUris.parseId(channelUri);チャンネルのロゴを追加する必要があります。
UriまたはBitmapを使用します。ロゴアイコンは 80 dp × 80 dp で、不透明にする必要があります。円形のマスクの下に表示されます。
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);デフォルト チャンネルを作成する(任意): アプリが最初のチャンネルを作成するときに、それをデフォルト チャンネルに設定すると、ユーザーが操作しなくてもすぐにホーム画面に表示されるようになります。作成した他のチャンネルは、ユーザーが明示的に選択するまで表示されません。
Kotlin
TvContractCompat.requestChannelBrowsable(context, channelId)Java
TvContractCompat.requestChannelBrowsable(context, channelId);アプリを開く前にデフォルト チャンネルを表示します。この動作を実現するには、アプリのインストール後にホーム画面から送信される
android.media.tv.action.INITIALIZE_PROGRAMSアクションをリッスンするBroadcastReceiverを追加します。<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まれに、ユーザーがアプリを起動したときに、アプリがブロードキャストを受信することがあります。コードがデフォルト チャンネルを複数回追加しようとしないようにしてください。
チャンネルを更新する
チャンネルの更新は、チャンネルの作成とよく似ています。
別の 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 チャンネルに追加する
次のおすすめ チャンネルに番組を挿入するには、次のおすすめ チャンネルに番組を追加するをご覧ください。
プログラムを更新する
プログラムの情報は変更できます。たとえば、映画のレンタル料金を更新したり、ユーザーが視聴した番組の割合を示す進行状況バーを更新したりできます。
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
}
チャンネルの承認をユーザーに求めるダイアログが表示されます。アクティビティ(Activity.RESULT_CANCELED または Activity.RESULT_OK)の onActivityResult メソッドで、リクエストの結果を処理します。
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 アプリがユーザーのログインを必要とします。この場合、
android.media.tv.action.INITIALIZE_PROGRAMSをリッスンするBroadcastReceiverは、未認証ユーザーにチャンネル コンテンツを提案する必要があります。たとえば、アプリは最初に、最適なコンテンツや現在人気のコンテンツを表示できます。ユーザーがログインすると、パーソナライズされたコンテンツを表示できます。これは、ログイン前のユーザーにアプリをアップセルする絶好の機会です。 - アプリがフォアグラウンドにないときにチャンネルまたは番組を更新する必要がある場合は、
JobSchedulerを使用して作業をスケジュールします(JobSchedulerとJobServiceを参照)。 - アプリが不正な動作(プロバイダにデータを継続的にスパム送信するなど)を行った場合、システムはアプリのプロバイダ権限を取り消すことができます。プロバイダにアクセスするコードを 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 のホーム画面にチャンネルや番組を追加する方法について詳しくは、ホーム画面のCodelabsをご覧ください。