主畫面管道

Android TV 主畫面或主畫面提供了 UI, 會以頻道節目表格的形式顯示推薦內容。每一列都代表一個管道。頻道上的所有節目資訊卡都會包含該頻道:

電視主畫面

這份文件說明如何將頻道和節目新增至主畫面、更新內容、處理使用者動作,以及為使用者提供最佳體驗。(如要深入瞭解 API,請嘗試使用 主畫面程式碼研究室 ,並觀看 2017 年 I/O 大會的 Android TV 講座)。

注意:推薦管道僅適用於 Android 8.0 (API 級別 26) 以上版本。您必須使用這些名稱 在 Android 8.0 (API 級別 26) 以上版本中執行應用程式的推薦。目的地: 針對在舊版 Android、你的應用程式、 必須使用 建議列

主畫面 UI

應用程式可以建立新頻道、新增、移除和更新頻道的節目,以及調整頻道節目的順序。 舉例來說,應用程式可以建立名為「新功能」的頻道並顯示新節目的資訊卡

應用程式無法控制頻道在主畫面中的顯示順序。應用程式建立新頻道時,主畫面會將其新增至頻道清單底部。使用者可以重新排序、隱藏及顯示頻道。

「接下來請看」頻道

「接下來請看」頻道是主畫面上的第二列, 應用程式列系統建立並維護這個頻道。應用程式可新增下列項目: 加入「接下來請看」頻道如需詳細資訊,請參閱將節目加入 「接下來請看」頻道

應用程式管道

應用程式建立的管道會經歷這段生命週期:

  1. 使用者在應用程式中發現頻道,並要求將頻道新增到主畫面。
  2. 應用程式會建立頻道,並將其新增至 TvProvider (此時無法看到頻道)。
  3. 應用程式要求系統顯示頻道。
  4. 系統會要求使用者核准新頻道。
  5. 新頻道會顯示在主畫面的最後一列。

預設頻道

應用程式可提供多種頻道,讓使用者加入主畫面,數量不限。使用者通常必須 選取及核准要顯示在主畫面上的頻道。每個應用程式都可建立一個預設頻道。 預設頻道會自動出現在主畫面上,因此是特別的。使用者不必 明確要求。

必要條件

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

如要建立頻道,請按照下列步驟操作:

  1. 建立頻道製作工具並設定屬性。請注意, 管道類型必須是 TYPE_PREVIEW。新增更多 attributes

    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);
    
  2. 將頻道插入供應器:

    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());
    
  3. 你必須先儲存頻道 ID,才能在頻道中新增節目 從傳回的 URI 中擷取頻道 ID:

    Kotlin

    var channelId = ContentUris.parseId(channelUri)
    

    Java

    long channelId = ContentUris.parseId(channelUri);
    
  4. 您必須為頻道新增標誌。使用 UriBitmap。Google 標誌 圖示應為 80dp x 80dp,且必須為不透明。這項資訊會顯示在 圓形遮罩:

    電視主畫面圖示遮罩

    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);
    
  5. 建立預設管道 (選擇性):應用程式建立第一個管道時 就交給我們 預設頻道,讓該頻道出現在首頁中 使用者不需要執行任何操作您建立的任何其他頻道 必須等到使用者明確顯示 選取圖片。

    Kotlin

    TvContractCompat.requestChannelBrowsable(context, channelId)
    

    Java

    TvContractCompat.requestChannelBrowsable(context, channelId);
    

  6. 在應用程式開啟前顯示預設頻道。你可以 新增 BroadcastReceiver 來監聽 android.media.tv.action.INITIALIZE_PROGRAMS 動作,也就是主畫面 系統會在應用程式安裝後傳送:
    <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);

將節目新增至「接下來請看」頻道

如要在「接下來請看」頻道中插入節目,請參閱在「接下來請看」中新增節目 下一個頻道

更新程式

您可以變更節目的資訊。舉例來說,您可以更新電影的租借價格,或是更新進度列,顯示使用者的節目觀看進度。

使用 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
}

系統會顯示對話方塊,要求使用者核准頻道。 在活動的 onActivityResult 方法 (Activity.RESULT_CANCELEDActivity.RESULT_OK) 中處理要求的結果。

Android TV 主畫面事件

當使用者與應用程式發布的節目/頻道互動時,主畫面會將意圖傳送至應用程式:

  • 當使用者選取頻道標誌時,主畫面會將儲存在頻道 APP_LINK_INTENT_URI 屬性中的 Uri 傳送至應用程式。應用程式應僅啟動與所選頻道相關的主要 UI 或檢視畫面。
  • 當使用者選取節目時,主畫面會將程式中 INTENT_URI 屬性中儲存的 Uri 傳送至應用程式。應用程式應會播放所選內容。
  • 使用者可以表明自己已不感興趣節目,並希望從主畫面的使用者介面移除。系統會將程式從 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 應用程式都會要求使用者登入。在本例中:BroadcastReceiver 監聽android.media.tv.action.INITIALIZE_PROGRAMS的指令應建議 在此提供內容給未經驗證的使用者舉例來說,您的應用程式一開始 顯示最佳內容或目前的熱門內容使用者登入後 就會顯示個人化內容這是應用程式向上銷售的絕佳機會 在使用者登入前執行。
  • 您的應用程式不在前景運作,而您需要更新管道或 程式,使用 JobScheduler 安排工作時間 (請參閱: JobSchedulerJobService)。
  • 如果您的應用程式運作異常,系統可能會撤銷應用程式的供應商權限 (例如:持續傳送資料給供應商)。務必 使用 try-catch 子句納入存取提供者的程式碼,以便處理 和例外狀況。
  • 更新節目和管道前,請先向供應商查詢你提供的資料 需要更新和協調資料舉例來說,您不需要更新 使用者要從使用者介面中移除的程式。使用符合下列條件的背景工作: 查詢現有的 再要求取得頻道核准您可以在 應用程式啟動時,以及應用程式需要更新資料時。

    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_PREVIEW」。
DISPLAY_NAME 設為頻道名稱。
APP_LINK_INTENT_URI 使用者選取頻道標誌時,系統會傳送意圖,啟動能顯示頻道相關內容的活動。請將此屬性設為該活動意圖篩選器中使用的 URI。

此外,管道還有六個專用欄位供內部應用程式使用。這些欄位可用來儲存鍵或其他值,以協助應用程式將頻道對應至其內部資料結構:

  • 內部供應商 ID
  • 內部供應商資料
  • INTERNAL_PROVIDER_FLAG1
  • INTERNAL_PROVIDER_FLAG2
  • INTERNAL_PROVIDER_FLAG3
  • INTERNAL_PROVIDER_FLAG4

計畫屬性

針對各類課程查看個別網頁:

程式碼範例

如要進一步瞭解如何建構可與主畫面互動的應用程式,以及將頻道和節目新增到 Android TV 主畫面,請參閱我們的主畫面程式碼研究室