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.</li>
     67 <li><strong>New content</strong> recommendations, such as for a new first-run episode, if the user
     68 finished watching another series.
     69 <li><strong>Related content</strong> recommendations based on the users historic viewing behavior.
     70 </ul>
     71 
     72 <p>For more information on how to design recommendation cards for the best user experience, see
     73 <a href="https://www.google.com/design/spec-tv/system-overview/recommendation-row.html#recommendation-row-types-of-recommendations"
     74 class="external-link">Recommendation Row</a> in the Android TV Design Spec.</p>
     75 
     76 <h3 id="refreshing">Refreshing Recommendations</h3>
     77 
     78 <p>When refreshing recommendations, don't just remove and repost them, because doing so causes
     79 the recommendations to appear at the end of the recommendations row. Once a content item, such as a
     80 movie, has been played, <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html#Removing">
     81 remove it</a> from the recommendations.</p>
     82 
     83 <h3 id="customization">Customizing Recommendations</h3>
     84 
     85 <p>You can customize recommendation cards to convey branding information, by setting user interface
     86 elements such as the card's foreground and background image, color, app icon, title, and subtitle.
     87 To learn more, see
     88 <a href="https://www.google.com/design/spec-tv/system-overview/recommendation-row.html#recommendation-row-card-customization"
     89 class="external-link">Recommendation Row</a> in the Android TV Design Spec.</p>
     90 
     91 
     92 <h2 id="service">Create a Recommendations Service</h2>
     93 
     94 <p>
     95   Content recommendations are created with background processing. In order for your application to
     96   contribute to recommendations, create a service that periodically adds listings from your
     97   app's catalog to the system's list of recommendations.
     98 </p>
     99 
    100 <p>
    101   The following code example illustrates how to extend {@link android.app.IntentService} to
    102   create a recommendation service for your application:
    103 </p>
    104 
    105 <pre>
    106 public class UpdateRecommendationsService extends IntentService {
    107     private static final String TAG = "UpdateRecommendationsService";
    108     private static final int MAX_RECOMMENDATIONS = 3;
    109 
    110     public UpdateRecommendationsService() {
    111         super("RecommendationService");
    112     }
    113 
    114     &#64;Override
    115     protected void onHandleIntent(Intent intent) {
    116         Log.d(TAG, "Updating recommendation cards");
    117         HashMap&lt;String, List&lt;Movie&gt;&gt; recommendations = VideoProvider.getMovieList();
    118         if (recommendations == null) return;
    119 
    120         int count = 0;
    121 
    122         try {
    123             RecommendationBuilder builder = new RecommendationBuilder()
    124                     .setContext(getApplicationContext())
    125                     .setSmallIcon(R.drawable.videos_by_google_icon);
    126 
    127             for (Map.Entry&lt;String, List&lt;Movie&gt;&gt; entry : recommendations.entrySet()) {
    128                 for (Movie movie : entry.getValue()) {
    129                     Log.d(TAG, "Recommendation - " + movie.getTitle());
    130 
    131                     builder.setBackground(movie.getCardImageUrl())
    132                             .setId(count + 1)
    133                             .setPriority(MAX_RECOMMENDATIONS - count)
    134                             .setTitle(movie.getTitle())
    135                             .setDescription(getString(R.string.popular_header))
    136                             .setImage(movie.getCardImageUrl())
    137                             .setIntent(buildPendingIntent(movie))
    138                             .build();
    139 
    140                     if (++count >= MAX_RECOMMENDATIONS) {
    141                         break;
    142                     }
    143                 }
    144                 if (++count >= MAX_RECOMMENDATIONS) {
    145                     break;
    146                 }
    147             }
    148         } catch (IOException e) {
    149             Log.e(TAG, "Unable to update recommendation", e);
    150         }
    151     }
    152 
    153     private PendingIntent buildPendingIntent(Movie movie) {
    154         Intent detailsIntent = new Intent(this, DetailsActivity.class);
    155         detailsIntent.putExtra("Movie", movie);
    156 
    157         TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
    158         stackBuilder.addParentStack(DetailsActivity.class);
    159         stackBuilder.addNextIntent(detailsIntent);
    160         // Ensure a unique PendingIntents, otherwise all
    161         // recommendations end up with the same PendingIntent
    162         detailsIntent.setAction(Long.toString(movie.getId()));
    163 
    164         PendingIntent intent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
    165         return intent;
    166     }
    167 }
    168 </pre>
    169 
    170 <p>
    171   In order for this service to be recognized by the system and run, register it using your
    172   app manifest. The following code snippet illustrates how to declare this class as a service:
    173 </p>
    174 
    175 <pre>
    176 &lt;manifest ... &gt;
    177   &lt;application ... &gt;
    178     ...
    179 
    180     &lt;service
    181             android:name="com.example.android.tvleanback.UpdateRecommendationsService"
    182             android:enabled="true" /&gt;
    183   &lt;/application&gt;
    184 &lt;/manifest&gt;
    185 </pre>
    186 
    187 <h2 id="build">Build Recommendations</h2>
    188 
    189 <p>
    190   Once your recommendation service starts running, it must create recommendations and pass them to
    191   the Android framework. The framework receives the recommendations as {@link
    192   android.app.Notification} objects that use a specific template and are marked with a specific
    193   category.
    194 </p>
    195 
    196 <h3 id="setting-ui">Setting the Values</h3>
    197 
    198 <p>To set the UI element values for the recommendation card, you create a builder class that follows
    199 the builder pattern described as follows. First, you set the values of the recommendation card
    200 elements.</p>
    201 
    202 <pre>
    203 public class RecommendationBuilder {
    204     ...
    205 
    206     public RecommendationBuilder setTitle(String title) {
    207             mTitle = title;
    208             return this;
    209         }
    210 
    211         public RecommendationBuilder setDescription(String description) {
    212             mDescription = description;
    213             return this;
    214         }
    215 
    216         public RecommendationBuilder setImage(String uri) {
    217             mImageUri = uri;
    218             return this;
    219         }
    220 
    221         public RecommendationBuilder setBackground(String uri) {
    222             mBackgroundUri = uri;
    223             return this;
    224         }
    225 ...
    226 </pre>
    227 
    228 <h3 id="create-notification">Creating the Notification</h3>
    229 
    230 <p>
    231   Once you've set the values, you then build the notification, assigning the values from the builder
    232   class to the notification, and calling {@link android.support.v4.app.NotificationCompat.Builder#build()
    233   NotificationCompat.Builder.build()}.
    234 </p>
    235 
    236 <p>
    237   Also, be sure to call
    238   {@link android.support.v4.app.NotificationCompat.Builder#setLocalOnly(boolean) setLocalOnly()}
    239   so the {@link android.support.v4.app.NotificationCompat.BigPictureStyle} notification won't show up
    240   on other devices.
    241 </p>
    242 
    243 <p>
    244   The following code example demonstrates how to build a recommendation.
    245 </p>
    246 
    247 <pre>
    248 public class RecommendationBuilder {
    249     ...
    250 
    251     public Notification build() throws IOException {
    252         ...
    253 
    254         Notification notification = new NotificationCompat.BigPictureStyle(
    255                 new NotificationCompat.Builder(mContext)
    256                         .setContentTitle(mTitle)
    257                         .setContentText(mDescription)
    258                         .setPriority(mPriority)
    259                         .setLocalOnly(true)
    260                         .setOngoing(true)
    261                         .setColor(mContext.getResources().getColor(R.color.fastlane_background))
    262                         .setCategory(Notification.CATEGORY_RECOMMENDATION)
    263                         .setLargeIcon(image)
    264                         .setSmallIcon(mSmallIcon)
    265                         .setContentIntent(mIntent)
    266                         .setExtras(extras))
    267                 .build();
    268 
    269         return notification;
    270     }
    271 }
    272 </pre>
    273 
    274 <h2 id="run-service">Run Recommendations Service</h3>
    275 
    276 <p>
    277   Your app's recommendation service must run periodically in order to create current
    278   recommendations. To run your service, create a class that runs a timer and invokes
    279   it at regular intervals. The following code example extends the {@link
    280   android.content.BroadcastReceiver} class to start periodic execution of a recommendation service
    281   every half hour:
    282 </p>
    283 
    284 <pre>
    285 public class BootupActivity extends BroadcastReceiver {
    286     private static final String TAG = "BootupActivity";
    287 
    288     private static final long INITIAL_DELAY = 5000;
    289 
    290     &#64;Override
    291     public void onReceive(Context context, Intent intent) {
    292         Log.d(TAG, "BootupActivity initiated");
    293         if (intent.getAction().endsWith(Intent.ACTION_BOOT_COMPLETED)) {
    294             scheduleRecommendationUpdate(context);
    295         }
    296     }
    297 
    298     private void scheduleRecommendationUpdate(Context context) {
    299         Log.d(TAG, "Scheduling recommendations update");
    300 
    301         AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
    302         Intent recommendationIntent = new Intent(context, UpdateRecommendationsService.class);
    303         PendingIntent alarmIntent = PendingIntent.getService(context, 0, recommendationIntent, 0);
    304 
    305         alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
    306                 INITIAL_DELAY,
    307                 AlarmManager.INTERVAL_HALF_HOUR,
    308                 alarmIntent);
    309     }
    310 }
    311 </pre>
    312 
    313 <p>
    314   This implementation of the {@link android.content.BroadcastReceiver} class must run after start
    315   up of the TV device where it is installed. To accomplish this, register this class in your app
    316   manifest with an intent filter that listens for the completion of the device boot process. The
    317   following sample code demonstrates how to add this configuration to the manifest:
    318 </p>
    319 
    320 <pre>
    321 &lt;manifest ... &gt;
    322   &lt;application ... &gt;
    323     &lt;receiver android:name=&quot;com.example.android.tvleanback.BootupActivity&quot;
    324               android:enabled=&quot;true&quot;
    325               android:exported=&quot;false&quot;&gt;
    326       &lt;intent-filter&gt;
    327         &lt;action android:name=&quot;android.intent.action.BOOT_COMPLETED&quot;/&gt;
    328       &lt;/intent-filter&gt;
    329     &lt;/receiver&gt;
    330   &lt;/application&gt;
    331 &lt;/manifest&gt;
    332 </pre>
    333 
    334 <p class="note">
    335   <strong>Important:</strong> Receiving a boot completed notification requires that your app
    336   requests the {@link android.Manifest.permission#RECEIVE_BOOT_COMPLETED} permission.
    337   For more information, see {@link android.content.Intent#ACTION_BOOT_COMPLETED}.
    338 </p>
    339 
    340 <p>In your recommendation service class' {@link android.app.IntentService#onHandleIntent(android.content.Intent)
    341 onHandleIntent()}
    342 method, post the recommendation to the manager as follows:</p>
    343 
    344 <pre>
    345 Notification notification = notificationBuilder.build();
    346 mNotificationManager.notify(id, notification);
    347 </pre>
    348