开发者控制台

步骤3: 插入第一个频道


步骤3: 插入第一个频道

现在介绍如何插入第一个频道。除了这张图表之外,还可以查看Android开发基础知识中的TIF架构图。

频道插入流程

电视输入会在电视输入框架(TIF)数据库中插入频道和节目元数据。此数据将用于在Fire TV直播部分中显示服务的直播内容。电视输入频道和节目元数据必须为最新状态,且与应用内部数据相匹配。步骤3和4将演示如何插入此数据并使其保持最新状态。

向清单中添加权限

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

必须先在AndroidManifest.xml中添加这些权限,然后您的应用才能与TIF数据库交互。

在Android电视数据库中插入频道元数据

以下示例展示了如何在Android电视数据库中插入基本频道。在SetupActivity类中添加以下代码: 在Android电视数据库中插入基本频道的方法有两种:可以在一个类或对象中插入频道:

SetupActivity类
import android.content.ContentValues;
import android.media.tv.TvContract;
import android.util.Log;
import android.net.Uri;

private long insertChannel() {
    ContentValues values = new contentValues();
    values.put(TvContract.Channels.COLUMN_INPUT_ID, "com.example.android.sampletvinput/.RichTvInputService");
    values.put(TvContract.Channels.COLUMN_DISPLAY_NAME, "My Test Channel");
    values.put(TvContract.Channels.COLUMN_DISPLAY_NUMBER, "3");

    Uri uri = getApplicationContext().getContentResolver().insert(TvContract.Channels.CONTENT_URI, values);
    Log.i("SetupActivity", "Channel Inserted! Uri: " + uri);
    long channelId = Long.parseLong(uri.getLastPathSegment());

    return channelId;
}
AndroidX库提供的频道对象
import androidx.tvprovider.media.tv.Channel;
import android.media.tv.TvContract;
import android.util.Log;
import android.net.Uri;

private long insertChannel() {
    Channel testChannel = new Channel.Builder()
        .setDisplayName("My Test Channel")
        .setDisplayNumber("3")
        .setInputId("com.example.android.sampletvinput/.RichTvInputService")
        .build();

    Uri uri = getApplicationContext().getContentResolver().insert(TvContract.Channels.CONTENT_URI, testChannel.toContentValues());
    Log.i("SetupActivity", "Channel Inserted! Uri: " + uri);
    long channelId = Long.parseLong(uri.getLastPathSegment());

    return channelId;
}

接下来,在SetupActivity中调用onCreate()方法(取代现有代码)

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.rich_setup);

    insertChannel();
}
Activity 是否必需? 输入 说明
COLUMN_INPUT_ID TvInputService的完整类路径 示例: TvInputService位于应用主程序包中,完整类路径为<应用程序包>/<TvInputService的相对路径>。 如果TvInputService位于单独的程序包中,则inputId应为<应用程序包>/<完整单独包+TvInputService的路径>
TvContract.Channels.CONTENT_URI 这是指向Android电视数据库中频道表的URI。
ContentResolver.bulkInsert()ContentResolver.applyBatch() 是,位于生产代码中 以上任意一项都可以确保在一次数据库操作中完成所有频道插入。

插入GracenoteId

如果没有使用Gracenote,请跳过本节。

Gracenote是与Fire TV集成的电视目录提供方,能够从云端提供频道和节目的元数据。如果您的内容已与Gracenote集成,则可以提供唯一的ID,供Fire TV用来收集元数据。如果有兴趣与Gracenote集成,请联系您的亚马逊联系人以了解更多信息。

以下示例展示了如何使用亚马逊合约密钥将唯一频道Gracenote ID插入到JSON对象中,以显示频道类型和ID。可以在SetupActivity.java中插入channels函数。

/**
 * 外部ID的类型,类型列表需要被定义为wiki中的常量(以下列表的合约)
 * 空值或无效数据将导致元数据的服务匹配失败
 */
private final static String AMZ_KEY_EXTERNAL_ID_TYPE = "externalIdType";

/**
 * Gracenote输入类型的ID
 */
private final static String EXTERNAL_ID_TYPE_GRACENOTE_ONTV = "gracenote_ontv"; // gracenote ontv id
private final static String EXTERNAL_ID_TYPE_GRACENOTE_GVD = "gracenote_gvd"; // gracenote gvd id

/**
 * 外部ID的值,用于匹配服务元数据。
 * 空值或无效数据将导致元数据的服务匹配失败
 */
private final static String AMZ_KEY_EXTERNAL_ID_VALUE = "externalIdValue";
...

try {
    String jsonString = new JSONObject()
        .put(AMZ_KEY_EXTERNAL_ID_TYPE, EXTERNAL_ID_TYPE_GRACENOTE_ONTV)
        .put(AMZ_KEY_EXTERNAL_ID_VALUE, < gracenote Id > )
        .put(AMZ_KEY_PLAYBACK_DEEP_LINK_URI, playbackDeepLinkIntent.toUri(Intent.URI_INTENT_SCHEME))
        .toString();

    values.put(TvContract.Channels.COLUMN_INTERNAL_PROVIDER_DATA, jsonString.getBytes());
} catch (JSONException e) {
    Log.e(TAG, "Error when adding data to blob " + e);
}
Activity 是否必需? 说明
externalIdTypeexternalIdValue 这些字段名称属于开发者与亚马逊之间合约的一部分,用于向Fire TV提供Gracenote信息。请勿更改这些字符串。
TvContract.Channels.COLUMN_INTERNAL_PROVIDER_DATA 这属于开发者与亚马逊之间合约的一部分,用于向Fire TV提供深层链接和Gracenote信息。
  • 如果您的Gracenote ID属于其他类型,请查看这是哪个类型。如果不确定,请联系您的亚马逊联系人。
  • 如果您计划使用Gracenote,但尚未拥有Gracenote ID,则可能暂时需要采用以下方式来进行开发。在美国/英国/德国,可以使用以下示例ID: 10171(迪士尼频道)、10240 (HBO)和12131(Cartoon Network),带有gracenote_ontv externalIdType。对于所有其他市场,可以使用以下示例ID,即带有gracenote_gvd externalIdTypeGN9BBXQSECYVNGW(HBO)。

如果选择使用深层链接,则使用亚马逊合约密钥字符串playbackDeepLinkUri将深层链接插入到JSON对象中。

/**
 * 用于在外部播放器中插入播放的深层链接的URI。
 * 空值或无效数据将导致默认与GLive TV Native播放器集成
 */
private final static String AMZ_KEY_PLAYBACK_DEEP_LINK_URI = "playbackDeepLinkUri";

...

Intent playbackDeepLinkIntent = new Intent();
...
// 构建频道的contentValues
ContentValues values = new contentValues();
values.put(Channels.COLUMN_INPUT_ID, inputId);
values.put(Channels.COLUMN_DISPLAY_NAME, channel.name);
...
// 构建深层链接Intent
playbackDeepLinkIntent = //提供方频道的深层链接Intent
    ...
    try {
        String jsonString = new JSONObject()
            .put(AMZ_KEY_PLAYBACK_DEEP_LINK_URI, playbackDeepLinkIntent.toUri(Intent.URI_INTENT_SCHEME))
            .toString();

        // 将jsonString添加到频道的contentValues
        values.put(TvContract.Channels.COLUMN_INTERNAL_PROVIDER_DATA, jsonString.getBytes());
    } catch (JSONException e) {
        Log.i(TAG, "Error when adding data to blob " + e);
    }

Uri uri = context.getContentResolver().insert(TvContract.Channels.CONTENT_URI, values);
Activity 是否必需? 说明
playbackDeepLinkUri 这属于开发者与亚马逊之间合约的一部分,用于向Fire TV提供频道的深层链接信息。请勿更改此字符串。
TvContract.Channels.COLUMN_INTERNAL_PROVIDER_DATA 这属于开发者与亚马逊之间合约的一部分,用于向Fire TV提供深层链接和Gracenote信息。

检查点 - 在Fire TV的UI中显示一个频道

  1. 在Fire TV上构建并安装您的APK。
  2. 导航到“Settings(设置)”>“Live TV(电视直播)”>“Sync Sources(同步来源)”,并选择相应的来源。
  3. 导航到主页 >“On Now”行。插入的频道应显示为卡片(内容框,有时称为磁贴)之一。如果没有使用Gracenote,则将看到一个带有频道名称的灰色磁贴。如果您的设备上有多个来自其他来源的频道,则设备可能无法显示这些频道(存在限制)。
  4. 导航到“Live TV”>“Channel Guide”,打开“Options(选项)”菜单(3行)>“Filter Channels(筛选频道)”>“您的输入名称”。插入的频道应显示为屏幕上的一行。
  5. 导航到“Settings”>“Live TV”>“Manage Channels(管理频道)”。输入名称(来自作业服务XML文件)应显示在列表下方,并且插入的频道应该已被分配给该输入名称。
  6. (如果使用深层链接)单击“On Now”行的频道卡片。此时应用应该启动并显示预期的频道。
  7. (如果已集成Gracenote)频道将在“On Now”行和“Channel Guide”中显示完整的节目元数据。

故障排除

频道没有显示在“On Now(正在播放)”行或“Channel Guide(频道指南)”中

  • 请参阅“检查点”以确认是否已在允许列表中添加该频道。
  • 确认频道的inputId是否与TvInputService的完整类路径等同。
  • 确认调试APK和生产APK是否具有相同的程序包名称。
  • 确认频道是否正确插入到TIF中。
    • 插入之后,为频道信息创建硬编码查询,以确保频道位于数据库中。
  • 确认亚马逊能否正确提取该频道。
    • 插入频道之前,请查看adb日志:

      对于Mac/Linux,请查看adb logcat | grep StationSync

      对于Windows,请查看adb logcat | findstr StationSync

    • 插入频道后,您应该能够看到类似下文所示的日志。“Added(已添加)”意味着亚马逊正在识别Android电视数据库中的新频道。

08-07 15:24:57.101 11882 11941 I StationSync: Started full channel sync
08-07 15:24:57.188 11882 11941 I StationSync: Finished full channel sync, found: 15, added: 1, removed: 0, updated: 0

频道在“On Now”行中显示为没有图像的空白磁贴(仅显示频道名称)

  • 如果频道未集成Gracenote,则此情况属于预期行为。如果已集成Gracenote,请参阅以下信息。

频道具有Gracenote ID,但“On Now”行或“Channel Guide”中没有显示元数据

  • 确保您清楚自己的源支持onTV还是GVD,并在TvContractUtils中准确定义这一点。Amazon Catalog在某些市场支持onTV。如果亚马逊的支持情况与您拥有的Gracenote ID不匹配,请联系您的亚马逊联系人。他们可能会与Gracenote共同修正该问题,或切换到TIF。
  • 重复检查Gracenote ID值。onTV仅使用数字值,而GVD使用字母数字。

后续步骤

如果没有使用Gracenote,请跳过本步骤: 步骤4: 插入节目

如果正在使用Gracenote,请跳过步骤4,然后转到以下内容: 步骤5: 在Fire TV UI中播放