一般に、ユーザーはテレビを操作する際、最小限の入力でコンテンツを視聴することを好みます。多くの TV ユーザーにとって理想的なシナリオは、「TV の前に座り、電源を入れ、コンテンツを視聴する」というものです。ユーザーが興味のあるコンテンツにアクセスするための最も簡単なステップは、一般的にユーザーが好む方法です。
注: ここで説明する API は、Android 7.1(API レベル 25)以前の Android バージョンで実行されているアプリでおすすめ商品を作成する場合に使用します。Android 8.0(API レベル 26)以降で実行されているアプリにおすすめを提供するには、アプリでおすすめチャンネルを使用する必要があります。
Android フレームワークは、ホーム画面におすすめ行を表示することで、最小限の入力での操作を支援します。デバイスを初めて使用すると、テレビのホーム画面の最初の行としておすすめのコンテンツが表示されます。アプリのコンテンツ カタログからおすすめを投稿すると、ユーザーをアプリに呼び戻すことができます。
このガイドでは、ユーザーがアプリのコンテンツを簡単に見つけて楽しめるように、おすすめを作成して Android フレームワークに提供する方法について説明します。 Leanback サンプルアプリの実装例もご覧ください。
おすすめに関するベスト プラクティス
ユーザーはおすすめを利用することで、目当てのコンテンツやアプリをすばやく見つけることができます。TV アプリの優れたユーザー エクスペリエンスを生み出すには、高品質でユーザーとの関連性が高いレコメンデーションを作成することが重要です。そのため、ユーザーに提示するレコメンデーションは慎重に検討し、細かく管理する必要があります。
おすすめのタイプ
おすすめを作成する際は、未完了の表示アクティビティにユーザーを戻すか、それを関連コンテンツに拡張するアクティビティを提案する必要があります。考慮すべき具体的な推奨事項は次のとおりです。
- 連続コンテンツ: ユーザーがシリーズの視聴を再開できるように、次のエピソードに関するおすすめコンテンツ。または、一時停止した映画、テレビ番組、ポッドキャストに、連続するおすすめを使用して、ユーザーが数回クリックするだけで一時停止したコンテンツの視聴に戻れるようにします。
- 新規コンテンツ: ユーザーが別のシリーズの視聴を完了した場合の、初回放送の新しいエピソードなど、新しいおすすめコンテンツ。また、アプリでユーザーがコンテンツの購読、フォロー、追跡を行える場合は、追跡対象コンテンツのリスト内の未視聴のアイテムに新しいおすすめコンテンツを使用します。
- 関連コンテンツ: ユーザーの過去の視聴行動に基づいたおすすめコンテンツ。
最適なユーザー エクスペリエンスのためにおすすめカードをデザインする方法については、Android TV のデザイン仕様のおすすめの行をご覧ください。
おすすめを更新する
推奨事項を更新する際は、単に削除して再投稿するのではなく、推奨事項の行の末尾に表示されるようにします。映画などのコンテンツ アイテムが再生されたら、おすすめから 削除します。
おすすめをカスタマイズする
おすすめカードをカスタマイズしてブランディング情報を伝えるには、カードの前景画像と背景画像、色、アプリアイコン、タイトル、サブタイトルなどのユーザー インターフェース要素を設定します。詳しくは、Android TV デザイン仕様のおすすめの行をご覧ください。
おすすめをグループ化する
必要に応じて、おすすめのソースに基づいておすすめをグループ化できます。たとえば、アプリで、ユーザーが登録しているコンテンツのおすすめと、ユーザーが認識していない可能性のある新しいトレンドのコンテンツに関するおすすめという 2 つのおすすめグループを提供できます。
推奨事項の行を作成または更新するときに、システムは推奨事項をランク付けして、グループごとに個別にランク付けします。おすすめにグループ情報を提供することで、おすすめが無関係なおすすめより先に並べられないようにできます。
NotificationCompat.Builder.setGroup()
を使用して、推奨事項のグループキー文字列を設定します。たとえば、新しいトレンド コンテンツを含むグループに属するものとしておすすめをマークするには、setGroup("trending")
を呼び出します。
おすすめサービスを作成する
コンテンツのおすすめはバックグラウンド処理で作成されます。アプリがレコメンデーションに貢献できるようにするには、アプリのカタログからシステムのおすすめリストに掲載情報を定期的に追加するサービスを作成します。
次のコード例は、IntentService
を拡張してアプリのレコメンデーション サービスを作成する方法を示しています。
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; } }
このサービスをシステムで認識して実行するには、アプリ マニフェストを使用してサービスを登録します。次のコード スニペットは、このクラスをサービスとして宣言する方法を示しています。
<manifest ... > <application ... > ... <service android:name="com.example.android.tvleanback.UpdateRecommendationsService" android:enabled="true" /> </application> </manifest>
おすすめを作成する
おすすめサービスの実行を開始したら、おすすめを作成して Android フレームワークに渡す必要があります。フレームワークは、特定のテンプレートを使用し、特定のカテゴリのマークが付けられた Notification
オブジェクトとしておすすめを受け取ります。
値を設定する
おすすめカードの UI 要素の値を設定するには、以下に示すビルダー パターンに従うビルダークラスを作成します。まず、おすすめカード要素の値を設定します。
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; } ...
通知を作成する
値を設定したら通知を作成し、ビルダークラスから通知に値を割り当てて、NotificationCompat.Builder.build()
を呼び出します。
また、NotificationCompat.BigPictureStyle
通知が他のデバイスに表示されないように、必ず setLocalOnly()
を呼び出します。
次のコード例は、レコメンデーションを作成する方法を示しています。
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; } }
おすすめサービスを実行する
最新のおすすめを作成するために、アプリのおすすめサービスを定期的に実行する必要があります。サービスを実行するには、タイマーを実行して一定間隔で呼び出すクラスを作成します。次のコードサンプルでは、BroadcastReceiver
クラスを拡張して、30 分ごとにレコメンデーション サービスの定期的な実行を開始します。
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); } }
BroadcastReceiver
クラスのこの実装は、インストール先のテレビデバイスの起動後に実行する必要があります。そのためには、デバイスの起動プロセスの完了をリッスンするインテント フィルタを使用して、このクラスをアプリ マニフェストに登録します。次のサンプルコードは、この構成をマニフェストに追加する方法を示しています。
<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>
重要: 起動完了の通知を受け取るには、アプリが RECEIVE_BOOT_COMPLETED
権限をリクエストする必要があります。
詳細については、ACTION_BOOT_COMPLETED
をご覧ください。
おすすめサービスクラスの onHandleIntent()
メソッドで、次のようにおすすめをマネージャーに送信します。
Kotlin
val notification = notificationBuilder.build() notificationManager.notify(id, notification)
Java
Notification notification = notificationBuilder.build(); notificationManager.notify(id, notification);