1page.title=Recommending TV Content
2page.tags=tv, recommendations
3helpoutsWidget=true
4
5trainingnavtop=true
6
7@jd:body
8
9<div id="tb-wrapper">
10<div id="tb">
11  <h2>This lesson teaches you to</h2>
12  <ol>
13    <li><a href="#best-practices">Best Practices for Recommendations</a></li>
14    <li><a href="#service">Create a Recommendations Service</a></li>
15    <li><a href="#build">Build Recommendations</a></li>
16    <li><a href="#run-service">Run Recommendations Service</a></li>
17  </ol>
18  <h2>Try it out</h2>
19  <ul>
20    <li><a class="external-link" href="https://github.com/googlesamples/androidtv-Leanback">Android
21      Leanback sample app</a></li>
22  </ul>
23</div>
24</div>
25
26<p>
27  When interacting with TVs, users generally prefer to give minimal input before watching
28  content. An ideal scenario for many TV users is: sit down, turn on, and watch. The fewest steps
29  to get users to content they enjoy is generally the path they prefer.
30</p>
31
32<p>
33  The Android framework assists with minimum-input interaction by providing a recommendations row
34  on the home screen. Content recommendations appear as the first row of the TV home screen after
35  the first use of the device. Contributing recommendations from your app's content catalog can help
36  bring users back to your app.
37</p>
38
39<img src="{@docRoot}images/tv/home-recommendations.png" alt="" id="figure1" />
40<p class="img-caption">
41  <strong>Figure 1.</strong> An example of the recommendations row.
42</p>
43
44<p>
45  This lesson teaches you how to create recommendations and provide them to the Android framework
46  so users can easily discover and enjoy your app content. This discussion describes some code from
47  the <a class="external-link" href="https://github.com/googlesamples/androidtv-Leanback">Android
48  Leanback sample app</a>.
49</p>
50
51<h2 id="best-practices">Best Practices for Recommendations</h2>
52
53<p>Recommendations help users quickly find the content and apps they enjoy. Creating
54recommendations that are high-quality and relevant to users is an important factor in creating a
55great user experience with your TV app. For this reason, you should carefully consider what
56recommendations you present to the user and manage them closely.</p>
57
58<h3 id="types">Types of Recommendations</h3>
59
60<p>When you create recommendations, you should link users back to incomplete viewing activities or
61suggest activities that extend that to related content. Here are some specific type of
62recommendations you should consider:</p>
63
64<ul>
65<li><strong>Continuation content</strong> recommendations for the next episode for users to resume
66watching a series. Or, use continuation recommendations for paused movies, TV shows, or podcasts
67so users can get back to watching paused content in just a few clicks.</li>
68<li><strong>New content</strong> recommendations, such as for a new first-run episode, if the user
69finished watching another series. Also, if your app lets users subscribe to, follow, or track
70content, use new content recommendations for unwatched items in their list of tracked content.</li>
71<li><strong>Related content</strong> recommendations based on the users' historic viewing behavior.
72</li>
73</ul>
74
75<p>For more information on how to design recommendation cards for the best user experience, see
76<a href="https://www.google.com/design/spec-tv/system-overview/recommendation-row.html#recommendation-row-types-of-recommendations"
77class="external-link">Recommendation Row</a> in the Android TV Design Spec.</p>
78
79<h3 id="refreshing">Refreshing Recommendations</h3>
80
81<p>When refreshing recommendations, don't just remove and repost them, because doing so causes
82the recommendations to appear at the end of the recommendations row. Once a content item, such as a
83movie, has been played, <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html#Removing">
84remove it</a> from the recommendations.</p>
85
86<h3 id="customization">Customizing Recommendations</h3>
87
88<p>You can customize recommendation cards to convey branding information, by setting user interface
89elements such as the card's foreground and background image, color, app icon, title, and subtitle.
90To learn more, see
91<a href="https://www.google.com/design/spec-tv/system-overview/recommendation-row.html#recommendation-row-card-customization"
92class="external-link">Recommendation Row</a> in the Android TV Design Spec.</p>
93
94<h3 id="grouping">Grouping Recommendations</h3>
95
96<p>
97You can optionally group recommendations based on recommendation source. For example, your app
98might provide two groups of recommendations: recommendations for content the user is subscribed to,
99and recommendations for new trending content the user might not be aware of.
100</p>
101<p>
102The system ranks and orders recommendations for each group separately when creating or updating
103the recommendation row. By providing group information for your recommendations, you can ensure
104that your recommendations don’t get ordered below unrelated recommendations.
105</p>
106<p>
107Use
108{@link android.support.v4.app.NotificationCompat.Builder#setGroup
109NotificationCompat.Builder.setGroup()} to set the group key string of a recommendation. For
110example, to mark a recommendation as belonging to a group that contains new trending content,
111you might call <code>setGroup("trending")</code>.
112</p>
113
114<h2 id="service">Create a Recommendations Service</h2>
115
116<p>
117  Content recommendations are created with background processing. In order for your application to
118  contribute to recommendations, create a service that periodically adds listings from your
119  app's catalog to the system's list of recommendations.
120</p>
121
122<p>
123  The following code example illustrates how to extend {@link android.app.IntentService} to
124  create a recommendation service for your application:
125</p>
126
127<pre>
128public class UpdateRecommendationsService extends IntentService {
129    private static final String TAG = "UpdateRecommendationsService";
130    private static final int MAX_RECOMMENDATIONS = 3;
131
132    public UpdateRecommendationsService() {
133        super("RecommendationService");
134    }
135
136    &#64;Override
137    protected void onHandleIntent(Intent intent) {
138        Log.d(TAG, "Updating recommendation cards");
139        HashMap&lt;String, List&lt;Movie&gt;&gt; recommendations = VideoProvider.getMovieList();
140        if (recommendations == null) return;
141
142        int count = 0;
143
144        try {
145            RecommendationBuilder builder = new RecommendationBuilder()
146                    .setContext(getApplicationContext())
147                    .setSmallIcon(R.drawable.videos_by_google_icon);
148
149            for (Map.Entry&lt;String, List&lt;Movie&gt;&gt; entry : recommendations.entrySet()) {
150                for (Movie movie : entry.getValue()) {
151                    Log.d(TAG, "Recommendation - " + movie.getTitle());
152
153                    builder.setBackground(movie.getCardImageUrl())
154                            .setId(count + 1)
155                            .setPriority(MAX_RECOMMENDATIONS - count)
156                            .setTitle(movie.getTitle())
157                            .setDescription(getString(R.string.popular_header))
158                            .setImage(movie.getCardImageUrl())
159                            .setIntent(buildPendingIntent(movie))
160                            .build();
161
162                    if (++count >= MAX_RECOMMENDATIONS) {
163                        break;
164                    }
165                }
166                if (++count >= MAX_RECOMMENDATIONS) {
167                    break;
168                }
169            }
170        } catch (IOException e) {
171            Log.e(TAG, "Unable to update recommendation", e);
172        }
173    }
174
175    private PendingIntent buildPendingIntent(Movie movie) {
176        Intent detailsIntent = new Intent(this, DetailsActivity.class);
177        detailsIntent.putExtra("Movie", movie);
178
179        TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
180        stackBuilder.addParentStack(DetailsActivity.class);
181        stackBuilder.addNextIntent(detailsIntent);
182        // Ensure a unique PendingIntents, otherwise all
183        // recommendations end up with the same PendingIntent
184        detailsIntent.setAction(Long.toString(movie.getId()));
185
186        PendingIntent intent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
187        return intent;
188    }
189}
190</pre>
191
192<p>
193  In order for this service to be recognized by the system and run, register it using your
194  app manifest. The following code snippet illustrates how to declare this class as a service:
195</p>
196
197<pre>
198&lt;manifest ... &gt;
199  &lt;application ... &gt;
200    ...
201
202    &lt;service
203            android:name="com.example.android.tvleanback.UpdateRecommendationsService"
204            android:enabled="true" /&gt;
205  &lt;/application&gt;
206&lt;/manifest&gt;
207</pre>
208
209<h2 id="build">Build Recommendations</h2>
210
211<p>
212  Once your recommendation service starts running, it must create recommendations and pass them to
213  the Android framework. The framework receives the recommendations as {@link
214  android.app.Notification} objects that use a specific template and are marked with a specific
215  category.
216</p>
217
218<h3 id="setting-ui">Setting the Values</h3>
219
220<p>To set the UI element values for the recommendation card, you create a builder class that follows
221the builder pattern described as follows. First, you set the values of the recommendation card
222elements.</p>
223
224<pre>
225public class RecommendationBuilder {
226    ...
227
228    public RecommendationBuilder setTitle(String title) {
229            mTitle = title;
230            return this;
231        }
232
233        public RecommendationBuilder setDescription(String description) {
234            mDescription = description;
235            return this;
236        }
237
238        public RecommendationBuilder setImage(String uri) {
239            mImageUri = uri;
240            return this;
241        }
242
243        public RecommendationBuilder setBackground(String uri) {
244            mBackgroundUri = uri;
245            return this;
246        }
247...
248</pre>
249
250<h3 id="create-notification">Creating the Notification</h3>
251
252<p>
253  Once you've set the values, you then build the notification, assigning the values from the builder
254  class to the notification, and calling {@link android.support.v4.app.NotificationCompat.Builder#build()
255  NotificationCompat.Builder.build()}.
256</p>
257
258<p>
259  Also, be sure to call
260  {@link android.support.v4.app.NotificationCompat.Builder#setLocalOnly(boolean) setLocalOnly()}
261  so the {@link android.support.v4.app.NotificationCompat.BigPictureStyle} notification won't show up
262  on other devices.
263</p>
264
265<p>
266  The following code example demonstrates how to build a recommendation.
267</p>
268
269<pre>
270public class RecommendationBuilder {
271    ...
272
273    public Notification build() throws IOException {
274        ...
275
276        Notification notification = new NotificationCompat.BigPictureStyle(
277                new NotificationCompat.Builder(mContext)
278                        .setContentTitle(mTitle)
279                        .setContentText(mDescription)
280                        .setPriority(mPriority)
281                        .setLocalOnly(true)
282                        .setOngoing(true)
283                        .setColor(mContext.getResources().getColor(R.color.fastlane_background))
284                        .setCategory(Notification.CATEGORY_RECOMMENDATION)
285                        .setLargeIcon(image)
286                        .setSmallIcon(mSmallIcon)
287                        .setContentIntent(mIntent)
288                        .setExtras(extras))
289                .build();
290
291        return notification;
292    }
293}
294</pre>
295
296<h2 id="run-service">Run Recommendations Service</h3>
297
298<p>
299  Your app's recommendation service must run periodically in order to create current
300  recommendations. To run your service, create a class that runs a timer and invokes
301  it at regular intervals. The following code example extends the {@link
302  android.content.BroadcastReceiver} class to start periodic execution of a recommendation service
303  every half hour:
304</p>
305
306<pre>
307public class BootupActivity extends BroadcastReceiver {
308    private static final String TAG = "BootupActivity";
309
310    private static final long INITIAL_DELAY = 5000;
311
312    &#64;Override
313    public void onReceive(Context context, Intent intent) {
314        Log.d(TAG, "BootupActivity initiated");
315        if (intent.getAction().endsWith(Intent.ACTION_BOOT_COMPLETED)) {
316            scheduleRecommendationUpdate(context);
317        }
318    }
319
320    private void scheduleRecommendationUpdate(Context context) {
321        Log.d(TAG, "Scheduling recommendations update");
322
323        AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
324        Intent recommendationIntent = new Intent(context, UpdateRecommendationsService.class);
325        PendingIntent alarmIntent = PendingIntent.getService(context, 0, recommendationIntent, 0);
326
327        alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
328                INITIAL_DELAY,
329                AlarmManager.INTERVAL_HALF_HOUR,
330                alarmIntent);
331    }
332}
333</pre>
334
335<p>
336  This implementation of the {@link android.content.BroadcastReceiver} class must run after start
337  up of the TV device where it is installed. To accomplish this, register this class in your app
338  manifest with an intent filter that listens for the completion of the device boot process. The
339  following sample code demonstrates how to add this configuration to the manifest:
340</p>
341
342<pre>
343&lt;manifest ... &gt;
344  &lt;application ... &gt;
345    &lt;receiver android:name=&quot;com.example.android.tvleanback.BootupActivity&quot;
346              android:enabled=&quot;true&quot;
347              android:exported=&quot;false&quot;&gt;
348      &lt;intent-filter&gt;
349        &lt;action android:name=&quot;android.intent.action.BOOT_COMPLETED&quot;/&gt;
350      &lt;/intent-filter&gt;
351    &lt;/receiver&gt;
352  &lt;/application&gt;
353&lt;/manifest&gt;
354</pre>
355
356<p class="note">
357  <strong>Important:</strong> Receiving a boot completed notification requires that your app
358  requests the {@link android.Manifest.permission#RECEIVE_BOOT_COMPLETED} permission.
359  For more information, see {@link android.content.Intent#ACTION_BOOT_COMPLETED}.
360</p>
361
362<p>In your recommendation service class' {@link android.app.IntentService#onHandleIntent(android.content.Intent)
363onHandleIntent()}
364method, post the recommendation to the manager as follows:</p>
365
366<pre>
367Notification notification = notificationBuilder.build();
368mNotificationManager.notify(id, notification);
369</pre>
370