テレビ入力サービスはメディア ストリーム ソースを表し、メディア コンテンツをチャンネルおよび番組として地上波 TV の方法で提供できるようにします。テレビ入力サービスを使用すると、保護者による使用制限、番組ガイド情報、コンテンツのレーティングを提供できます。TV 入力サービスは Android システムの TV アプリと連携します。このアプリは、最終的にチャンネル コンテンツをテレビに制御して表示します。システム TV アプリは、デバイス専用に開発されたものであり、サードパーティ アプリでは変更できません。TV 入力フレームワーク(TIF)アーキテクチャとそのコンポーネントについて詳しくは、 TV 入力フレームワークをご覧ください。
TIF Companion Library を使用してテレビ入力サービスを作成する
TIF コンパニオン ライブラリは、一般的なテレビ入力サービス機能の拡張可能な実装を提供するフレームワークです。OEM が Android 5.0(API レベル 21)から Android 7.1(API レベル 25)向けのチャネルを作成するのに使用することを想定しています。
プロジェクトを更新する
OEM は、androidtv-sample-inputs リポジトリで TIF コンパニオン ライブラリをレガシーに利用できます。アプリにライブラリを組み込む方法の例については、こちらのリポジトリをご覧ください。
マニフェストでテレビ入力サービスを宣言する
アプリは、システムがアプリへのアクセスに使用する TvInputService
互換サービスを提供する必要があります。TIF コンパニオン ライブラリには、カスタマイズ可能な TvInputService
のデフォルト実装を提供する BaseTvInputService
クラスが用意されています。BaseTvInputService
のサブクラスを作成し、マニフェストでサブクラスをサービスとして宣言します。
マニフェストの宣言内で BIND_TV_INPUT
権限を指定して、サービスが TV 入力をシステムに接続できるようにします。システム サービスはバインディングを実行し、BIND_TV_INPUT
権限を付与します。システム TV アプリは、TvInputManager
インターフェースを介して TV 入力サービスにリクエストを送信します。
サービス宣言に、インテントで実行するアクションとして TvInputService
を指定するインテント フィルタを含めます。また、サービス メタデータを個別の XML リソースとして宣言します。次の例は、サービス宣言、インテント フィルタ、サービス メタデータの宣言を示しています。
<service android:name=".rich.RichTvInputService" android:label="@string/rich_input_label" android:permission="android.permission.BIND_TV_INPUT"> <!-- Required filter used by the system to launch our account service. --> <intent-filter> <action android:name="android.media.tv.TvInputService" /> </intent-filter> <!-- An XML file which describes this input. This provides pointers to the RichTvInputSetupActivity to the system/TV app. --> <meta-data android:name="android.media.tv.input" android:resource="@xml/richtvinputservice" /> </service>
サービス メタデータを個別の XML ファイルで定義します。サービス メタデータ XML ファイルには、テレビ入力の初期設定とチャンネル スキャンを記述する設定インターフェースが含まれている必要があります。また、メタデータ ファイルには、ユーザーがコンテンツを録画できるかどうかを示すフラグも含める必要があります。アプリでコンテンツの録画をサポートする方法の詳細については、コンテンツの録画をサポートするをご覧ください。
サービス メタデータ ファイルはアプリの XML リソース ディレクトリにあり、マニフェストで宣言したリソースの名前と一致する必要があります。前の例のマニフェスト エントリを使用して、次の内容の XML ファイルを res/xml/richtvinputservice.xml
に作成します。
<?xml version="1.0" encoding="utf-8"?> <tv-input xmlns:android="http://schemas.android.com/apk/res/android" android:canRecord="true" android:setupActivity="com.example.android.sampletvinput.rich.RichTvInputSetupActivity" />
チャンネルを定義してセットアップ アクティビティを作成する
TV 入力サービスは、ユーザーがシステム TV アプリを介してアクセスするチャンネルを少なくとも 1 つ定義する必要があります。そのチャンネルをシステム データベースに登録し、アプリのチャンネルが見つからない場合にシステムが呼び出すセットアップ アクティビティを指定する必要があります。
まず、アプリがシステムの電子番組ガイド(EPG)に対して読み書きできるようにします。EPG のデータには、ユーザーが視聴できるチャンネルと番組が含まれます。アプリでこれらのアクションを実行し、デバイスの再起動後に EPG と同期できるようにするには、次の要素をアプリ マニフェストに追加します。
<uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED "/>
次の要素を追加して、Android TV でコンテンツ チャンネルを提供するアプリとして Google Play ストアにアプリが表示されるようにします。
<uses-feature android:name="android.software.live_tv" android:required="true" />
次に、EpgSyncJobService
クラスを拡張するクラスを作成します。この抽象クラスを使用すると、システム データベース内にチャネルを作成および更新するジョブサービスを簡単に作成できます。
サブクラスで、getChannels()
にチャンネルの全リストを作成して返します。チャンネルが XMLTV ファイルに由来する場合は、XmlTvParser
クラスを使用します。それ以外の場合は、Channel.Builder
クラスを使用してプログラムでチャネルを生成します。
チャンネルごとに、チャンネルで特定の時間枠内に視聴できる番組のリストが必要になると、システムは getProgramsForChannel()
を呼び出します。チャンネルの Program
オブジェクトのリストを返します。XmlTvParser
クラスを使用して XMLTV ファイルから番組を取得するか、Program.Builder
クラスを使用してプログラムで番組を生成します。
Program
オブジェクトごとに、InternalProviderData
オブジェクトを使用して、番組の動画タイプなどの番組情報を設定します。番組の数が限られていて、チャンネルでループを繰り返す場合は、番組に関する情報を設定するときに、値が true
の InternalProviderData.setRepeatable()
メソッドを使用します。
ジョブサービスを実装したら、アプリ マニフェストに追加します。
<service android:name=".sync.SampleJobService" android:permission="android.permission.BIND_JOB_SERVICE" android:exported="true" />
最後に、セットアップ アクティビティを作成します。セットアップ アクティビティでは、チャンネルと番組のデータを同期する方法を指定する必要があります。これを行う方法の 1 つは、ユーザーがアクティビティの UI を介して行うことです。また、アクティビティの開始時にアプリで自動的に行うようにすることもできます。設定アクティビティがチャンネルと番組の情報を同期する必要がある場合、アプリはジョブサービスを開始する必要があります。
Kotlin
val inputId = getActivity().intent.getStringExtra(TvInputInfo.EXTRA_INPUT_ID) EpgSyncJobService.cancelAllSyncRequests(getActivity()) EpgSyncJobService.requestImmediateSync( getActivity(), inputId, ComponentName(getActivity(), SampleJobService::class.java) )
Java
String inputId = getActivity().getIntent().getStringExtra(TvInputInfo.EXTRA_INPUT_ID); EpgSyncJobService.cancelAllSyncRequests(getActivity()); EpgSyncJobService.requestImmediateSync(getActivity(), inputId, new ComponentName(getActivity(), SampleJobService.class));
requestImmediateSync()
メソッドを使用して、ジョブサービスを同期します。ユーザーは同期が完了するまで待つ必要があるため、リクエスト期間は比較的短くしてください。
setUpPeriodicSync()
メソッドを使用して、ジョブサービスがバックグラウンドでチャンネルと番組のデータを定期的に同期するようにします。
Kotlin
EpgSyncJobService.setUpPeriodicSync( context, inputId, ComponentName(context, SampleJobService::class.java) )
Java
EpgSyncJobService.setUpPeriodicSync(context, inputId, new ComponentName(context, SampleJobService.class));
TIF コンパニオン ライブラリには、requestImmediateSync()
のオーバーロード メソッドが追加で用意されています。このメソッドを使用すると、同期するチャンネル データの長さをミリ秒単位で指定できます。デフォルトのメソッドでは、1 時間分のチャンネル データが同期されます。
TIF コンパニオン ライブラリには、setUpPeriodicSync()
のオーバーロード メソッドも追加で用意されています。このメソッドでは、同期するチャンネル データの長さと、定期的な同期の頻度を指定できます。デフォルトの方法では、12 時間ごとに 48 時間分のチャンネル データが同期されます。
チャンネル データと EPG の詳細については、 チャンネル データを操作するをご覧ください。
チューニング リクエストとメディア再生を処理する
ユーザーが特定のチャンネルを選択すると、システム TV アプリはアプリが作成した Session
を使用して、リクエストされたチャンネルにチューニングしてコンテンツを再生します。TIF コンパニオン ライブラリには、システムからのチャネル呼び出しやセッション呼び出しを処理するために拡張できるクラスがいくつか用意されています。
BaseTvInputService
サブクラスにより、選局リクエストを処理するセッションが作成されます。onCreateSession()
メソッドをオーバーライドし、BaseTvInputService.Session
クラスから拡張されたセッションを作成して、新しいセッションで super.sessionCreated()
を呼び出します。次の例では、onCreateSession()
は BaseTvInputService.Session
を拡張する RichTvInputSessionImpl
オブジェクトを返します。
Kotlin
override fun onCreateSession(inputId: String): Session = RichTvInputSessionImpl(this, inputId).apply { setOverlayViewEnabled(true) }
Java
@Override public final Session onCreateSession(String inputId) { RichTvInputSessionImpl session = new RichTvInputSessionImpl(this, inputId); session.setOverlayViewEnabled(true); return session; }
ユーザーがシステム TV アプリを使用していずれかのチャンネルの視聴を開始すると、システムはセッションの onPlayChannel()
メソッドを呼び出します。番組の再生を開始する前に特別なチャンネルの初期化が必要な場合は、このメソッドをオーバーライドします。
次に、システムは現在スケジュールされている番組を取得し、セッションの onPlayProgram()
メソッドを呼び出して、番組情報と開始時間をミリ秒単位で指定します。TvPlayer
インターフェースを使用してプログラムの再生を開始します。
メディア プレーヤーのコードでは、特定の再生イベントを処理するために TvPlayer
を実装する必要があります。TvPlayer
クラスは、BaseTvInputService
の実装を複雑にすることなく、タイムシフト制御などの機能を処理します。
セッションの getTvPlayer()
メソッドで、TvPlayer
を実装するメディア プレーヤーを返します。
TV 入力サービスのサンプルアプリには、ExoPlayer を使用するメディア プレーヤーが実装されています。
TV 入力フレームワークを使用して TV 入力サービスを作成する
テレビ入力サービスが TIF コンパニオン ライブラリを使用できない場合は、次のコンポーネントを実装する必要があります。
TvInputService
: TV 入力に長時間の可用性とバックグラウンド機能を提供します。TvInputService.Session
はテレビの入力状態を維持し、ホスティング アプリと通信します。TvContract
は、TV 入力で利用可能なチャンネルと番組を記述します。TvContract.Channels
: テレビ チャンネルに関する情報を表します。TvContract.Programs
は、番組のタイトルや開始時間などのデータを持つテレビ番組を表します。TvTrackInfo
: 音声、動画、または字幕トラックを表します。TvContentRating
: コンテンツのレーティングを記述します。これにより、カスタムのコンテンツのレーティング スキームを作成できます。TvInputManager
はシステム TV アプリに API を提供し、TV 入力とアプリとのやり取りを管理します。
また、次の処理も行う必要があります。
- マニフェストで TV 入力サービスを宣言するの説明に沿って、マニフェストで TV 入力サービスを宣言します。
- サービス メタデータ ファイルを作成する。
- チャンネルと番組の情報を作成して登録する。
- セットアップ アクティビティを作成する。
テレビ入力サービスを定義する
サービス用に、TvInputService
クラスを拡張します。TvInputService
実装はバインドされたサービスであり、システム サービスがそれにバインドするクライアントです。図 1 に、実装する必要があるサービス ライフサイクル メソッドを示します。
onCreate()
メソッドは HandlerThread
を初期化して開始します。これにより、システム主導のアクションを処理するために UI スレッドとは別のプロセス スレッドが提供されます。次の例では、onCreate()
メソッドは CaptioningManager
を初期化し、ACTION_BLOCKED_RATINGS_CHANGED
アクションと ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED
アクションを処理する準備をします。これらのアクションは、ユーザーが保護者による使用制限の設定を変更したり、ブロックした評価のリストが変更されたりしたときに実行されるシステム インテントを記述します。
Kotlin
override fun onCreate() { super.onCreate() handlerThread = HandlerThread(javaClass.simpleName).apply { start() } dbHandler = Handler(handlerThread.looper) handler = Handler() captioningManager = getSystemService(Context.CAPTIONING_SERVICE) as CaptioningManager setTheme(android.R.style.Theme_Holo_Light_NoActionBar) sessions = mutableListOf<BaseTvInputSessionImpl>() val intentFilter = IntentFilter().apply { addAction(TvInputManager.ACTION_BLOCKED_RATINGS_CHANGED) addAction(TvInputManager.ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED) } registerReceiver(broadcastReceiver, intentFilter) }
Java
@Override public void onCreate() { super.onCreate(); handlerThread = new HandlerThread(getClass() .getSimpleName()); handlerThread.start(); dbHandler = new Handler(handlerThread.getLooper()); handler = new Handler(); captioningManager = (CaptioningManager) getSystemService(Context.CAPTIONING_SERVICE); setTheme(android.R.style.Theme_Holo_Light_NoActionBar); sessions = new ArrayList<BaseTvInputSessionImpl>(); IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(TvInputManager .ACTION_BLOCKED_RATINGS_CHANGED); intentFilter.addAction(TvInputManager .ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED); registerReceiver(broadcastReceiver, intentFilter); }
ブロックされたコンテンツの操作と保護者による使用制限の設定について詳しくは、
コンテンツを管理するをご覧ください。TV 入力サービスで処理できる、その他のシステム主導のアクションについては、TvInputManager
をご覧ください。
TvInputService
は、プレーヤーの状態の変化を処理するために Handler.Callback
を実装する TvInputService.Session
を作成します。onSetSurface()
を使用して、TvInputService.Session
が Surface
を動画コンテンツに設定します。Surface
を使用して動画をレンダリングする方法については、プレーヤーをサーフェスと統合するをご覧ください。
TvInputService.Session
は、ユーザーがチャンネルを選択すると onTune()
イベントを処理し、コンテンツとコンテンツ メタデータの変更をシステム TV アプリに通知します。これらの notify()
メソッドについては、このトレーニングの
コントロール コンテンツとトラック選択を処理するで詳しく説明します。
セットアップ アクティビティを定義する
システム TV アプリは、デベロッパーが TV 入力用に定義したセットアップ アクティビティで動作します。セットアップ アクティビティは必須で、システム データベースに少なくとも 1 つのチャンネル レコードを提供する必要があります。システム TV アプリは、テレビ入力のチャンネルが見つからない場合、セットアップ アクティビティを呼び出します。
セットアップ アクティビティでは、次のレッスンのチャンネル データの作成と更新で説明するように、TV 入力を通じて利用可能になったチャンネルがシステム TV アプリに記述されます。