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="#service">Create a Recommendations Service</a></li>
14    <li><a href="#build">Build Recommendations</a></li>
15    <li><a href="#run-service">Run Recommendations Service</a></li>
16  </ol>
17  <h2>Try it out</h2>
18  <ul>
19    <li><a class="external-link" href="https://github.com/googlesamples/androidtv-Leanback">Android
20      Leanback sample app</a></li>
21  </ul>
22</div>
23</div>
24
25<p>
26  When interacting with TVs, users generally prefer to give minimal input before watching
27  content. An ideal scenario for many TV users is: sit down, turn on, and watch. The fewest steps
28  to get users to content they enjoy is generally the path they prefer.
29</p>
30
31<p>
32  The Android framework assists with minimum-input interaction by providing a recommendations row
33  on the home screen. Content recommendations appear as the first row of the TV home screen after
34  the first use of the device. Contributing recommendations from your app's content catalog can help
35  bring users back to your app.
36</p>
37
38<img src="{@docRoot}images/tv/home-recommendations.png" alt="" id="figure1" />
39<p class="img-caption">
40  <strong>Figure 1.</strong> An example of the recommendations row.
41</p>
42
43<p>
44  This lesson teaches you how to create recommendations and provide them to the Android framework
45  so users can easily discover and enjoy your app content. This discussion describes some code from
46  the <a class="external-link" href="https://github.com/googlesamples/androidtv-Leanback">Android
47  Leanback sample app</a>.
48</p>
49
50
51<h2 id="service">Create a Recommendations Service</h2>
52
53<p>
54  Content recommendations are created with background processing. In order for your application to
55  contribute to recommendations, create a service that periodically adds listings from your
56  app's catalog to the system's list of recommendations.
57</p>
58
59<p>
60  The following code example illustrates how to extend {@link android.app.IntentService} to
61  create a recommendation service for your application:
62</p>
63
64<pre>
65public class UpdateRecommendationsService extends IntentService {
66    private static final String TAG = "UpdateRecommendationsService";
67    private static final int MAX_RECOMMENDATIONS = 3;
68
69    public UpdateRecommendationsService() {
70        super("RecommendationService");
71    }
72
73    &#64;Override
74    protected void onHandleIntent(Intent intent) {
75        Log.d(TAG, "Updating recommendation cards");
76        HashMap&lt;String, List&lt;Movie&gt;&gt; recommendations = VideoProvider.getMovieList();
77        if (recommendations == null) return;
78
79        int count = 0;
80
81        try {
82            RecommendationBuilder builder = new RecommendationBuilder()
83                    .setContext(getApplicationContext())
84                    .setSmallIcon(R.drawable.videos_by_google_icon);
85
86            for (Map.Entry&lt;String, List&lt;Movie&gt;&gt; entry : recommendations.entrySet()) {
87                for (Movie movie : entry.getValue()) {
88                    Log.d(TAG, "Recommendation - " + movie.getTitle());
89
90                    builder.setBackground(movie.getCardImageUrl())
91                            .setId(count + 1)
92                            .setPriority(MAX_RECOMMENDATIONS - count)
93                            .setTitle(movie.getTitle())
94                            .setDescription(getString(R.string.popular_header))
95                            .setImage(movie.getCardImageUrl())
96                            .setIntent(buildPendingIntent(movie))
97                            .build();
98
99                    if (++count >= MAX_RECOMMENDATIONS) {
100                        break;
101                    }
102                }
103                if (++count >= MAX_RECOMMENDATIONS) {
104                    break;
105                }
106            }
107        } catch (IOException e) {
108            Log.e(TAG, "Unable to update recommendation", e);
109        }
110    }
111
112    private PendingIntent buildPendingIntent(Movie movie) {
113        Intent detailsIntent = new Intent(this, DetailsActivity.class);
114        detailsIntent.putExtra("Movie", movie);
115
116        TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
117        stackBuilder.addParentStack(DetailsActivity.class);
118        stackBuilder.addNextIntent(detailsIntent);
119        // Ensure a unique PendingIntents, otherwise all recommendations end up with the same
120        // PendingIntent
121        detailsIntent.setAction(Long.toString(movie.getId()));
122
123        PendingIntent intent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
124        return intent;
125    }
126}
127</pre>
128
129<p>
130  In order for this service to be recognized by the system and run, register it using your
131  app manifest. The following code snippet illustrates how to declare this class as a service:
132</p>
133
134<pre>
135&lt;manifest ... &gt;
136  &lt;application ... &gt;
137    ...
138
139    &lt;service
140            android:name="com.example.android.tvleanback.UpdateRecommendationsService"
141            android:enabled="true" /&gt;
142  &lt;/application&gt;
143&lt;/manifest&gt;
144</pre>
145
146<h3 id="refreshing">Refreshing Recommendations</h3>
147
148<p>Base your recommendations on user behavior and data such as play lists, wish lists, and associated
149content. When refreshing recommendations, don't just remove and repost them, because doing so causes
150the recommendations to appear at the end of the recommendations row. Once a content item, such as a
151movie, has been played, <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html#Removing">
152remove it</a> from the recommendations.</p>
153
154<p>The order of an app's recommendations is preserved according to the order in which the app
155provides them. The framework interleaves app recommendations based on recommendation quality,
156as measured by user behavior. Better recommendations make an app's recommendations more likely
157to appear near the front of the list.</p>
158
159<h2 id="build">Build Recommendations</h2>
160
161<p>
162  Once your recommendation service starts running, it must create recommendations and pass them to
163  the Android framework. The framework receives the recommendations as {@link
164  android.app.Notification} objects that use a specific template and are marked with a specific
165  category.
166</p>
167
168<h3 id="setting-ui">Setting the Values</h3>
169
170<p>To set the UI element values for the recommendation card, you create a builder class that follows
171the builder pattern described as follows. First, you set the values of the recommendation card
172elements.</p>
173
174<pre>
175public class RecommendationBuilder {
176    ...
177
178    public RecommendationBuilder setTitle(String title) {
179            mTitle = title;
180            return this;
181        }
182
183        public RecommendationBuilder setDescription(String description) {
184            mDescription = description;
185            return this;
186        }
187
188        public RecommendationBuilder setImage(String uri) {
189            mImageUri = uri;
190            return this;
191        }
192
193        public RecommendationBuilder setBackground(String uri) {
194            mBackgroundUri = uri;
195            return this;
196        }
197...
198</pre>
199
200<h3 id="create-notification">Creating the Notification</h3>
201
202<p>
203  Once you've set the values, you then build the notification, assigning the values from the builder
204  class to the notification, and calling {@link android.support.v4.app.NotificationCompat.Builder#build()
205  NotificationCompat.Builder.build()}.
206</p>
207
208<p>
209  Also, be sure to call
210  {@link android.support.v4.app.NotificationCompat.Builder#setLocalOnly(boolean) setLocalOnly()}
211  so the {@link android.support.v4.app.NotificationCompat.BigPictureStyle} notification won't show up
212  on other devices.
213</p>
214
215<p>
216  The following code example demonstrates how to build a recommendation.
217</p>
218
219<pre>
220public class RecommendationBuilder {
221    ...
222
223    public Notification build() throws IOException {
224        ...
225
226        Notification notification = new NotificationCompat.BigPictureStyle(
227                new NotificationCompat.Builder(mContext)
228                        .setContentTitle(mTitle)
229                        .setContentText(mDescription)
230                        .setPriority(mPriority)
231                        .setLocalOnly(true)
232                        .setOngoing(true)
233                        .setColor(mContext.getResources().getColor(R.color.fastlane_background))
234                        .setCategory(Notification.CATEGORY_RECOMMENDATION)
235                        .setLargeIcon(image)
236                        .setSmallIcon(mSmallIcon)
237                        .setContentIntent(mIntent)
238                        .setExtras(extras))
239                .build();
240
241        return notification;
242    }
243}
244</pre>
245
246<h2 id="run-service">Run Recommendations Service</h3>
247
248<p>
249  Your app's recommendation service must run periodically in order to create current
250  recommendations. To run your service, create a class that runs a timer and invokes
251  it at regular intervals. The following code example extends the {@link
252  android.content.BroadcastReceiver} class to start periodic execution of a recommendation service
253  every half hour:
254</p>
255
256<pre>
257public class BootupActivity extends BroadcastReceiver {
258    private static final String TAG = "BootupActivity";
259
260    private static final long INITIAL_DELAY = 5000;
261
262    &#64;Override
263    public void onReceive(Context context, Intent intent) {
264        Log.d(TAG, "BootupActivity initiated");
265        if (intent.getAction().endsWith(Intent.ACTION_BOOT_COMPLETED)) {
266            scheduleRecommendationUpdate(context);
267        }
268    }
269
270    private void scheduleRecommendationUpdate(Context context) {
271        Log.d(TAG, "Scheduling recommendations update");
272
273        AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
274        Intent recommendationIntent = new Intent(context, UpdateRecommendationsService.class);
275        PendingIntent alarmIntent = PendingIntent.getService(context, 0, recommendationIntent, 0);
276
277        alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
278                INITIAL_DELAY,
279                AlarmManager.INTERVAL_HALF_HOUR,
280                alarmIntent);
281    }
282}
283</pre>
284
285<p>
286  This implementation of the {@link android.content.BroadcastReceiver} class must run after start
287  up of the TV device where it is installed. To accomplish this, register this class in your app
288  manifest with an intent filter that listens for the completion of the device boot process. The
289  following sample code demonstrates how to add this configuration to the manifest:
290</p>
291
292<pre>
293&lt;manifest ... &gt;
294  &lt;application ... &gt;
295    &lt;receiver android:name=&quot;com.example.android.tvleanback.BootupActivity&quot;
296              android:enabled=&quot;true&quot;
297              android:exported=&quot;false&quot;&gt;
298      &lt;intent-filter&gt;
299        &lt;action android:name=&quot;android.intent.action.BOOT_COMPLETED&quot;/&gt;
300      &lt;/intent-filter&gt;
301    &lt;/receiver&gt;
302  &lt;/application&gt;
303&lt;/manifest&gt;
304</pre>
305
306<p class="note">
307  <strong>Important:</strong> Receiving a boot completed notification requires that your app
308  requests the {@link android.Manifest.permission#RECEIVE_BOOT_COMPLETED} permission.
309  For more information, see {@link android.content.Intent#ACTION_BOOT_COMPLETED}.
310</p>
311
312<p>In your recommendation service class' {@link android.app.IntentService#onHandleIntent(android.content.Intent)
313onHandleIntent()}
314method, post the recommendation to the manager as follows:</p>
315
316<pre>
317Notification notification = notificationBuilder.build();
318mNotificationManager.notify(id, notification);
319</pre>
320