編寫:awong1900 - 原文:http://developer.android.com/training/tv/discovery/recommendations.html
當操作TV時,用戶通常喜歡使用最少的輸入操作來找內容。許多用戶的理想場景是,坐下,打開TV然後觀看。用最少的步驟讓用戶觀看他們的喜歡的內容是最好的方式。
Android framework為了實現較少交互而提供了主屏幕推薦欄。在設備第一次使用時候,內容推薦出現在TV主屏幕的第一欄。應用程序的內容目錄提供推薦建議可以把用戶帶回到我們的應用。
圖1. 一個推薦欄的例子
這節課教我們如何創建推薦和提供他們到Android framework,這樣用戶能容易的發現和使用我們的應用內容。這個討論描述了一些代碼,在Android Leanback示例代碼。
內容推薦是被後臺處理創建。為了把我們的應用提供到內容推薦,創建一個週期性添加列表服務,從應用目錄到系統推薦列表。
接下來的代碼描繪瞭如何擴展IntentService為我們的應用創建推薦服務:
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;
}
}
使服務被系統意識和運行,在應用manifest中註冊它,接下來的代碼片段展示瞭如何定義這個類做為服務:
<manifest ... >
<application ... >
...
<service
android:name="com.example.android.tvleanback.UpdateRecommendationsService"
android:enabled="true" />
</application>
</manifest>
基於用戶的行為和數據來推薦,例如播放列表,喜愛列表和相關內容。當刷新推薦時,不僅僅是刪除和重新加載他們,因為這樣會導致推薦出現在推薦欄的結尾。一旦一個內容項被播放,如一個影片,從推薦中刪除它。
應用的推薦順序被保存依據應用提供他們的順序。framework interleave應用推薦基於推薦質量,用戶習慣的收集。最好的推薦應是推薦最合適的出現在列表前面。
一旦我們的推薦服務開始運行,它必須創建推薦和推送他們到Android framework。Framework收到推薦作為通知對象。它用特定的模板並且標記為特定的目錄。
去設置推薦卡片的UI元素,創建一個builder類用接下來的builder樣式描述。首先,設置推薦卡片元素的值。
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;
}
...
一旦我們設置了值,然後去創建通知,從builder類分配值到通知,並且調用NotificationCompat.Builder.build。
並且,確信調用setLocalOnly(),這樣NotificationCompat.BigPictureStyle通知不將顯示在另一個設備。
接下來的代碼示例展示瞭如何創建推薦。
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;
}
}
我們的應用推薦服務必須週期性運行確保創建當前的推薦。去運行我們的服務,創建一個類運行計時器和在週期間隔關聯它。接下來的代碼例子擴展了BroadcastReceiver類去開始每半小時的推薦服務的週期性執行:
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);
}
}
這個BroadcastReceiver類的實現必須運行在TV設備啟動後。 為了完成這個,註冊這個類在應用manifest的intet filter中,它監聽設備啟動完成。接下來的代碼展示瞭如何添加這個配置到manifest。
<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>
Important: 接收一個啟動完成通知需要我們的應用有RECEIVE_BOOT_COMPLETED權限。更多信息,查看ACTION_BOOT_COMPLETED。
在推薦服務類的onHandleIntent()方法中,用以下代碼提交推薦到管理器:
Notification notification = notificationBuilder.build();
mNotificationManager.notify(id, notification);