推薦TV內容

編寫:awong1900 - 原文:http://developer.android.com/training/tv/discovery/recommendations.html

當操作TV時,用戶通常喜歡使用最少的輸入操作來找內容。許多用戶的理想場景是,坐下,打開TV然後觀看。用最少的步驟讓用戶觀看他們的喜歡的內容是最好的方式。

Android framework為了實現較少交互而提供了主屏幕推薦欄。在設備第一次使用時候,內容推薦出現在TV主屏幕的第一欄。應用程序的內容目錄提供推薦建議可以把用戶帶回到我們的應用。

home-recommendations

圖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);

下一節: 使TV應用是可被搜索的 >


书籍推荐