320 lines
11 KiB
Plaintext
320 lines
11 KiB
Plaintext
page.title=Recommending TV Content
|
|
page.tags=tv, recommendations
|
|
helpoutsWidget=true
|
|
|
|
trainingnavtop=true
|
|
|
|
@jd:body
|
|
|
|
<div id="tb-wrapper">
|
|
<div id="tb">
|
|
<h2>This lesson teaches you to</h2>
|
|
<ol>
|
|
<li><a href="#service">Create a Recommendations Service</a></li>
|
|
<li><a href="#build">Build Recommendations</a></li>
|
|
<li><a href="#run-service">Run Recommendations Service</a></li>
|
|
</ol>
|
|
<h2>Try it out</h2>
|
|
<ul>
|
|
<li><a class="external-link" href="https://github.com/googlesamples/androidtv-Leanback">Android
|
|
Leanback sample app</a></li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<p>
|
|
When interacting with TVs, users generally prefer to give minimal input before watching
|
|
content. An ideal scenario for many TV users is: sit down, turn on, and watch. The fewest steps
|
|
to get users to content they enjoy is generally the path they prefer.
|
|
</p>
|
|
|
|
<p>
|
|
The Android framework assists with minimum-input interaction by providing a recommendations row
|
|
on the home screen. Content recommendations appear as the first row of the TV home screen after
|
|
the first use of the device. Contributing recommendations from your app's content catalog can help
|
|
bring users back to your app.
|
|
</p>
|
|
|
|
<img src="{@docRoot}images/tv/home-recommendations.png" alt="" id="figure1" />
|
|
<p class="img-caption">
|
|
<strong>Figure 1.</strong> An example of the recommendations row.
|
|
</p>
|
|
|
|
<p>
|
|
This lesson teaches you how to create recommendations and provide them to the Android framework
|
|
so users can easily discover and enjoy your app content. This discussion describes some code from
|
|
the <a class="external-link" href="https://github.com/googlesamples/androidtv-Leanback">Android
|
|
Leanback sample app</a>.
|
|
</p>
|
|
|
|
|
|
<h2 id="service">Create a Recommendations Service</h2>
|
|
|
|
<p>
|
|
Content recommendations are created with background processing. In order for your application to
|
|
contribute to recommendations, create a service that periodically adds listings from your
|
|
app's catalog to the system's list of recommendations.
|
|
</p>
|
|
|
|
<p>
|
|
The following code example illustrates how to extend {@link android.app.IntentService} to
|
|
create a recommendation service for your application:
|
|
</p>
|
|
|
|
<pre>
|
|
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;
|
|
}
|
|
}
|
|
</pre>
|
|
|
|
<p>
|
|
In order for this service to be recognized by the system and run, register it using your
|
|
app manifest. The following code snippet illustrates how to declare this class as a service:
|
|
</p>
|
|
|
|
<pre>
|
|
<manifest ... >
|
|
<application ... >
|
|
...
|
|
|
|
<service
|
|
android:name="com.example.android.tvleanback.UpdateRecommendationsService"
|
|
android:enabled="true" />
|
|
</application>
|
|
</manifest>
|
|
</pre>
|
|
|
|
<h3 id="refreshing">Refreshing Recommendations</h3>
|
|
|
|
<p>Base your recommendations on user behavior and data such as play lists, wish lists, and associated
|
|
content. When refreshing recommendations, don't just remove and repost them, because doing so causes
|
|
the recommendations to appear at the end of the recommendations row. Once a content item, such as a
|
|
movie, has been played, <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html#Removing">
|
|
remove it</a> from the recommendations.</p>
|
|
|
|
<p>The order of an app's recommendations is preserved according to the order in which the app
|
|
provides them. The framework interleaves app recommendations based on recommendation quality,
|
|
as measured by user behavior. Better recommendations make an app's recommendations more likely
|
|
to appear near the front of the list.</p>
|
|
|
|
<h2 id="build">Build Recommendations</h2>
|
|
|
|
<p>
|
|
Once your recommendation service starts running, it must create recommendations and pass them to
|
|
the Android framework. The framework receives the recommendations as {@link
|
|
android.app.Notification} objects that use a specific template and are marked with a specific
|
|
category.
|
|
</p>
|
|
|
|
<h3 id="setting-ui">Setting the Values</h3>
|
|
|
|
<p>To set the UI element values for the recommendation card, you create a builder class that follows
|
|
the builder pattern described as follows. First, you set the values of the recommendation card
|
|
elements.</p>
|
|
|
|
<pre>
|
|
public class RecommendationBuilder {
|
|
...
|
|
|
|
public RecommendationBuilder setTitle(String title) {
|
|
mTitle = title;
|
|
return this;
|
|
}
|
|
|
|
public RecommendationBuilder setDescription(String description) {
|
|
mDescription = description;
|
|
return this;
|
|
}
|
|
|
|
public RecommendationBuilder setImage(String uri) {
|
|
mImageUri = uri;
|
|
return this;
|
|
}
|
|
|
|
public RecommendationBuilder setBackground(String uri) {
|
|
mBackgroundUri = uri;
|
|
return this;
|
|
}
|
|
...
|
|
</pre>
|
|
|
|
<h3 id="create-notification">Creating the Notification</h3>
|
|
|
|
<p>
|
|
Once you've set the values, you then build the notification, assigning the values from the builder
|
|
class to the notification, and calling {@link android.support.v4.app.NotificationCompat.Builder#build()
|
|
NotificationCompat.Builder.build()}.
|
|
</p>
|
|
|
|
<p>
|
|
Also, be sure to call
|
|
{@link android.support.v4.app.NotificationCompat.Builder#setLocalOnly(boolean) setLocalOnly()}
|
|
so the {@link android.support.v4.app.NotificationCompat.BigPictureStyle} notification won't show up
|
|
on other devices.
|
|
</p>
|
|
|
|
<p>
|
|
The following code example demonstrates how to build a recommendation.
|
|
</p>
|
|
|
|
<pre>
|
|
public class RecommendationBuilder {
|
|
...
|
|
|
|
public Notification build() throws IOException {
|
|
...
|
|
|
|
Notification notification = new NotificationCompat.BigPictureStyle(
|
|
new NotificationCompat.Builder(mContext)
|
|
.setContentTitle(mTitle)
|
|
.setContentText(mDescription)
|
|
.setPriority(mPriority)
|
|
.setLocalOnly(true)
|
|
.setOngoing(true)
|
|
.setColor(mContext.getResources().getColor(R.color.fastlane_background))
|
|
.setCategory(Notification.CATEGORY_RECOMMENDATION)
|
|
.setLargeIcon(image)
|
|
.setSmallIcon(mSmallIcon)
|
|
.setContentIntent(mIntent)
|
|
.setExtras(extras))
|
|
.build();
|
|
|
|
return notification;
|
|
}
|
|
}
|
|
</pre>
|
|
|
|
<h2 id="run-service">Run Recommendations Service</h3>
|
|
|
|
<p>
|
|
Your app's recommendation service must run periodically in order to create current
|
|
recommendations. To run your service, create a class that runs a timer and invokes
|
|
it at regular intervals. The following code example extends the {@link
|
|
android.content.BroadcastReceiver} class to start periodic execution of a recommendation service
|
|
every half hour:
|
|
</p>
|
|
|
|
<pre>
|
|
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);
|
|
}
|
|
}
|
|
</pre>
|
|
|
|
<p>
|
|
This implementation of the {@link android.content.BroadcastReceiver} class must run after start
|
|
up of the TV device where it is installed. To accomplish this, register this class in your app
|
|
manifest with an intent filter that listens for the completion of the device boot process. The
|
|
following sample code demonstrates how to add this configuration to the manifest:
|
|
</p>
|
|
|
|
<pre>
|
|
<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>
|
|
</pre>
|
|
|
|
<p class="note">
|
|
<strong>Important:</strong> Receiving a boot completed notification requires that your app
|
|
requests the {@link android.Manifest.permission#RECEIVE_BOOT_COMPLETED} permission.
|
|
For more information, see {@link android.content.Intent#ACTION_BOOT_COMPLETED}.
|
|
</p>
|
|
|
|
<p>In your recommendation service class' {@link android.app.IntentService#onHandleIntent(android.content.Intent)
|
|
onHandleIntent()}
|
|
method, post the recommendation to the manager as follows:</p>
|
|
|
|
<pre>
|
|
Notification notification = notificationBuilder.build();
|
|
mNotificationManager.notify(id, notification);
|
|
</pre>
|