Màn hình chính của Android TV, hoặc đơn giản là màn hình chính, cung cấp một giao diện người dùng hiển thị nội dung đề xuất dưới dạng bảng kênh và chương trình. Mỗi hàng là một kênh. Kênh chứa các thẻ của tất cả chương trình có trên kênh đó:
Tài liệu này trình bày cách thêm kênh và chương trình vào màn hình chính, cập nhật nội dung, xử lý thao tác của người dùng và mang lại trải nghiệm tốt nhất cho người dùng. (Nếu bạn muốn tìm hiểu sâu hơn về API này, hãy thử lớp học lập trình màn hình chính và xem phiên trình bày tại I/O 2017 về Android TV.)
Lưu ý: Kênh đề xuất chỉ có ở Android 8.0 (API cấp 26) trở lên. Bạn phải sử dụng các thành phần này để cung cấp đề xuất dành cho ứng dụng chạy trong Android 8.0 (API cấp 26) trở lên. Người nhận cung cấp đề xuất cho các ứng dụng chạy trên các phiên bản Android cũ hơn, phải sử dụng hàng đề xuất thay thế.
Giao diện người dùng trên màn hình chính
Các ứng dụng có thể tạo kênh mới, thêm, xoá và cập nhật các chương trình trên một kênh, cũng như kiểm soát thứ tự của các chương trình trong một kênh. Ví dụ: một ứng dụng có thể tạo kênh có tên là "Tính năng mới" và hiển thị thẻ cho các chương trình mới có sẵn.
Các ứng dụng không thể kiểm soát thứ tự xuất hiện của các kênh trên màn hình chính. Khi ứng dụng của bạn tạo một kênh mới, màn hình chính sẽ thêm kênh đó vào cuối danh sách kênh. Người dùng có thể sắp xếp lại, ẩn và hiển thị kênh.
Kênh Watch Next
Kênh Watch Next là hàng thứ hai xuất hiện trên màn hình chính, sau hàng ứng dụng. Hệ thống tạo ra và duy trì kênh này. Ứng dụng của bạn có thể thêm chương trình lên kênh Watch Next. Để biết thêm thông tin, hãy xem bài viết Thêm chương trình vào kênh Watch Next.
Kênh ứng dụng
Tất cả các kênh mà ứng dụng của bạn tạo ra đều tuân theo vòng đời sau:
- Người dùng khám phá một kênh trong ứng dụng của bạn và yêu cầu thêm kênh đó vào màn hình chính.
- Ứng dụng tạo kênh và thêm kênh đó vào
TvProvider
(tại thời điểm này, kênh không hiển thị). - Ứng dụng yêu cầu hệ thống cho thấy kênh đó.
- Hệ thống sẽ yêu cầu người dùng phê duyệt kênh mới.
- Kênh mới sẽ xuất hiện ở hàng cuối cùng của màn hình chính.
Kênh mặc định
Ứng dụng của bạn có thể cung cấp số lượng kênh bất kỳ để người dùng thêm vào màn hình chính. Người dùng thường phải chọn và phê duyệt từng kênh trước khi kênh đó xuất hiện trong màn hình chính. Mỗi ứng dụng đều có thể tạo một kênh mặc định. Kênh mặc định đặc biệt vì tự động xuất hiện trên màn hình chính; người dùng không phải yêu cầu truy vấn đó một cách rõ ràng.
Điều kiện tiên quyết
Màn hình chính của Android TV sử dụng các API TvProvider
của Android để quản lý những kênh và chương trình mà ứng dụng của bạn tạo ra.
Để truy cập vào dữ liệu của nhà cung cấp, hãy thêm quyền sau đây vào tệp kê khai của ứng dụng:
<uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
Thư viện hỗ trợ TvProvider
giúp bạn dễ dàng sử dụng trình cung cấp hơn. Thêm vào các phần phụ thuộc trong tệp build.gradle
:
Groovy
implementation 'androidx.tvprovider:tvprovider:1.0.0'
Kotlin
implementation("androidx.tvprovider:tvprovider:1.0.0")
Để làm việc với kênh và chương trình, hãy nhớ thêm các tệp nhập thư viện hỗ trợ sau đây vào chương trình của bạn:
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;
Kênh
Kênh đầu tiên mà ứng dụng của bạn tạo sẽ trở thành kênh mặc định. Kênh mặc định sẽ tự động xuất hiện trên màn hình chính. Tất cả các kênh khác mà bạn tạo phải được người dùng chọn và chấp nhận thì mới có thể xuất hiện trên màn hình chính.
Tạo kênh
Ứng dụng của bạn nên yêu cầu hệ thống chỉ hiển thị các kênh mới thêm khi ứng dụng đang chạy ở nền trước. Việc này ngăn ứng dụng hiển thị hộp thoại yêu cầu phê duyệt thêm kênh của bạn trong khi người dùng đang chạy một ứng dụng khác. Nếu bạn cố thêm một kênh khi đang chạy ở chế độ nền, thì phương thức onActivityResult()
của hoạt động sẽ trả về mã trạng thái RESULT_CANCELED
.
Để tạo kênh, hãy làm theo các bước sau:
Tạo trình tạo kênh và đặt các thuộc tính cho trình tạo kênh đó. Lưu ý rằng loại kênh phải là
TYPE_PREVIEW
. Thêm người khác thuộc tính là bắt buộc.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);Chèn kênh vào trình cung cấp:
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());
-
Bạn cần phải lưu mã nhận dạng kênh để thêm chương trình vào kênh sau. Trích xuất mã nhận dạng kênh từ URI trả về:
Kotlin
var channelId = ContentUris.parseId(channelUri)
Java
long channelId = ContentUris.parseId(channelUri);
Bạn phải thêm biểu trưng cho kênh của mình. Sử dụng
Uri
hoặcBitmap
. Biểu trưng biểu tượng phải có kích thước 80dp x 80dp và phải mờ. Quảng cáo này được hiển thị dưới mặt nạ tròn: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);
Tạo kênh mặc định (không bắt buộc): Khi ứng dụng của bạn tạo kênh đầu tiên bạn có thể biến nó thành kênh kênh mặc định để kênh này xuất hiện trong trang chủ màn hình ngay lập tức mà không cần bất kỳ hành động nào của người dùng. Mọi kênh khác mà bạn tạo không hiển thị cho đến khi người dùng nói rõ chọn chúng.
Kotlin
TvContractCompat.requestChannelBrowsable(context, channelId)
Java
TvContractCompat.requestChannelBrowsable(context, channelId);
- Đặt kênh mặc định xuất hiện trước khi mở ứng dụng. Bạn có thể
hãy làm cho hành vi này xảy ra bằng cách thêm
BroadcastReceiver
theo dõi Hành độngandroid.media.tv.action.INITIALIZE_PROGRAMS
mà màn hình chính sẽ gửi sau khi ứng dụng được cài đặt: Khi cài đặt ứng dụng không qua cửa hàng ứng dụng trong quá trình phát triển, bạn có thể kiểm thử bước này bằng cách kích hoạt ý định thông qua adb, trong đó your.package.name/.YourReceiverName là của ứng dụng của bạn<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>
BroadcastReceiver
:adb shell am broadcast -a android.media.tv.action.INITIALIZE_PROGRAMS -n \ your.package.name/.YourReceiverName
Trong một số ít trường hợp, ứng dụng của bạn có thể nhận được thông báo truyền tin cùng lúc với người dùng khởi động ứng dụng của bạn. Hãy đảm bảo rằng mã của bạn không cố gắng thêm kênh mặc định nhiều lần.
Cập nhật kênh
Việc cập nhật kênh cũng tương tự như việc tạo kênh.
Sử dụng một Channel.Builder
khác để đặt các thuộc tính cần thay đổi.
Sử dụng ContentResolver
để cập nhật kênh. Sử dụng mã nhận dạng kênh mà bạn đã lưu khi kênh được thêm lần đầu:
Kotlin
context.contentResolver.update( TvContractCompat.buildChannelUri(channelId), builder.build().toContentValues(), null, null )
Java
context.getContentResolver().update(TvContractCompat.buildChannelUri(channelId), builder.build().toContentValues(), null, null);
Để cập nhật biểu trưng của kênh, hãy dùng storeChannelLogo()
.
Xoá kênh
Kotlin
context.contentResolver.delete(TvContractCompat.buildChannelUri(channelId), null, null)
Java
context.getContentResolver().delete(TvContractCompat.buildChannelUri(channelId), null, null);
Chương trình
Thêm chương trình vào kênh ứng dụng
Tạo một PreviewProgram.Builder
và đặt các thuộc tính cho thành phần đó:
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);
Hãy thêm các thuộc tính khác tuỳ theo loại chương trình. (Để xem các thuộc tính có sẵn cho từng loại chương trình, hãy tham khảo bảng dưới đây.)
Chèn chương trình vào nhà cung cấp:
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());
Truy xuất mã chương trình để tham khảo sau này:
Kotlin
val programId = ContentUris.parseId(programUri)
Java
long programId = ContentUris.parseId(programUri);
Thêm chương trình vào kênh Watch Next
Để chèn chương trình vào kênh Watch Next, hãy xem bài viết Thêm chương trình vào đồng hồ Kênh tiếp theo.
Cập nhật chương trình
Bạn có thể thay đổi thông tin của chương trình. Ví dụ: Bạn nên cập nhật giá thuê của một bộ phim hoặc cập nhật một thanh tiến trình cho biết thời lượng mà người dùng đã xem một chương trình.
Hãy sử dụng PreviewProgram.Builder
để đặt các thuộc tính bạn cần thay đổi,
sau đó gọi getContentResolver().update
để cập nhật chương trình. Chỉ định mã chương trình mà bạn đã lưu khi chương trình được thêm lần đầu tiên:
Kotlin
context.contentResolver.update( TvContractCompat.buildPreviewProgramUri(programId), builder.build().toContentValues(), null, null )
Java
context.getContentResolver().update(TvContractCompat.buildPreviewProgramUri(programId), builder.build().toContentValues(), null, null);
Xoá chương trình
Kotlin
context.contentResolver .delete(TvContractCompat.buildPreviewProgramUri(programId), null, null)
Java
context.getContentResolver().delete(TvContractCompat.buildPreviewProgramUri(programId), null, null);
Xử lý hành động của người dùng
Ứng dụng của bạn có thể giúp người dùng khám phá nội dung bằng cách cung cấp một giao diện người dùng để hiển thị và thêm các kênh. Ứng dụng của bạn cũng phải xử lý các hoạt động tương tác với kênh sau khi kênh xuất hiện trên màn hình chính.
Khám phá và thêm kênh
Ứng dụng của bạn có thể cung cấp một thành phần trên giao diện người dùng cho phép người dùng chọn và thêm kênh (ví dụ: nút yêu cầu thêm kênh).
Sau khi người dùng yêu cầu một kênh cụ thể, hãy thực thi mã này để yêu cầu người dùng cho phép thêm kênh vào giao diện người dùng của màn hình chính:
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 }
Hệ thống sẽ hiển thị hộp thoại yêu cầu người dùng phê duyệt kênh.
Xử lý kết quả của yêu cầu trong phương thức onActivityResult
của hoạt động (Activity.RESULT_CANCELED
hoặc Activity.RESULT_OK
).
Sự kiện trên màn hình chính của Android TV
Khi người dùng tương tác với các chương trình/kênh mà ứng dụng phát hành, màn hình chính sẽ gửi ý định đến ứng dụng:
- Màn hình chính sẽ gửi
Uri
được lưu trữ trong thuộc tính APP_LINK_INTENT_URI của một kênh đến ứng dụng khi người dùng chọn biểu trưng của kênh đó. Ứng dụng chỉ nên chạy giao diện người dùng chính hoặc một khung hiển thị liên quan đến kênh đã chọn. - Màn hình chính sẽ gửi
Uri
đã lưu trữ trong thuộc tính INTENT_URI của một chương trình đến ứng dụng khi người dùng chọn một chương trình. Ứng dụng sẽ phát nội dung đã chọn. - Người dùng có thể cho biết rằng họ không còn quan tâm đến một chương trình nữa và muốn xoá chương trình đó khỏi giao diện người dùng của màn hình chính. Hệ thống sẽ xoá chương trình này khỏi giao diện người dùng và gửi một ý định cho ứng dụng sở hữu chương trình (android.media.tv.ACTION_preview_PROJECT_BROWSABLE_DISABLED hoặc android.media.tv.ACTION_WATCH_NEXT_NEXT_BROWSABLE_DISABLED) bằng mã của chương trình. Ứng dụng phải xoá chương trình khỏi nhà cung cấp và KHÔNG được cài đặt lại.
Hãy nhớ tạo bộ lọc ý định cho tất cả Uris
mà màn hình chính gửi cho các hoạt động tương tác của người dùng; ví dụ:
<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>
Các phương pháp hay nhất
- Nhiều ứng dụng truyền hình yêu cầu người dùng đăng nhập. Trong trường hợp này,
BroadcastReceiver
ngheandroid.media.tv.action.INITIALIZE_PROGRAMS
nên đề xuất nội dung trên kênh đối với người dùng chưa được xác thực.Ví dụ: ban đầu, ứng dụng của bạn có thể hiển thị nội dung hay nhất hoặc nội dung đang phổ biến. Sau khi người dùng đăng nhập, có thể hiện nội dung được cá nhân hoá. Đây là cơ hội tuyệt vời để bán thêm ứng dụng người dùng trước khi họ đăng nhập. - Khi ứng dụng của bạn không chạy ở nền trước và bạn cần cập nhật một kênh hoặc một
hãy sử dụng
JobScheduler
để lên lịch cho công việc (xem: jobScheduler và jobService). - Hệ thống có thể thu hồi quyền của nhà cung cấp nếu ứng dụng của bạn hoạt động không đúng cách (ví dụ: liên tục gửi dữ liệu rác cho nhà cung cấp). Đảm bảo bạn gói mã truy cập vào trình cung cấp bằng mệnh đề try-catch để xử lý ngoại lệ về bảo mật.
Trước khi cập nhật các chương trình và kênh, hãy truy vấn nhà cung cấp để biết dữ liệu bạn cần cập nhật và điều chỉnh dữ liệu. Ví dụ: không cần phải cập nhật chương trình mà người dùng muốn xoá khỏi giao diện người dùng. Sử dụng công việc ở chế độ nền chèn/cập nhật dữ liệu của bạn vào nhà cung cấp sau khi truy vấn dữ liệu hiện có rồi yêu cầu phê duyệt cho các kênh của bạn. Bạn có thể chạy công việc này khi ứng dụng khởi động và bất cứ khi nào ứng dụng cần cập nhật dữ liệu.
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 } } }
Sử dụng URI duy nhất cho tất cả hình ảnh (biểu trưng, biểu tượng, hình ảnh nội dung). Hãy nhớ sử dụng một URI khác khi bạn cập nhật hình ảnh. Tất cả hình ảnh đều được lưu vào bộ nhớ đệm. Nếu bạn không thay đổi Uri khi thay đổi hình ảnh, hình ảnh cũ sẽ tiếp tục xuất hiện.
Hãy nhớ rằng mệnh đề WHERE không được cho phép và các lệnh gọi đến trình cung cấp có mệnh đề WHERE sẽ gửi một ngoại lệ bảo mật.
Thuộc tính
Phần này mô tả riêng biệt thuộc tính kênh và chương trình.
Thuộc tính của kênh
Bạn phải chỉ định các thuộc tính này cho mỗi kênh:
Thuộc tính | Ghi chú |
---|---|
LOẠI | được đặt thành TYPE_PREVIEW . |
DISPLAY_NAME | được đặt thành tên của kênh. |
APP_LINK_INTENT_URI | Khi người dùng chọn biểu trưng của kênh, hệ thống sẽ gửi ý định để bắt đầu một hoạt động trình bày nội dung liên quan đến kênh. Đặt thuộc tính này thành URI dùng trong bộ lọc ý định cho hoạt động đó. |
Ngoài ra, một kênh cũng có 6 trường dành riêng cho việc sử dụng ứng dụng nội bộ. Các trường này có thể được dùng để lưu trữ khoá hoặc các giá trị khác có thể giúp ứng dụng liên kết kênh với cấu trúc dữ liệu nội bộ của nó:
- INTERNAL_PROVIDER_ID
- INTERNAL_PROVIDER_DATA (DỮ LIỆU NỘI BỘ)
- NỘI BỘ_PROVIDER_FLAG1
- NỘI BỘ_PROVIDER_FLAG2
- NỘI BỘ_PROVIDER_FLAG3
- NỘI BỘ_PROVIDER_FLAG4
Thuộc tính chương trình
Hãy xem trang riêng để biết các đặc điểm của từng loại chương trình:
- Thuộc tính của chương trình video
- Thuộc tính chương trình âm thanh
- Thuộc tính chương trình trò chơi
- Thuộc tính của chương trình Watch Next
Mã mẫu
Để tìm hiểu thêm về cách tạo các ứng dụng tương tác với màn hình chính, cũng như cách thêm kênh và chương trình vào màn hình chính của Android TV, hãy xem lớp học lập trình trên màn hình chính của chúng tôi.