チャンネル データを利用する

TV 入力で、少なくとも次の期間の電子番組ガイド(EPG)データを提供する必要があります 設定アクティビティで 1 つのチャネルを 作成しますまた、Cloud Monitoring で 更新と処理スレッドのサイズを考慮して、 処理しますまた、チャンネルのアプリリンクを提供することもできます。 関連するコンテンツやアクティビティにユーザーを誘導します。 このレッスンでは、 システムのデータベースについてよく理解しておく必要があります。

<ph type="x-smartling-placeholder"></ph>をお試しください TV 入力サービス サンプルアプリ。

権限を付与する

テレビ入力で EPG データを扱うには、テレビ入力で Android マニフェスト ファイルで、次のように書き込み権限を設定します。

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

データベースにチャンネルを登録する

テレビ入力のためのチャンネル データのレコードは、Android TV システム データベースに保持されます。設定 作成するには、チャネルごとに、チャネル データを TvContract.Channels クラス:

TV 入力フレームワークは汎用性が高いため、従来の放送と配信の両方に対応できます。 特に区別なくオーバー ザ トップ(OTT)コンテンツである場合は、 従来の放送チャンネルをより適切に識別できるようにしました。

チャンネルのアプリリンクの詳細を提供する場合は、以下を行う必要があります。 いくつかのフィールドを更新します。アプリリンク フィールドの詳細については、以下をご覧ください。 アプリリンク情報を追加します

インターネット ストリーミング ベースのテレビ入力の場合は、上記に応じて独自の値を割り当てて、 各チャネルは一意に識別できます

バックエンド サーバーからチャンネルのメタデータ(XML、JSON など)を取得し、設定で取得します。 アクティビティは、次のように値をシステム データベースにマッピングします。

Kotlin

val values = ContentValues().apply {
    put(TvContract.Channels.COLUMN_DISPLAY_NUMBER, channel.number)
    put(TvContract.Channels.COLUMN_DISPLAY_NAME, channel.name)
    put(TvContract.Channels.COLUMN_ORIGINAL_NETWORK_ID, channel.originalNetworkId)
    put(TvContract.Channels.COLUMN_TRANSPORT_STREAM_ID, channel.transportStreamId)
    put(TvContract.Channels.COLUMN_SERVICE_ID, channel.serviceId)
    put(TvContract.Channels.COLUMN_VIDEO_FORMAT, channel.videoFormat)
}
val uri = context.contentResolver.insert(TvContract.Channels.CONTENT_URI, values)

Java

ContentValues values = new ContentValues();

values.put(Channels.COLUMN_DISPLAY_NUMBER, channel.number);
values.put(Channels.COLUMN_DISPLAY_NAME, channel.name);
values.put(Channels.COLUMN_ORIGINAL_NETWORK_ID, channel.originalNetworkId);
values.put(Channels.COLUMN_TRANSPORT_STREAM_ID, channel.transportStreamId);
values.put(Channels.COLUMN_SERVICE_ID, channel.serviceId);
values.put(Channels.COLUMN_VIDEO_FORMAT, channel.videoFormat);

Uri uri = context.getContentResolver().insert(TvContract.Channels.CONTENT_URI, values);

上の例では、channel は バックエンドサーバーと通信します

チャンネルと番組の情報を表示する

システム TV アプリは、ユーザーがチャンネルを次々と切り替えたときにチャンネルと番組の情報を表示します。 必要があります。チャンネルと番組の情報がシステムの TV アプリのものと一致することを確認する チャンネルとプログラムの情報のプレゼンターは、以下のガイドラインに従ってください。

  1. チャンネル番号COLUMN_DISPLAY_NUMBER
  2. アイコンandroid:icon、 TV 入力のマニフェスト)
  3. プログラムの説明COLUMN_SHORT_DESCRIPTION
  4. 番組タイトルCOLUMN_TITLE
  5. チャンネルのロゴTvContract.Channels.Logo) <ph type="x-smartling-placeholder">
      </ph>
    • 周囲のテキストに合わせるには #EEEEEE の色を使用します。
    • パディングを含めないでください
  6. ポスターアートCOLUMN_POSTER_ART_URI) <ph type="x-smartling-placeholder">
      </ph>
    • 16:9~4:3 のアスペクト比

図 1. システム テレビ アプリのチャンネルと番組情報のプレゼンター。

システム TV アプリは、ポスターアート、 必要があります。

図 2. システム テレビ アプリの番組ガイド。

チャンネル データを更新する

既存のチャンネル データを更新する場合は、 update() メソッドを使用します。データの現在のバージョンと Channels.COLUMN_VERSION_NUMBER を使用 および Programs.COLUMN_VERSION_NUMBER 選択します

注: チャンネル データを ContentProvider に追加する 時間がかかります。現在の番組(現在の時刻から 2 時間以内の番組)を追加する 残りの要素を更新するように EpgSyncJobService を構成した場合のみ バックグラウンドで自動的に処理されます。詳しくは、 <ph type="x-smartling-placeholder"></ph> Android TV Live TV サンプルアプリをご覧ください。

チャンネル データを一括で読み込む

大量のチャンネル データでシステム データベースを更新する場合は、ContentResolver を使用します。 applyBatch() または bulkInsert() メソッドを呼び出します。以下に applyBatch() を使用した例を示します。

Kotlin

val ops = ArrayList<ContentProviderOperation>()
val programsCount = channelInfo.mPrograms.size
channelInfo.mPrograms.forEachIndexed { index, program ->
    ops += ContentProviderOperation.newInsert(
            TvContract.Programs.CONTENT_URI).run {
        withValues(programs[index])
        withValue(TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS, programStartSec * 1000)
        withValue(
                TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS,
                (programStartSec + program.durationSec) * 1000
        )
        build()
    }
    programStartSec += program.durationSec
    if (index % 100 == 99 || index == programsCount - 1) {
        try {
            contentResolver.applyBatch(TvContract.AUTHORITY, ops)
        } catch (e: RemoteException) {
            Log.e(TAG, "Failed to insert programs.", e)
            return
        } catch (e: OperationApplicationException) {
            Log.e(TAG, "Failed to insert programs.", e)
            return
        }
        ops.clear()
    }
}

Java

ArrayList<ContentProviderOperation> ops = new ArrayList<>();
int programsCount = channelInfo.mPrograms.size();
for (int j = 0; j < programsCount; ++j) {
    ProgramInfo program = channelInfo.mPrograms.get(j);
    ops.add(ContentProviderOperation.newInsert(
            TvContract.Programs.CONTENT_URI)
            .withValues(programs.get(j))
            .withValue(Programs.COLUMN_START_TIME_UTC_MILLIS,
                    programStartSec * 1000)
            .withValue(Programs.COLUMN_END_TIME_UTC_MILLIS,
                    (programStartSec + program.durationSec) * 1000)
            .build());
    programStartSec = programStartSec + program.durationSec;
    if (j % 100 == 99 || j == programsCount - 1) {
        try {
            getContentResolver().applyBatch(TvContract.AUTHORITY, ops);
        } catch (RemoteException | OperationApplicationException e) {
            Log.e(TAG, "Failed to insert programs.", e);
            return;
        }
        ops.clear();
    }
}

チャンネル データを非同期で処理する

サーバーからのストリームのフェッチやデータベースへのアクセスなどのデータ操作は、 UI スレッドをブロックしませんAsyncTask を使用するのは、 更新を非同期で実行することもできますたとえば、バックエンド サーバーからチャンネル情報を読み込む場合、 次のように AsyncTask を使用できます。

Kotlin

private class LoadTvInputTask(val context: Context) : AsyncTask<Uri, Unit, Unit>() {

    override fun doInBackground(vararg uris: Uri) {
        try {
            fetchUri(uris[0])
        } catch (e: IOException) {
            Log.d("LoadTvInputTask", "fetchUri error")
        }
    }

    @Throws(IOException::class)
    private fun fetchUri(videoUri: Uri) {
        context.contentResolver.openInputStream(videoUri).use { inputStream ->
            Xml.newPullParser().also { parser ->
                try {
                    parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false)
                    parser.setInput(inputStream, null)
                    sTvInput = ChannelXMLParser.parseTvInput(parser)
                    sSampleChannels = ChannelXMLParser.parseChannelXML(parser)
                } catch (e: XmlPullParserException) {
                    e.printStackTrace()
                }
            }
        }
    }
}

Java

private static class LoadTvInputTask extends AsyncTask<Uri, Void, Void> {

    private Context mContext;

    public LoadTvInputTask(Context context) {
        mContext = context;
    }

    @Override
    protected Void doInBackground(Uri... uris) {
        try {
            fetchUri(uris[0]);
        } catch (IOException e) {
          Log.d("LoadTvInputTask", "fetchUri error");
        }
        return null;
    }

    private void fetchUri(Uri videoUri) throws IOException {
        InputStream inputStream = null;
        try {
            inputStream = mContext.getContentResolver().openInputStream(videoUri);
            XmlPullParser parser = Xml.newPullParser();
            try {
                parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
                parser.setInput(inputStream, null);
                sTvInput = ChannelXMLParser.parseTvInput(parser);
                sSampleChannels = ChannelXMLParser.parseChannelXML(parser);
            } catch (XmlPullParserException e) {
                e.printStackTrace();
            }
        } finally {
            if (inputStream != null) {
                inputStream.close();
            }
        }
    }
}

EPG データを定期的に更新する必要がある場合は、 WorkManager たとえば、毎日午前 3 時など、アイドル時間にアップデート プロセスを実行します。

データ更新タスクを UI スレッドから分離するその他の手法として、 HandlerThread クラスを使用するか、Looper を使用して独自のものを実装できます。 および Handler クラス。<ph type="x-smartling-placeholder"></ph>をご覧ください。 プロセスとスレッドをご覧ください。

チャンネルでアプリリンクを使用すると、関連するアプリをユーザーが簡単に起動できるようになります。 行動を起こすことができます。チャネルアプリの使用状況 以下を表示するアクティビティを起動して、ユーザー エンゲージメントを拡大します。 関連する情報や追加のコンテンツですたとえばアプリリンクを使用して 次の操作を行います。

  • ユーザーが関連コンテンツを見つけて購入できる場所を案内する
  • 現在再生中のコンテンツに関する追加情報を提供する
  • エピソード形式のコンテンツの視聴中に、 シリーズとして提供されます
  • ユーザーがコンテンツを操作できるようにする(評価、レビューなど) ストリーミングできます。

アプリリンクは、ユーザーが [選択] を押して チャンネル コンテンツの視聴中にテレビのメニューを表示。

図 1. アプリリンクの例 チャンネルのコンテンツが表示されている間に [チャンネル] 行に表示されます。

ユーザーがアプリリンクを選択すると、システムは チャネルアプリで指定されたインテント URI。チャンネルのコンテンツが引き続き再生される アプリリンク アクティビティがアクティブな間。ユーザーはチャンネルに戻ることができます [戻る] を押します。

アプリリンクにチャンネル データを提供する

Android TV はチャンネルごとに自動的にアプリリンクを作成します。 チャンネルデータの情報を使用しますアプリリンク情報を提供するには その際、 TvContract.Channels フィールド:

  • COLUMN_APP_LINK_COLOR - このチャンネルのアプリリンクのアクセント カラー。たとえば アクセントカラーは 図 2 の吹き出し 3 を参照してください。
  • COLUMN_APP_LINK_ICON_URI~ このチャンネルのアプリリンクのアプリバッジ アイコンの URI。1 つの アプリバッジ アイコンの例(図 2 の吹き出し 2 を参照)
  • COLUMN_APP_LINK_INTENT_URI~ このチャンネルのアプリリンクのインテント URI。URI は toUri(int)URI_INTENT_SCHEME と 元のインテントに URI を戻します。 parseUri()
  • COLUMN_APP_LINK_POSTER_ART_URI - アプリリンクの背景として使用されるポスターアートの URI このチャンネルのポスター画像の例は、図 2 の吹き出し 1 をご覧ください。
  • COLUMN_APP_LINK_TEXT~ このチャンネルのアプリリンクの説明リンクテキスト。例 アプリリンクの説明(図 2 の吹き出し 3 のテキストを参照)。

図 2. アプリリンクの詳細項目

チャンネル データにアプリリンク情報が指定されていない場合は、 デフォルトのアプリリンクを作成します。詳細項目はデフォルトでは以下のように選択されます。

  • インテント URI の場合 (COLUMN_APP_LINK_INTENT_URI), システムは ACTION_MAIN を使用する CATEGORY_LEANBACK_LAUNCHER カテゴリのアクティビティ。通常はアプリ マニフェストで定義されます。 このアクティビティが定義されていない場合、 ユーザーがクリックしても何も起こりません
  • 説明文 (COLUMN_APP_LINK_TEXT)、システム 「Open app-name」を使用します。アプリリンクのインテント URI が定義されていない場合、 「No link available」と表示されます。
  • アクセント カラー用 (COLUMN_APP_LINK_COLOR), デフォルトのアプリ色が使用されます。
  • ポスター画像の場合 (COLUMN_APP_LINK_POSTER_ART_URI), システムはアプリのホーム画面のバナーを使用します。アプリが バナーに表示される場合、システムはデフォルトのテレビアプリの画像を使用します。
  • バッジアイコンの場合 (COLUMN_APP_LINK_ICON_URI)、 アプリ名を示すバッジが使用されます。システムが ポスター画像にアプリバナーまたはデフォルトのアプリ画像が表示され、アプリバッジは表示されません。

チャンネルのアプリリンクの詳細は、 設定します。アプリリンクの詳細は随時更新できるため、 チャンネルの変更に合わせてアプリリンクを変更する場合は、アプリを更新する リンクの詳細と呼び出し 必要に応じて ContentResolver.update()。更新について詳しくは、 チャンネル データについて詳しくは、チャンネル データを更新するをご覧ください。