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 @Override 74 protected void onHandleIntent(Intent intent) { 75 Log.d(TAG, "Updating recommendation cards"); 76 HashMap<String, List<Movie>> 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<String, List<Movie>> 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<manifest ... > 136 <application ... > 137 ... 138 139 <service 140 android:name="com.example.android.tvleanback.UpdateRecommendationsService" 141 android:enabled="true" /> 142 </application> 143</manifest> 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 @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<manifest ... > 294 <application ... > 295 <receiver android:name="com.example.android.tvleanback.BootupActivity" 296 android:enabled="true" 297 android:exported="false"> 298 <intent-filter> 299 <action android:name="android.intent.action.BOOT_COMPLETED"/> 300 </intent-filter> 301 </receiver> 302 </application> 303</manifest> 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