Android TV 主畫面或主畫面會提供 UI,以「頻道」和「節目」表格顯示推薦內容。每一列都代表一個管道。頻道上包含該頻道提供的所有節目資訊卡:
這份文件說明如何將頻道和節目新增至主畫面、更新內容、處理使用者操作,以及為使用者提供最佳體驗。(如要深入瞭解 API,請參閱主畫面程式碼研究室,並觀看 I/O 2017 Android TV 課程)。
注意:建議管道僅適用於 Android 8.0 (API 級別 26) 以上版本。對於在 Android 8.0 (API 級別 26) 以上版本中執行的應用程式,必須使用這些修飾符提供建議。如要針對在舊版 Android 上執行的應用程式提供建議,應用程式必須改用建議列。
主畫面 UI
應用程式可以建立新的頻道、新增、移除及更新頻道中的節目,以及控制頻道中的節目順序。 舉例來說,應用程式可以建立名為「新功能」的頻道,並為新上架的節目顯示資訊卡。
應用程式無法控制頻道在主畫面上顯示的順序。應用程式建立新頻道時,主畫面會將其新增至頻道清單底部。使用者可以重新排序、隱藏及顯示頻道。
「接下來請看」頻道
「接下來請看」頻道是主畫面中的應用程式列後方的第二列。系統建立並維護這個頻道。您的應用程式可將節目新增至「接下來請看」頻道。詳情請參閱將節目新增至「接下來請看」頻道。
應用程式管道
您應用程式建立的管道都會遵循這個生命週期:
- 使用者在應用程式中發現頻道,並要求將頻道新增至主畫面。
- 應用程式會建立管道並新增至
TvProvider
(目前不會顯示該管道)。 - 應用程式要求系統顯示頻道。
- 系統會要求使用者核准新頻道。
- 新頻道會顯示在主畫面的最後一列。
預設管道
應用程式可以為使用者提供不限數量的頻道,方便他們新增至主畫面。使用者通常必須先選取並核准每個頻道,頻道才會顯示在主畫面上。每個應用程式都有建立一個「預設」頻道的選項。預設版本是特殊的,因為預設管道會自動顯示在主畫面上;使用者不必明確要求。
必要條件
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
。
如要建立頻道,請按照下列步驟操作:
建立頻道製作工具並設定其屬性。請注意,管道類型必須為
TYPE_PREVIEW
。然後視需求新增更多屬性。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);將頻道插入供應器:
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());
-
您必須先儲存頻道 ID,日後才能在頻道中新增節目。從傳回的 URI 中擷取頻道 ID:
Kotlin
var channelId = ContentUris.parseId(channelUri)
Java
long channelId = ContentUris.parseId(channelUri);
您必須為頻道新增標誌。使用
Uri
或Bitmap
。標誌圖示應為 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);
建立預設管道 (選用):當應用程式建立第一個管道時,您可以將其設為預設管道。如此一來,使用者不需執行任何操作,就能立即在主畫面上看到這個預設管道。除非使用者明確選取您建立的任何其他管道,否則不會顯示。
Kotlin
TvContractCompat.requestChannelBrowsable(context, channelId)
Java
TvContractCompat.requestChannelBrowsable(context, channelId);
- 請在開啟應用程式前,先顯示預設頻道。如要執行這項行為,您可以新增
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:
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_CANCELED
或 Activity.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 應用程式都會要求使用者登入。在這種情況下,監聽
android.media.tv.action.INITIALIZE_PROGRAMS
的BroadcastReceiver
應向未經驗證的使用者建議頻道內容。舉例來說,您的應用程式一開始可以顯示最佳內容或目前熱門內容。使用者登入後可顯示個人化內容。這是在使用者登入前,對應用程式進行向上銷售的大好機會。 - 如果應用程式不在前景,而且您需要更新管道或程式,請使用
JobScheduler
安排工作時間 (請參閱 JobScheduler 和 JobService)。 - 如果應用程式出現異常行為 (例如持續利用資料為供應器濫發垃圾內容),系統會撤銷應用程式的供應器權限。請務必使用 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 主畫面中新增頻道和節目,請參閱我們的主畫面程式碼研究室。