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.</li>
67<li><strong>New content</strong> recommendations, such as for a new first-run episode, if the user
68finished watching another series.
69<li><strong>Related content</strong> recommendations based on the users historic viewing behavior.
70</ul>
71
72<p>For more information on how to design recommendation cards for the best user experience, see
73<a href="https://www.google.com/design/spec-tv/system-overview/recommendation-row.html#recommendation-row-types-of-recommendations"
74class="external-link">Recommendation Row</a> in the Android TV Design Spec.</p>
75
76<h3 id="refreshing">Refreshing Recommendations</h3>
77
78<p>When refreshing recommendations, don't just remove and repost them, because doing so causes
79the recommendations to appear at the end of the recommendations row. Once a content item, such as a
80movie, has been played, <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html#Removing">
81remove it</a> from the recommendations.</p>
82
83<h3 id="customization">Customizing Recommendations</h3>
84
85<p>You can customize recommendation cards to convey branding information, by setting user interface
86elements such as the card's foreground and background image, color, app icon, title, and subtitle.
87To learn more, see
88<a href="https://www.google.com/design/spec-tv/system-overview/recommendation-row.html#recommendation-row-card-customization"
89class="external-link">Recommendation Row</a> in the Android TV Design Spec.</p>
90
91
92<h2 id="service">Create a Recommendations Service</h2>
93
94<p>
95  Content recommendations are created with background processing. In order for your application to
96  contribute to recommendations, create a service that periodically adds listings from your
97  app's catalog to the system's list of recommendations.
98</p>
99
100<p>
101  The following code example illustrates how to extend {@link android.app.IntentService} to
102  create a recommendation service for your application:
103</p>
104
105<pre>
106public class UpdateRecommendationsService extends IntentService {
107    private static final String TAG = "UpdateRecommendationsService";
108    private static final int MAX_RECOMMENDATIONS = 3;
109
110    public UpdateRecommendationsService() {
111        super("RecommendationService");
112    }
113
114    &#64;Override
115    protected void onHandleIntent(Intent intent) {
116        Log.d(TAG, "Updating recommendation cards");
117        HashMap&lt;String, List&lt;Movie&gt;&gt; recommendations = VideoProvider.getMovieList();
118        if (recommendations == null) return;
119
120        int count = 0;
121
122        try {
123            RecommendationBuilder builder = new RecommendationBuilder()
124                    .setContext(getApplicationContext())
125                    .setSmallIcon(R.drawable.videos_by_google_icon);
126
127            for (Map.Entry&lt;String, List&lt;Movie&gt;&gt; entry : recommendations.entrySet()) {
128                for (Movie movie : entry.getValue()) {
129                    Log.d(TAG, "Recommendation - " + movie.getTitle());
130
131                    builder.setBackground(movie.getCardImageUrl())
132                            .setId(count + 1)
133                            .setPriority(MAX_RECOMMENDATIONS - count)
134                            .setTitle(movie.getTitle())
135                            .setDescription(getString(R.string.popular_header))
136                            .setImage(movie.getCardImageUrl())
137                            .setIntent(buildPendingIntent(movie))
138                            .build();
139
140                    if (++count >= MAX_RECOMMENDATIONS) {
141                        break;
142                    }
143                }
144                if (++count >= MAX_RECOMMENDATIONS) {
145                    break;
146                }
147            }
148        } catch (IOException e) {
149            Log.e(TAG, "Unable to update recommendation", e);
150        }
151    }
152
153    private PendingIntent buildPendingIntent(Movie movie) {
154        Intent detailsIntent = new Intent(this, DetailsActivity.class);
155        detailsIntent.putExtra("Movie", movie);
156
157        TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
158        stackBuilder.addParentStack(DetailsActivity.class);
159        stackBuilder.addNextIntent(detailsIntent);
160        // Ensure a unique PendingIntents, otherwise all
161        // recommendations end up with the same PendingIntent
162        detailsIntent.setAction(Long.toString(movie.getId()));
163
164        PendingIntent intent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
165        return intent;
166    }
167}
168</pre>
169
170<p>
171  In order for this service to be recognized by the system and run, register it using your
172  app manifest. The following code snippet illustrates how to declare this class as a service:
173</p>
174
175<pre>
176&lt;manifest ... &gt;
177  &lt;application ... &gt;
178    ...
179
180    &lt;service
181            android:name="com.example.android.tvleanback.UpdateRecommendationsService"
182            android:enabled="true" /&gt;
183  &lt;/application&gt;
184&lt;/manifest&gt;
185</pre>
186
187<h2 id="build">Build Recommendations</h2>
188
189<p>
190  Once your recommendation service starts running, it must create recommendations and pass them to
191  the Android framework. The framework receives the recommendations as {@link
192  android.app.Notification} objects that use a specific template and are marked with a specific
193  category.
194</p>
195
196<h3 id="setting-ui">Setting the Values</h3>
197
198<p>To set the UI element values for the recommendation card, you create a builder class that follows
199the builder pattern described as follows. First, you set the values of the recommendation card
200elements.</p>
201
202<pre>
203public class RecommendationBuilder {
204    ...
205
206    public RecommendationBuilder setTitle(String title) {
207            mTitle = title;
208            return this;
209        }
210
211        public RecommendationBuilder setDescription(String description) {
212            mDescription = description;
213            return this;
214        }
215
216        public RecommendationBuilder setImage(String uri) {
217            mImageUri = uri;
218            return this;
219        }
220
221        public RecommendationBuilder setBackground(String uri) {
222            mBackgroundUri = uri;
223            return this;
224        }
225...
226</pre>
227
228<h3 id="create-notification">Creating the Notification</h3>
229
230<p>
231  Once you've set the values, you then build the notification, assigning the values from the builder
232  class to the notification, and calling {@link android.support.v4.app.NotificationCompat.Builder#build()
233  NotificationCompat.Builder.build()}.
234</p>
235
236<p>
237  Also, be sure to call
238  {@link android.support.v4.app.NotificationCompat.Builder#setLocalOnly(boolean) setLocalOnly()}
239  so the {@link android.support.v4.app.NotificationCompat.BigPictureStyle} notification won't show up
240  on other devices.
241</p>
242
243<p>
244  The following code example demonstrates how to build a recommendation.
245</p>
246
247<pre>
248public class RecommendationBuilder {
249    ...
250
251    public Notification build() throws IOException {
252        ...
253
254        Notification notification = new NotificationCompat.BigPictureStyle(
255                new NotificationCompat.Builder(mContext)
256                        .setContentTitle(mTitle)
257                        .setContentText(mDescription)
258                        .setPriority(mPriority)
259                        .setLocalOnly(true)
260                        .setOngoing(true)
261                        .setColor(mContext.getResources().getColor(R.color.fastlane_background))
262                        .setCategory(Notification.CATEGORY_RECOMMENDATION)
263                        .setLargeIcon(image)
264                        .setSmallIcon(mSmallIcon)
265                        .setContentIntent(mIntent)
266                        .setExtras(extras))
267                .build();
268
269        return notification;
270    }
271}
272</pre>
273
274<h2 id="run-service">Run Recommendations Service</h3>
275
276<p>
277  Your app's recommendation service must run periodically in order to create current
278  recommendations. To run your service, create a class that runs a timer and invokes
279  it at regular intervals. The following code example extends the {@link
280  android.content.BroadcastReceiver} class to start periodic execution of a recommendation service
281  every half hour:
282</p>
283
284<pre>
285public class BootupActivity extends BroadcastReceiver {
286    private static final String TAG = "BootupActivity";
287
288    private static final long INITIAL_DELAY = 5000;
289
290    &#64;Override
291    public void onReceive(Context context, Intent intent) {
292        Log.d(TAG, "BootupActivity initiated");
293        if (intent.getAction().endsWith(Intent.ACTION_BOOT_COMPLETED)) {
294            scheduleRecommendationUpdate(context);
295        }
296    }
297
298    private void scheduleRecommendationUpdate(Context context) {
299        Log.d(TAG, "Scheduling recommendations update");
300
301        AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
302        Intent recommendationIntent = new Intent(context, UpdateRecommendationsService.class);
303        PendingIntent alarmIntent = PendingIntent.getService(context, 0, recommendationIntent, 0);
304
305        alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
306                INITIAL_DELAY,
307                AlarmManager.INTERVAL_HALF_HOUR,
308                alarmIntent);
309    }
310}
311</pre>
312
313<p>
314  This implementation of the {@link android.content.BroadcastReceiver} class must run after start
315  up of the TV device where it is installed. To accomplish this, register this class in your app
316  manifest with an intent filter that listens for the completion of the device boot process. The
317  following sample code demonstrates how to add this configuration to the manifest:
318</p>
319
320<pre>
321&lt;manifest ... &gt;
322  &lt;application ... &gt;
323    &lt;receiver android:name=&quot;com.example.android.tvleanback.BootupActivity&quot;
324              android:enabled=&quot;true&quot;
325              android:exported=&quot;false&quot;&gt;
326      &lt;intent-filter&gt;
327        &lt;action android:name=&quot;android.intent.action.BOOT_COMPLETED&quot;/&gt;
328      &lt;/intent-filter&gt;
329    &lt;/receiver&gt;
330  &lt;/application&gt;
331&lt;/manifest&gt;
332</pre>
333
334<p class="note">
335  <strong>Important:</strong> Receiving a boot completed notification requires that your app
336  requests the {@link android.Manifest.permission#RECEIVE_BOOT_COMPLETED} permission.
337  For more information, see {@link android.content.Intent#ACTION_BOOT_COMPLETED}.
338</p>
339
340<p>In your recommendation service class' {@link android.app.IntentService#onHandleIntent(android.content.Intent)
341onHandleIntent()}
342method, post the recommendation to the manager as follows:</p>
343
344<pre>
345Notification notification = notificationBuilder.build();
346mNotificationManager.notify(id, notification);
347</pre>
348