Khi tương tác với TV, người dùng thường thích nhập liệu ở mức tối thiểu trước khi xem nội dung. Đối với nhiều người dùng TV, kịch bản lý tưởng là: ngồi xuống, bật TV lên và xem. Con đường ít bước nhất để người dùng tiếp cận nội dung họ yêu thích thường là con đường họ ưa thích nhất.
Lưu ý: Chỉ sử dụng các API được mô tả ở đây để đưa ra đề xuất trong các ứng dụng chạy trên các phiên bản Android từ Android 7.1 trở xuống (API cấp 25). Để cung cấp đề xuất cho các ứng dụng chạy trên Android 8.0 (API cấp 26) trở lên, ứng dụng của bạn phải sử dụng kênh đề xuất.
Khung phần mềm Android hỗ trợ tương tác tối thiểu bằng cách cung cấp hàng gợi ý trên màn hình chính. Các đề xuất nội dung sẽ xuất hiện ở hàng đầu tiên trên màn hình chính của TV sau lần sử dụng thiết bị đầu tiên. Việc đóng góp các đề xuất từ kho nội dung của ứng dụng có thể giúp thu hút người dùng quay lại ứng dụng của bạn.
Hướng dẫn này sẽ chỉ cho bạn cách tạo các đề xuất và cung cấp chúng cho khung phần mềm Android để người dùng có thể khám phá và tận hưởng nội dung ứng dụng của bạn. Xem thêm ví dụ triển khai trong ứng dụng mẫu Leanback .
Các phương pháp tốt nhất để đưa ra khuyến nghị
Các đề xuất giúp người dùng nhanh chóng tìm thấy nội dung và ứng dụng mà họ yêu thích. Việc tạo ra các đề xuất chất lượng cao và phù hợp với người dùng là yếu tố quan trọng để tạo nên trải nghiệm tuyệt vời cho ứng dụng TV của bạn. Vì lý do này, bạn nên cân nhắc kỹ lưỡng những đề xuất mình đưa ra cho người dùng và quản lý chúng một cách chặt chẽ.
Các loại khuyến nghị
Khi tạo đề xuất, bạn nên liên kết người dùng trở lại các hoạt động xem chưa hoàn tất hoặc đề xuất các hoạt động mở rộng sang nội dung liên quan. Dưới đây là một số loại khuyến nghị cụ thể mà bạn nên cân nhắc:
- Nội dung tiếp theo đề xuất tập tiếp theo để người dùng tiếp tục xem loạt phim. Hoặc, sử dụng tính năng đề xuất tiếp tục xem cho các bộ phim, chương trình truyền hình hoặc podcast đã tạm dừng để người dùng có thể tiếp tục xem nội dung đã tạm dừng chỉ với vài cú nhấp chuột.
- Nội dung mới đề xuất, chẳng hạn như tập phim mới phát sóng lần đầu, nếu người dùng xem xong một loạt phim khác. Ngoài ra, nếu ứng dụng của bạn cho phép người dùng đăng ký, theo dõi hoặc xem nội dung, hãy sử dụng tính năng đề xuất nội dung mới cho các mục chưa xem trong danh sách nội dung được theo dõi của họ.
- Nội dung liên quan đề xuất dựa trên hành vi xem trước đây của người dùng.
Để biết thêm thông tin về cách thiết kế thẻ đề xuất nhằm mang lại trải nghiệm người dùng tốt nhất, hãy xem Hàng đề xuất trong Tài liệu đặc tả thiết kế Android TV.
Làm mới đề xuất
Khi làm mới các đề xuất, đừng chỉ xóa rồi đăng lại chúng, vì làm như vậy sẽ khiến các đề xuất xuất hiện ở cuối hàng đề xuất. Sau khi một nội dung, chẳng hạn như một bộ phim, đã được phát xong, hãy xóa nó khỏi phần đề xuất.
Tùy chỉnh nội dung đề xuất
Bạn có thể tùy chỉnh các thẻ đề xuất để truyền tải thông tin thương hiệu bằng cách thiết lập các yếu tố giao diện người dùng như hình nền và hình ảnh chính của thẻ, màu sắc, biểu tượng ứng dụng, tiêu đề và phụ đề. Để tìm hiểu thêm, hãy xem Recommendation Row trong Tài liệu đặc tả thiết kế Android TV.
Đề xuất của nhóm
Bạn có thể tùy chọn nhóm các đề xuất dựa trên nguồn đề xuất. Ví dụ, ứng dụng của bạn có thể cung cấp hai nhóm đề xuất: đề xuất nội dung mà người dùng đã đăng ký và đề xuất nội dung mới đang thịnh hành mà người dùng có thể chưa biết đến.
Hệ thống sẽ xếp hạng và sắp xếp các đề xuất cho từng nhóm riêng biệt khi tạo hoặc cập nhật dòng đề xuất. Bằng cách cung cấp thông tin nhóm cho các đề xuất của bạn, bạn có thể đảm bảo rằng các đề xuất của bạn không bị xếp chồng lên các đề xuất không liên quan.
Sử dụng NotificationCompat.Builder.setGroup() để đặt chuỗi khóa nhóm của một đề xuất. Ví dụ, để đánh dấu một đề xuất thuộc về một nhóm chứa nội dung thịnh hành mới, bạn có thể gọi setGroup("trending").
Tạo dịch vụ đề xuất
Các đề xuất nội dung được tạo ra bằng quá trình xử lý ngầm. Để ứng dụng của bạn đóng góp vào hệ thống đề xuất, hãy tạo một dịch vụ định kỳ thêm các sản phẩm từ danh mục của ứng dụng vào danh sách đề xuất của hệ thống.
Ví dụ mã sau minh họa cách mở rộng IntentService để tạo dịch vụ đề xuất cho ứng dụng của bạn:
Kotlin
class UpdateRecommendationsService : IntentService("RecommendationService") { override protected fun onHandleIntent(intent: Intent) { Log.d(TAG, "Updating recommendation cards") val recommendations = VideoProvider.getMovieList() if (recommendations == null) return var count = 0 try { val builder = RecommendationBuilder() .setContext(applicationContext) .setSmallIcon(R.drawable.videos_by_google_icon) for (entry in recommendations.entrySet()) { for (movie in entry.getValue()) { Log.d(TAG, "Recommendation - " + movie.getTitle()) builder.setBackground(movie.getCardImageUrl()) .setId(count + 1) .setPriority(MAX_RECOMMENDATIONS - count) .setTitle(movie.getTitle()) .setDescription(getString(R.string.popular_header)) .setImage(movie.getCardImageUrl()) .setIntent(buildPendingIntent(movie)) .build() if (++count >= MAX_RECOMMENDATIONS) { break } } if (++count >= MAX_RECOMMENDATIONS) { break } } } catch (e: IOException) { Log.e(TAG, "Unable to update recommendation", e) } } private fun buildPendingIntent(movie: Movie): PendingIntent { val detailsIntent = Intent(this, DetailsActivity::class.java) detailsIntent.putExtra("Movie", movie) val stackBuilder = TaskStackBuilder.create(this) stackBuilder.addParentStack(DetailsActivity::class.java) stackBuilder.addNextIntent(detailsIntent) // Ensure a unique PendingIntents, otherwise all // recommendations end up with the same PendingIntent detailsIntent.setAction(movie.getId().toString()) val intent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT) return intent } companion object { private val TAG = "UpdateRecommendationsService" private val MAX_RECOMMENDATIONS = 3 } }
Java
public class UpdateRecommendationsService extends IntentService { private static final String TAG = "UpdateRecommendationsService"; private static final int MAX_RECOMMENDATIONS = 3; public UpdateRecommendationsService() { super("RecommendationService"); } @Override protected void onHandleIntent(Intent intent) { Log.d(TAG, "Updating recommendation cards"); HashMap<String, List<Movie>> recommendations = VideoProvider.getMovieList(); if (recommendations == null) return; int count = 0; try { RecommendationBuilder builder = new RecommendationBuilder() .setContext(getApplicationContext()) .setSmallIcon(R.drawable.videos_by_google_icon); for (Map.Entry<String, List<Movie>> entry : recommendations.entrySet()) { for (Movie movie : entry.getValue()) { Log.d(TAG, "Recommendation - " + movie.getTitle()); builder.setBackground(movie.getCardImageUrl()) .setId(count + 1) .setPriority(MAX_RECOMMENDATIONS - count) .setTitle(movie.getTitle()) .setDescription(getString(R.string.popular_header)) .setImage(movie.getCardImageUrl()) .setIntent(buildPendingIntent(movie)) .build(); if (++count >= MAX_RECOMMENDATIONS) { break; } } if (++count >= MAX_RECOMMENDATIONS) { break; } } } catch (IOException e) { Log.e(TAG, "Unable to update recommendation", e); } } private PendingIntent buildPendingIntent(Movie movie) { Intent detailsIntent = new Intent(this, DetailsActivity.class); detailsIntent.putExtra("Movie", movie); TaskStackBuilder stackBuilder = TaskStackBuilder.create(this); stackBuilder.addParentStack(DetailsActivity.class); stackBuilder.addNextIntent(detailsIntent); // Ensure a unique PendingIntents, otherwise all // recommendations end up with the same PendingIntent detailsIntent.setAction(Long.toString(movie.getId())); PendingIntent intent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT); return intent; } }
Để hệ thống nhận diện và cho phép dịch vụ này hoạt động, hãy đăng ký nó bằng tệp kê khai ứng dụng của bạn. Đoạn mã sau minh họa cách khai báo lớp này như một dịch vụ:
<manifest ... > <application ... > ... <service android:name="com.example.android.tvleanback.UpdateRecommendationsService" android:enabled="true" /> </application> </manifest>
Xây dựng các đề xuất
Sau khi dịch vụ đề xuất của bạn bắt đầu hoạt động, nó phải tạo ra các đề xuất và chuyển chúng đến khung Android. Khung này nhận các đề xuất dưới dạng các đối tượng Notification sử dụng một mẫu cụ thể và được đánh dấu bằng một danh mục cụ thể.
Thiết lập các giá trị
Để thiết lập các giá trị phần tử giao diện người dùng cho thẻ đề xuất, bạn tạo một lớp trình tạo tuân theo mẫu trình tạo được mô tả như sau. Đầu tiên, bạn thiết lập giá trị cho các phần tử của thẻ đề xuất.
Kotlin
class RecommendationBuilder { ... fun setTitle(title: String): RecommendationBuilder { this.title = title return this } fun setDescription(description: String): RecommendationBuilder { this.description = description return this } fun setImage(uri: String): RecommendationBuilder { imageUri = uri return this } fun setBackground(uri: String): RecommendationBuilder { backgroundUri = uri return this } ...
Java
public class RecommendationBuilder { ... public RecommendationBuilder setTitle(String title) { this.title = title; return this; } public RecommendationBuilder setDescription(String description) { this.description = description; return this; } public RecommendationBuilder setImage(String uri) { imageUri = uri; return this; } public RecommendationBuilder setBackground(String uri) { backgroundUri = uri; return this; } ...
Tạo thông báo
Sau khi bạn đã thiết lập các giá trị, bạn sẽ xây dựng thông báo, gán các giá trị từ lớp xây dựng cho thông báo và gọi NotificationCompat.Builder.build().
Ngoài ra, hãy nhớ gọi setLocalOnly() để thông báo NotificationCompat.BigPictureStyle sẽ không hiển thị trên các thiết bị khác.
Ví dụ mã sau đây minh họa cách xây dựng hệ thống đề xuất.
Kotlin
class RecommendationBuilder { ... @Throws(IOException::class) fun build(): Notification { ... val notification = NotificationCompat.BigPictureStyle( NotificationCompat.Builder(context) .setContentTitle(title) .setContentText(description) .setPriority(priority) .setLocalOnly(true) .setOngoing(true) .setColor(context.resources.getColor(R.color.fastlane_background)) .setCategory(Notification.CATEGORY_RECOMMENDATION) .setLargeIcon(image) .setSmallIcon(smallIcon) .setContentIntent(intent) .setExtras(extras)) .build() return notification } }
Java
public class RecommendationBuilder { ... public Notification build() throws IOException { ... Notification notification = new NotificationCompat.BigPictureStyle( new NotificationCompat.Builder(context) .setContentTitle(title) .setContentText(description) .setPriority(priority) .setLocalOnly(true) .setOngoing(true) .setColor(context.getResources().getColor(R.color.fastlane_background)) .setCategory(Notification.CATEGORY_RECOMMENDATION) .setLargeIcon(image) .setSmallIcon(smallIcon) .setContentIntent(intent) .setExtras(extras)) .build(); return notification; } }
Chạy dịch vụ đề xuất
Dịch vụ đề xuất của ứng dụng cần phải chạy định kỳ để tạo ra các đề xuất hiện tại. Để chạy dịch vụ của bạn, hãy tạo một lớp chạy bộ hẹn giờ và gọi nó theo định kỳ. Ví dụ mã sau đây mở rộng lớp BroadcastReceiver để bắt đầu thực thi định kỳ dịch vụ đề xuất mỗi nửa giờ:
Kotlin
class BootupActivity : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { Log.d(TAG, "BootupActivity initiated") if (intent.action.endsWith(Intent.ACTION_BOOT_COMPLETED)) { scheduleRecommendationUpdate(context) } } private fun scheduleRecommendationUpdate(context: Context) { Log.d(TAG, "Scheduling recommendations update") val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager val recommendationIntent = Intent(context, UpdateRecommendationsService::class.java) val alarmIntent = PendingIntent.getService(context, 0, recommendationIntent, 0) alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, INITIAL_DELAY, AlarmManager.INTERVAL_HALF_HOUR, alarmIntent ) } companion object { private val TAG = "BootupActivity" private val INITIAL_DELAY:Long = 5000 } }
Java
public class BootupActivity extends BroadcastReceiver { private static final String TAG = "BootupActivity"; private static final long INITIAL_DELAY = 5000; @Override public void onReceive(Context context, Intent intent) { Log.d(TAG, "BootupActivity initiated"); if (intent.getAction().endsWith(Intent.ACTION_BOOT_COMPLETED)) { scheduleRecommendationUpdate(context); } } private void scheduleRecommendationUpdate(Context context) { Log.d(TAG, "Scheduling recommendations update"); AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); Intent recommendationIntent = new Intent(context, UpdateRecommendationsService.class); PendingIntent alarmIntent = PendingIntent.getService(context, 0, recommendationIntent, 0); alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, INITIAL_DELAY, AlarmManager.INTERVAL_HALF_HOUR, alarmIntent); } }
Việc triển khai lớp BroadcastReceiver này phải chạy sau khi thiết bị TV nơi nó được cài đặt khởi động. Để thực hiện điều này, hãy đăng ký lớp này trong tệp kê khai ứng dụng của bạn với một bộ lọc ý định lắng nghe quá trình khởi động thiết bị hoàn tất. Đoạn mã mẫu sau đây minh họa cách thêm cấu hình này vào tệp kê khai:
<manifest ... > <application ... > <receiver android:name="com.example.android.tvleanback.BootupActivity" android:enabled="true" android:exported="false"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED"/> </intent-filter> </receiver> </application> </manifest>
Quan trọng: Để nhận được thông báo khởi động hoàn tất, ứng dụng của bạn cần phải yêu cầu quyền RECEIVE_BOOT_COMPLETED.
Để biết thêm thông tin, hãy xem ACTION_BOOT_COMPLETED.
Trong phương thức onHandleIntent() của lớp dịch vụ đề xuất, hãy gửi đề xuất đến người quản lý như sau:
Kotlin
val notification = notificationBuilder.build() notificationManager.notify(id, notification)
Java
Notification notification = notificationBuilder.build(); notificationManager.notify(id, notification);