ライブTVのリソース
以下のベストプラクティスやコードサンプルなどの参考資料は、実装フェーズにおけるライブTV統合の詳細を理解するうえで役に立ちます。
許可リストへのパッケージの追加
許可リストによって、Fire TVの閲覧/検索時にチャンネルを表示できるアプリが決まります。
現在、Fire TVにリニアTVを統合できるのは、一部のパートナーに限られています。近い将来、すべての開発者がこの機能を利用できるようになる予定です。
ベストプラクティス
以下は、Fire TVで快適なライブTVエクスペリエンスを提供するのに役立つ製品および実装のガイドラインです。
- 簡単に登録できるようにして、適宜トライアルを促す。たとえば、アプリの登録フォームを簡素化したり、登録に電話番号を利用したりします。
- チャンネルラインナップの
TvContract.Channels.Logo
には、透明なモノクロロゴを使用する。 - ディープリンクフローを最適化して、全画面再生を2.5秒以内に開始する。
- 複数のチャンネルを操作しているときは常に一括アクションを使用する。
- 必要に応じてGracenoteチャンネルIDを活用し、統合を簡素化する。
- 完全なスケジュールを提供することや推奨される画像サイズを使用することよりも、メタデータ読み込みのパフォーマンスの最適化に重点を置く。
JobScheduler
またはWorkManager
を使用して、視聴権限が適切であることを定期的に確認する。これにより、アプリがフォアグラウンドで実行されていなくても、閲覧/検索されるチャンネルが視聴権限のあるチャンネルと常に同期されるようになります。- カスタムのチャンネル順序が使用されている場合を除き、視聴権限のあるチャンネルのリストが一部だけ変更された場合は、すべてのチャンネルを削除して再度追加するのではなく、必要な部分を更新するようにする。
COLUMN_DISPLAY_NAME
列に表示可能なチャンネル名を指定する(UIでフォールバックとして使用されることがあるため)。Fire TVでは、最大16文字の英数字または8~10文字の全角文字が表示されます。- 変更を行う前に、毎回TIFデータベースに照会して、データベースに既に存在するチャンネルを確認する。
- チャンネルを挿入する前に、そのチャンネルがまだ存在していないことを確認する。チャンネルが既に存在する場合は、メタデータが最新であるかどうかを確認します。データベースの更新操作は、メタデータの更新が必要な場合にのみ行う必要があります。
- データベースカーソルがnullであることを確認する。カーソルがnullである場合は、入力IDを持つすべてのチャンネルに対して削除リクエストを送信してから、チャンネルを再度挿入します。
コードサンプル
このセクションには、ライブTVの統合に関連するコードサンプルが記載されています。
Gracenote IDで特定されたチャンネルに再生ディープリンクを設定する方法
次のコードはTVContractUtils.javaから抜粋したもので、Gracenote IDとディープリンクをTVデータベースに挿入する方法を示しています。
/**
* 外部IDのタイプを格納する変数。サービスメタデータのマッチングに使用されます。有効なタイプは
* 「EXTERNAL_ID_TYPE_」で始まる名前の定数として以下で定義されます
* Nullまたは無効なデータを使用すると、サービスメタデータの
* マッチングに失敗します
*/
private final static String EXTERNAL_ID_TYPE = "externalIdType";
/**
* 外部IDの値を格納する変数。サービスメタデータのマッチングに使用されます。
* Nullまたは無効なデータを使用すると、サービスメタデータのマッチングに失敗します
*/
private final static String EXTERNAL_ID_VALUE = "externalIdValue";
/**
* 外部プレーヤーへの再生のディープリンクURI。
* Nullまたは無効なデータを入力すると、Fire TVのネイティブプレーヤーとの統合がデフォルトとなります
*/
private final static String PLAYBACK_DEEP_LINK_URI = "playbackDeepLinkUri";
// Gracenote入力タイプのID
private final static String GRACENOTE_ID = "gracenote_ontv"; // onTV用Gracenote ID
private final static String GRACENOTE_GVD = "gracenote_gvd"; // GVD用Gracenote ID
// 再生のディープリンクURIのコントラクト
// Intent.URI_INTENT_SCHEMEを使用して、インテントからURIを作成したり、URIを元のインテントに戻したりします
Intent playbackDeepLinkIntent = new Intent(); // アプリで作成されます
String playbackDeepLinkUri = playbackDeepLinkIntent.toUri(Intent.URI_INTENT_SCHEME);
// BLOBを作成します
ContentValues values = new ContentValues(); // すべてのチャンネルデータを格納します
ContentResolver resolver = context.getContentResolver();
values.put(TvContract.Channels.COLUMN_DISPLAY_NAME, "<実際の表示名>");
values.put(TvContract.Channels.COLUMN_INPUT_ID, "<実際の入力ID>");
try {
String jsonString = new JSONObject()
.put(EXTERNAL_ID_TYPE, "<実際のIDタイプ>") // GRACENOTE_XXXに置き換えます
.put(EXTERNAL_ID_VALUE, "<実際のID値>") // チャンネルに関連付けられたGracenote ID値に置き換えます
.put(PLAYBACK_DEEP_LINK_URI, playbackDeepLinkUri).toString();
values.put(TvContract.Channels.COLUMN_INTERNAL_PROVIDER_DATA, jsonString.getBytes());
} catch (JSONException e) {
Log.e(TAG, "BLOBにデータを追加するときにエラーが発生しました " + e);
}
Uri uri = resolver.insert(TvContract.Channels.CONTENT_URI, values);
機能制限の実装方法
次のコードは、機能制限をリッスンし、ライブプレビューまたはネイティブの全画面再生を開始する方法を示しています。
private TvContentRating mBlockedRating = null;
@Override
public boolean onTune(final Uri channelUri) {
...
if (mTvInputManager.isParentalControlsEnabled()) {
// サーフェスで音声や画像が再生されないようにします
mBlockedRating = <content_rating>;
//1.全画面再生のときに、ユーザーにPINの入力を求めるプロンプトを表示します
//2.[放映中のチャンネル] 行を閲覧しているときに
// 番組画像が再生サーフェスで表示されないようにします
notifyContentBlocked(mBlockedRating);
} else {
// 再生が開始されます
notifyContentAllowed();
}
...
}
@Override
public void onUnblockContent(final TvContentRating unblockedRating) {
// ユーザーのPIN入力により、指定のレーティングに該当するコンテンツのブロックが
// 正常に解除されました
if (unblockedRating.unblockContent(mBlockedRating)) {
// 再生が開始されます
notifyContentAllowed();
}
}
アプリバナーの提供
ライブTVの設定でアプリバナーを表示するには、パッケージマネージャーを使用してアプリバナーを提供する必要があります。
// AndroidManifest.xml内
<application
android:allowBackup="false"
android:label="@string/app_name"
android:banner="@drawable/app_icon_banner"
tools:replace="android:allowBackup, allow:label, android:theme" >
<meta-data
android:name="****"
android:value="true"
/>
</application>
バナーをテストするには、次のコードスニペットを参照してください。
Drawable appDrawable = null;
try {
String packageName = "****"; // ****を実際のパッケージ名に置き換えます
PackageManager packageManager = getContext().getPackageManager();
appDrawable = packageManager.getApplicationBanner(packageName);
} catch (PackageManager.NameNotFoundException e) {
Log.i(TAG, "パッケージのアプリバナーが見つかりません:" + packageName);
}
ライブTV対応サンプルアプリ
ライブTVが統合されたサンプルアプリは、GitHub(github.com/amzn/ftv-livetv-sample-tv-app)で入手できます。このライブTV対応サンプルアプリは、GoogleのサンプルTVアプリをベースとしています。Fire TVへのライブTV統合のリファレンスとして、このサンプルアプリを使用できます。
サンプルアプリがサポートされるロケールは、 US、CA、UK、DE、JP、ES、INのみです。その他マーケットプレイスでのサポートは間もなく開始されます。
サンプルアプリを読み込むには、以下の手順を実行します。
-
https://github.com/amzn/ftv-livetv-sample-tv-appにアクセスして、[Clone or download] をクリックし、[Download ZIP] をクリックします。ダウンロードファイルを解凍します。
ライブTVを統合するためのサンプルコードがアプリに表示されます。結果を確認するには、次の手順で説明するように、ADBを使用してapp-debug.apkファイルをFire TVにサイドロードします。
-
既にデバッグが有効でADBがインストールされている場合は、[設定] > [デバイスとソフトウェア](または [My Fire TV])> [バージョン情報] > [ネットワーク] からFire TVのIPアドレスを取得し、以下を実行してFire TVのIPアドレスをカスタマイズします。
adb connect 123.456.7.89:5555
123.456.7.89をFire TVのIPアドレスに置き換えてください(コンピューターはFire TVと同じWi-Fiネットワーク上にある必要があるため、企業のVPNを利用していて接続に問題がある場合は、VPNから切断してみてください)。
-
ビルドされたAPKをサンプルアプリにインストールします。
adb install -r AndroidTvSampleInput/app/build/outputs/apk/app-debug.apk
レスポンスは次のとおりです。
Performing Streamed Install Success
なお、このサンプルアプリは、本来、スタンドアロンのアプリとして起動するものではありません。その代わり、Fire TVデバイスで利用できるライブTVチャンネルのコードが組み込まれています。
-
Fire TVデバイスで、[設定] > [アプリケーション] > [インストール済みアプリケーションを管理] の順に移動します。[Sample TV Inputs] を選択します。[アプリを起動] をクリックします。
Sample TV Inputs Amazon開発者ポータルが表示されます。
Amazon Fire TVサイト -
Fire TVリモコンのホームボタンを押して、この画面から戻ります。次に、[設定] > [ライブTV] > [チャンネル提供元を同期] > [Amazon Sample TV Input] の順にクリックします。
サンプルチャンネルが読み込まれます。
チャンネル提供元の同期 -
同期が完了したら、ホームボタンを押します。これで、チャンネルが [放映中のチャンネル] 行と番組表に表示されるはずです。
[放映中のチャンネル] 行は次のようになります。
Fire TVの [放映中のチャンネル] 行 番組表は次のようになります。
Fire TV番組表 Fire TVの番組表に移動するには、ホーム画面に移動して、[放映中のチャンネル] 行まで下にスクロールし、リモコンのメニューボタンを押してから [番組表] をクリックします。リモコンのマイクボタンを押して「番組表」と言う方法もあります。
カスタマイズされたログのデバッグ
既存の全チャンネルを検索する方法
既存の全チャンネルをAndroidのTVデータベースに照会する方法の例を以下に示します。
private static String[] CHANNEL_TABLE_PROJECTIONS = new String[] {
TvContractCompat.Channels._ID,
TvContractCompat.Channels.COLUMN_DESCRIPTION,
TvContractCompat.Channels.COLUMN_DISPLAY_NAME,
TvContractCompat.Channels.COLUMN_DISPLAY_NUMBER,
TvContractCompat.Channels.COLUMN_INPUT_ID,
TvContractCompat.Channels.COLUMN_INTERNAL_PROVIDER_DATA,
TvContractCompat.Channels.COLUMN_NETWORK_AFFILIATION,
TvContractCompat.Channels.COLUMN_ORIGINAL_NETWORK_ID,
TvContractCompat.Channels.COLUMN_PACKAGE_NAME,
TvContractCompat.Channels.COLUMN_SEARCHABLE,
TvContractCompat.Channels.COLUMN_SERVICE_ID,
TvContractCompat.Channels.COLUMN_SERVICE_TYPE,
TvContractCompat.Channels.COLUMN_TRANSPORT_STREAM_ID,
TvContractCompat.Channels.COLUMN_TYPE,
TvContractCompat.Channels.COLUMN_VIDEO_FORMAT,
TvContractCompat.Channels.COLUMN_BROWSABLE,
TvContractCompat.Channels.COLUMN_LOCKED,
TvContractCompat.Channels.COLUMN_APP_LINK_COLOR,
TvContractCompat.Channels.COLUMN_APP_LINK_ICON_URI,
TvContractCompat.Channels.COLUMN_APP_LINK_INTENT_URI,
TvContractCompat.Channels.COLUMN_APP_LINK_POSTER_ART_URI,
TvContractCompat.Channels.COLUMN_APP_LINK_TEXT,
TvContractCompat.Channels.COLUMN_INTERNAL_PROVIDER_FLAG1,
TvContractCompat.Channels.COLUMN_INTERNAL_PROVIDER_FLAG2,
TvContractCompat.Channels.COLUMN_INTERNAL_PROVIDER_FLAG3,
TvContractCompat.Channels.COLUMN_INTERNAL_PROVIDER_FLAG4
};
public List < Channel > getChannels(ContentResolver resolver) {
Log.d(Utils.DEBUG_TAG, "プレビューチャンネルのクエリチャンネルテーブルのテストを開始します...");
Cursor cursor = null;
List < Channel > channels = new ArrayList < > ();
try {
cursor = resolver.query(TvContractCompat.Channels.CONTENT_URI, CHANNEL_TABLE_PROJECTIONS, null, null, null);
if (cursor == null || cursor.getCount() == 0) {
Log.d(TAG, "チャンネルが挿入されていません \n");
return null;
}
while (cursor.moveToNext()) {
Channel channel = Channel.fromCursor(cursor);
channels.add(channel);
Log.d(TAG, "チャンネルが見つかりました:" + channel);
};
} catch (Exception e) {
Log.d(TAG, "チャンネルを取得できません" + e);
return null;
} finally {
if (cursor != null) {
cursor.close();
}
}
return channels;
}
チャンネル内の全番組を検索する方法
既存の番組を照会する方法を以下に示します。
コードサンプル: 特定のチャンネルに含まれる番組メタデータを照会する方法の例:
public static List < Program > getPrograms(ContentResolver resolver, Uri channelUri) {
if (channelUri == null) {
return null;
}
Uri uri = TvContract.buildProgramsUriForChannel(channelUri);
List < Program > programs = new ArrayList < > ();
// デフォルトでTvProviderから番組が時系列で返されます。
Cursor cursor = null;
try {
cursor = resolver.query(uri, Program.PROJECTION, null, null, null);
if (cursor == null || cursor.getCount() == 0) {
return programs;
}
while (cursor.moveToNext()) {
programs.add(Program.fromCursor(cursor));
}
} catch (Exception e) {
Log.w(TAG, "次の番組を取得できません:" + channelUri, e);
} finally {
if (cursor != null) {
cursor.close();
}
}
return programs;
}