summaryrefslogtreecommitdiffstats
path: root/docs/html/training/tv/discovery/recommendations.jd
blob: ffe33f28bb000fd513e166373f4db9af1a2efdfc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
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="#best-practices">Best Practices for Recommendations</a></li>
    <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="best-practices">Best Practices for Recommendations</h2>

<p>Recommendations help users quickly find the content and apps they enjoy. Creating
recommendations that are high-quality and relevant to users is an important factor in creating a
great user experience with your TV app. For this reason, you should carefully consider what
recommendations you present to the user and manage them closely.</p>

<h3 id="types">Types of Recommendations</h3>

<p>When you create recommendations, you should link users back to incomplete viewing activities or
suggest activities that extend that to related content. Here are some specific type of
recommendations you should consider:</p>

<ul>
<li><strong>Continuation content</strong> recommendations for the next episode for users to resume
watching a series.</li>
<li><strong>New content</strong> recommendations, such as for a new first-run episode, if the user
finished watching another series.
<li><strong>Related content</strong> recommendations based on the users historic viewing behavior.
</ul>

<p>For more information on how to design recommendation cards for the best user experience, see
<a href="https://www.google.com/design/spec-tv/system-overview/recommendation-row.html#recommendation-row-types-of-recommendations"
class="external-link">Recommendation Row</a> in the Android TV Design Spec.</p>

<h3 id="refreshing">Refreshing Recommendations</h3>

<p>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>

<h3 id="customization">Customizing Recommendations</h3>

<p>You can customize recommendation cards to convey branding information, by setting user interface
elements such as the card's foreground and background image, color, app icon, title, and subtitle.
To learn more, see
<a href="https://www.google.com/design/spec-tv/system-overview/recommendation-row.html#recommendation-row-card-customization"
class="external-link">Recommendation Row</a> in the Android TV Design Spec.</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>

<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>