This undoes the automerger skip which occured in commit e740c84dc32180214a7fd157105d6c18d30408ee and replays it as a standard (NOT -s ours) merge. Change-Id: If5a47be26f73d6a0735c425cd66310a3e2a89086
370 lines
14 KiB
Plaintext
370 lines
14 KiB
Plaintext
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. Or, use continuation recommendations for paused movies, TV shows, or podcasts
|
||
so users can get back to watching paused content in just a few clicks.</li>
|
||
<li><strong>New content</strong> recommendations, such as for a new first-run episode, if the user
|
||
finished watching another series. Also, if your app lets users subscribe to, follow, or track
|
||
content, use new content recommendations for unwatched items in their list of tracked content.</li>
|
||
<li><strong>Related content</strong> recommendations based on the users' historic viewing behavior.
|
||
</li>
|
||
</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>
|
||
|
||
<h3 id="grouping">Grouping Recommendations</h3>
|
||
|
||
<p>
|
||
You can optionally group recommendations based on recommendation source. For example, your app
|
||
might provide two groups of recommendations: recommendations for content the user is subscribed to,
|
||
and recommendations for new trending content the user might not be aware of.
|
||
</p>
|
||
<p>
|
||
The system ranks and orders recommendations for each group separately when creating or updating
|
||
the recommendation row. By providing group information for your recommendations, you can ensure
|
||
that your recommendations don’t get ordered below unrelated recommendations.
|
||
</p>
|
||
<p>
|
||
Use
|
||
{@link android.support.v4.app.NotificationCompat.Builder#setGroup
|
||
NotificationCompat.Builder.setGroup()} to set the group key string of a recommendation. For
|
||
example, to mark a recommendation as belonging to a group that contains new trending content,
|
||
you might call <code>setGroup("trending")</code>.
|
||
</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");
|
||
}
|
||
|
||
@Override
|
||
protected void onHandleIntent(Intent intent) {
|
||
Log.d(TAG, "Updating recommendation cards");
|
||
HashMap<String, List<Movie>> 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<String, List<Movie>> 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>
|
||
<manifest ... >
|
||
<application ... >
|
||
...
|
||
|
||
<service
|
||
android:name="com.example.android.tvleanback.UpdateRecommendationsService"
|
||
android:enabled="true" />
|
||
</application>
|
||
</manifest>
|
||
</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;
|
||
|
||
@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>
|
||
<manifest ... >
|
||
<application ... >
|
||
<receiver android:name="com.example.android.tvleanback.BootupActivity"
|
||
android:enabled="true"
|
||
android:exported="false">
|
||
<intent-filter>
|
||
<action android:name="android.intent.action.BOOT_COMPLETED"/>
|
||
</intent-filter>
|
||
</receiver>
|
||
</application>
|
||
</manifest>
|
||
</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>
|