Home | History | Annotate | Download | only in discovery
      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     &#64;Override
    137     protected void onHandleIntent(Intent intent) {
    138         Log.d(TAG, "Updating recommendation cards");
    139         HashMap&lt;String, List&lt;Movie&gt;&gt; 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&lt;String, List&lt;Movie&gt;&gt; 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 &lt;manifest ... &gt;
    199   &lt;application ... &gt;
    200     ...
    201 
    202     &lt;service
    203             android:name="com.example.android.tvleanback.UpdateRecommendationsService"
    204             android:enabled="true" /&gt;
    205   &lt;/application&gt;
    206 &lt;/manifest&gt;
    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     &#64;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 &lt;manifest ... &gt;
    344   &lt;application ... &gt;
    345     &lt;receiver android:name=&quot;com.example.android.tvleanback.BootupActivity&quot;
    346               android:enabled=&quot;true&quot;
    347               android:exported=&quot;false&quot;&gt;
    348       &lt;intent-filter&gt;
    349         &lt;action android:name=&quot;android.intent.action.BOOT_COMPLETED&quot;/&gt;
    350       &lt;/intent-filter&gt;
    351     &lt;/receiver&gt;
    352   &lt;/application&gt;
    353 &lt;/manifest&gt;
    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