1 page.title=Recommending TV Content 2 page.tags=tv, recommendations 3 helpoutsWidget=true 4 5 trainingnavtop=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 54 recommendations that are high-quality and relevant to users is an important factor in creating a 55 great user experience with your TV app. For this reason, you should carefully consider what 56 recommendations 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 61 suggest activities that extend that to related content. Here are some specific type of 62 recommendations you should consider:</p> 63 64 <ul> 65 <li><strong>Continuation content</strong> recommendations for the next episode for users to resume 66 watching a series. Or, use continuation recommendations for paused movies, TV shows, or podcasts 67 so 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 69 finished watching another series. Also, if your app lets users subscribe to, follow, or track 70 content, 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" 77 class="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 82 the recommendations to appear at the end of the recommendations row. Once a content item, such as a 83 movie, has been played, <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html#Removing"> 84 remove 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 89 elements such as the card's foreground and background image, color, app icon, title, and subtitle. 90 To learn more, see 91 <a href="https://www.google.com/design/spec-tv/system-overview/recommendation-row.html#recommendation-row-card-customization" 92 class="external-link">Recommendation Row</a> in the Android TV Design Spec.</p> 93 94 <h3 id="grouping">Grouping Recommendations</h3> 95 96 <p> 97 You can optionally group recommendations based on recommendation source. For example, your app 98 might provide two groups of recommendations: recommendations for content the user is subscribed to, 99 and recommendations for new trending content the user might not be aware of. 100 </p> 101 <p> 102 The system ranks and orders recommendations for each group separately when creating or updating 103 the recommendation row. By providing group information for your recommendations, you can ensure 104 that your recommendations dont get ordered below unrelated recommendations. 105 </p> 106 <p> 107 Use 108 {@link android.support.v4.app.NotificationCompat.Builder#setGroup 109 NotificationCompat.Builder.setGroup()} to set the group key string of a recommendation. For 110 example, to mark a recommendation as belonging to a group that contains new trending content, 111 you 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> 128 public 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 @Override 137 protected void onHandleIntent(Intent intent) { 138 Log.d(TAG, "Updating recommendation cards"); 139 HashMap<String, List<Movie>> 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<String, List<Movie>> 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 <manifest ... > 199 <application ... > 200 ... 201 202 <service 203 android:name="com.example.android.tvleanback.UpdateRecommendationsService" 204 android:enabled="true" /> 205 </application> 206 </manifest> 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 221 the builder pattern described as follows. First, you set the values of the recommendation card 222 elements.</p> 223 224 <pre> 225 public 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> 270 public 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> 307 public class BootupActivity extends BroadcastReceiver { 308 private static final String TAG = "BootupActivity"; 309 310 private static final long INITIAL_DELAY = 5000; 311 312 @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 <manifest ... > 344 <application ... > 345 <receiver android:name="com.example.android.tvleanback.BootupActivity" 346 android:enabled="true" 347 android:exported="false"> 348 <intent-filter> 349 <action android:name="android.intent.action.BOOT_COMPLETED"/> 350 </intent-filter> 351 </receiver> 352 </application> 353 </manifest> 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) 363 onHandleIntent()} 364 method, post the recommendation to the manager as follows:</p> 365 366 <pre> 367 Notification notification = notificationBuilder.build(); 368 mNotificationManager.notify(id, notification); 369 </pre> 370