blob: a74ee5683699fd67e7ea614a495188add6d2f786 [file] [log] [blame]
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");
}
&#64;Override
protected void onHandleIntent(Intent intent) {
Log.d(TAG, "Updating recommendation cards");
HashMap&lt;String, List&lt;Movie&gt;&gt; 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&lt;String, List&lt;Movie&gt;&gt; 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>
&lt;manifest ... &gt;
&lt;application ... &gt;
...
&lt;service
android:name="com.example.android.tvleanback.UpdateRecommendationsService"
android:enabled="true" /&gt;
&lt;/application&gt;
&lt;/manifest&gt;
</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;
&#64;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>
&lt;manifest ... &gt;
&lt;application ... &gt;
&lt;receiver android:name=&quot;com.example.android.tvleanback.BootupActivity&quot;
android:enabled=&quot;true&quot;
android:exported=&quot;false&quot;&gt;
&lt;intent-filter&gt;
&lt;action android:name=&quot;android.intent.action.BOOT_COMPLETED&quot;/&gt;
&lt;/intent-filter&gt;
&lt;/receiver&gt;
&lt;/application&gt;
&lt;/manifest&gt;
</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>