[译]Recommendation card (推荐卡) Android TV 开发手册十一

版权声明:本文为博主原创翻译文章,转载请注明出处。

推荐: 欢迎关注我创建的Android TV 简书专题,会定期给大家分享一些AndroidTv相关的内容: www.jianshu.com/c/37efc6e97…


建议

Android TV的home,LeanbackLauncher在第一行有一个推荐行。任何应用程序都可以为用户建议推荐的内容。在本章中,我将介绍如何从您的应用程序向LeanbackLauncher显示推荐卡。

该建议是通过使用Android手机/平板电脑SDK中已经存在结构的通知框架实现的。所以在LeanbackLauncher中显示的建议其实是发送通知。基本顺序如下

1.声明NotificationManager 2.使用您的自定义的RecommendationBuilder类(它是usingNotificationCompat类的自定义类)来准备推荐。 3.通过RecommendationBuilder建立通知 4.使用NotificationManager通知此通知。

本章将要实现什么?

本章将实现两个新的类,“RecommendationBuilder”和“RecommendationFactory”。 RecommendationBuilder是为您的应用程序创建通知的自定义类。RecommendationFactory实际上是使用RecommendationBuilder创建通知。

在本章中,我们将通过单击MainFragment中的按钮发送通知(提出recommendation)。它将在RecommendationFactory中引用推荐方法来推荐。 (建议通常应该在服务中完成,但是我在onClick中实现了推荐,以便于理解。) ###RecommendationBuilder

首先,RecommendationBuilder如下。

import android.app.Notification;
import android.app.PendingIntent;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.support.v4.app.NotificationCompat;
import android.util.Log;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

/*
 * This class builds recommendations as notifications with videos as inputs.
 */
public class RecommendationBuilder {

    private static final String TAG = RecommendationBuilder.class.getSimpleName();
    private static final String BACKGROUND_URI_PREFIX = "content://com.corochann.androidtvapptutorial/";

    private Context mContext;

    private int mId;
    private int mPriority;
    private int mFastLaneColor;
    private int mSmallIcon;
    private String mTitle;
    private String mDescription;
    private Bitmap mCardImageBitmap;
    private String mBackgroundUri;
    private Bitmap mBackgroundBitmap;
    private String mGroupKey;
    private String mSort;
    private PendingIntent mIntent;


    public RecommendationBuilder(Context context) {
        mContext = context;
        // default fast lane color
        setFastLaneColor(mContext.getResources().getColor(R.color.fastlane_background));
    }

    public RecommendationBuilder setFastLaneColor(int color) {
        mFastLaneColor = color;
        return this;
    }

    /* context must not be null. It should be specified in constructor */
/*
    public RecommendationBuilder setContext(Context context) {
        mContext = context;
        return this;
    }
*/

    public RecommendationBuilder setId(int id) {
        mId = id;
        return this;
    }

    public RecommendationBuilder setPriority(int priority) {
        mPriority = priority;
        return this;
    }

    public RecommendationBuilder setTitle(String title) {
        mTitle = title;
        return this;
    }

    public RecommendationBuilder setDescription(String description) {
        mDescription = description;
        return this;
    }

    public RecommendationBuilder setBitmap(Bitmap bitmap) {
        mCardImageBitmap = bitmap;
        return this;
    }

    public RecommendationBuilder setBackground(String uri) {
        mBackgroundUri = uri;
        return this;
    }

    public RecommendationBuilder setBackground(Bitmap bitmap) {
        mBackgroundBitmap = bitmap;
        return this;
    }

    public RecommendationBuilder setIntent(PendingIntent intent) {
        mIntent = intent;
        return this;
    }

    public RecommendationBuilder setSmallIcon(int resourceId) {
        mSmallIcon = resourceId;
        return this;
    }

    public Notification build() {

        Bundle extras = new Bundle();
        File bitmapFile = getNotificationBackground(mContext, mId);

        if (mBackgroundBitmap != null) {
            Log.d(TAG, "making URI for mBackgroundBitmap");
            extras.putString(Notification.EXTRA_BACKGROUND_IMAGE_URI,
                    Uri.parse(BACKGROUND_URI_PREFIX + Integer.toString(mId)).toString());
        } else {
            Log.w(TAG, "mBackgroundBitmap is null");
        }

        // the following simulates group assignment into "Top", "Middle", "Bottom"
        // by checking mId and similarly sort order
        mGroupKey = (mId < 3) ? "Group1" : (mId < 5) ? "Group2" : "Group3";
        mSort = (mId < 3) ? "1.0" : (mId < 5) ? "0.7" : "0.3";

        // save bitmap into files for content provider to serve later
        try {
            bitmapFile.createNewFile();
            FileOutputStream fOut = new FileOutputStream(bitmapFile);
            mBackgroundBitmap.compress(Bitmap.CompressFormat.PNG, 85, fOut); //
复制代码

你可以将此ReccomendationBuilder类用作库。 在这种情况下,您不需要知道此实现的细节,因此您可以跳过阅读本节。 关于这个类的说明写在本节的剩余部分中。

最重要的部分是build()函数,它显示了如何使Android TV推荐通知实例。 推荐Android TV确实是通知! 它通过使用NotificationCompat.BigPictureStyle方法创建Notification类的实例。

public Notification build() {
 
        ...
 
        Notification notification = new NotificationCompat.BigPictureStyle(
                new NotificationCompat.Builder(mContext)
                        .setAutoCancel(true)
                        .setContentTitle(mTitle)
                        .setContentText(mDescription)
                        .setPriority(mPriority)
                        .setLocalOnly(true)
                        .setOngoing(true)
                        .setGroup(mGroupKey)
                        .setSortKey(mSort)
                        .setColor(mContext.getResources().getColor(R.color.fastlane_background))
                        .setCategory(Notification.CATEGORY_RECOMMENDATION)
                        .setLargeIcon(mCardImageBitmap)
                        .setSmallIcon(mSmallIcon)
                        .setContentIntent(mIntent)
                        .setExtras(extras))
                .build();
 
        Log.d(TAG, "Building notification - " + this.toString());
 
        return notification;
    }
复制代码

推荐卡中的许多功能及其相关部分是操作简便的。

此功能中最棘手的部分是设置背景图像。 当用户在LeanbackLauncher中选择推荐卡时,将显示背景图像。 该背景图像由“extra”字段指定,通过使用Bundle。 key是Notification. EXTRA_BACKGROUND_IMAGE_URI ,值为backgroundimage的URI。 请注意,您可以为其指定位图文件

  • smallicon - 用作应用/公司标志,显示在推荐卡的右下方。
  • 大图 - 用作推荐卡的主要图像。

但是您不能通过bitmap指定背景图像。 在这个实现中,我们通过使用内容提供者和指定这个缓存的背景图像的URI来缓存位图背景图像。

存储部分如下

public Notification build() {

        Bundle extras = new Bundle();
        File bitmapFile = getNotificationBackground(mContext, mId);

        ...

        // save bitmap into files for content provider to serve later
        try {
            bitmapFile.createNewFile();
            FileOutputStream fOut = new FileOutputStream(bitmapFile);
            mBackgroundBitmap.compress(Bitmap.CompressFormat.PNG, 85, fOut); // <- background bitmap must be created by mBackgroundUri, and not  mCardImageBitmap
            fOut.flush();
            fOut.close();
        } catch (IOException ioe) {
            Log.d(TAG, "Exception caught writing bitmap to file!", ioe);
        }
复制代码

获取背景图片URI的部分如下。 当选择推荐卡时,它将使用key为“Notification.EXTRA_BACKGROUND_IMAGE_URI”来引用额外的字段。 该值将发送到Content Provider的openFile方法。 因此,这些值在openFile方法中处理,以提取已存储的背景图像的文件路径。

    public Notification build() {

        Bundle extras = new Bundle();
        File bitmapFile = getNotificationBackground(mContext, mId);

        if (mBackgroundBitmap != null) {
            Log.d(TAG, "making URI for mBackgroundBitmap");
            extras.putString(Notification.EXTRA_BACKGROUND_IMAGE_URI,
                    Uri.parse(BACKGROUND_URI_PREFIX + Integer.toString(mId)).toString());
        } else {
            Log.w(TAG, "mBackgroundBitmap is null");
        }

    ...

    }

    public static class RecommendationBackgroundContentProvider extends ContentProvider {

        ...

        @Override
        /*
         * content provider serving files that are saved locally when recommendations are built
         */
        public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
            Log.i(TAG, "openFile");
            int backgroundId = Integer.parseInt(uri.getLastPathSegment());
            File bitmapFile = getNotificationBackground(getContext(), backgroundId);
            return ParcelFileDescriptor.open(bitmapFile, ParcelFileDescriptor.MODE_READ_ONLY);
        }
    }
复制代码

###RecommendationFactory RecommendationFactory类使用推荐构建器创建推荐电影项目的通知。 代码实现如下。

import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.TaskStackBuilder;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.support.v4.app.NotificationCompat;
import android.util.Log;

import com.squareup.picasso.Picasso;

import java.net.URI;

public class RecommendationFactory {

    private static final String TAG = RecommendationFactory.class.getSimpleName();
    private static final int CARD_WIDTH = 500;
    private static final int CARD_HEIGHT = 500;
    private static final int BACKGROUND_WIDTH = 1920;
    private static final int BACKGROUND_HEIGHT = 1080;

    private Context mContext;
    private NotificationManager mNotificationManager;

    public RecommendationFactory(Context context) {
        mContext = context;
        if (mNotificationManager == null) {
            mNotificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
        }
    }


    public void recommend(int id, Movie movie) {
        recommend(id, movie, NotificationCompat.PRIORITY_DEFAULT);
    }

    /**
     * create a notification for recommending item of Movie class
     * @param movie
     */
    public void recommend(final int id, final Movie movie, final int priority) {
        Log.i(TAG, "recommend");
        /* Run in background thread, since bitmap loading must be done in background */
        new Thread(new Runnable() {
            @Override
            public void run() {
                Log.i(TAG, "recommendation in progress");
                Bitmap backgroundBitmap = prepareBitmap(movie.getCardImageUrl(), BACKGROUND_WIDTH, BACKGROUND_HEIGHT);
                Bitmap cardImageBitmap = prepareBitmap(movie.getCardImageUrl(), CARD_WIDTH, CARD_HEIGHT);
                PendingIntent intent = buildPendingIntent(movie, id);

                RecommendationBuilder recommendationBuilder = new RecommendationBuilder(mContext)
                        .setSmallIcon(R.mipmap.ic_launcher)
                        .setBackground(backgroundBitmap)
                        .setId(id)
                        .setPriority(priority)
                        .setTitle(movie.getTitle())
                        .setDescription(movie.getDescription())
                        .setIntent(intent)
                        .setBitmap(cardImageBitmap)
                        .setFastLaneColor(mContext.getResources().getColor(R.color.fastlane_background));
                Notification recommendNotification = recommendationBuilder.build();
                mNotificationManager.notify(id, recommendNotification);
            }}).start();
    }

    /**
     * prepare bitmap from URL string
     * @param url
     * @return
     */
    public Bitmap prepareBitmap(String url, int width, int height) {
        Bitmap bitmap = null;
        try {
            URI uri = new URI(url);
            bitmap = Picasso.with(mContext)
                    .load(uri.toString())
                    .resize(width, height)
                    .get();
        } catch (Exception e) {
            Log.e(TAG, e.toString());
        }
        return bitmap;
    }

    private PendingIntent buildPendingIntent(Movie movie, int id) {
        Intent detailsIntent = new Intent(mContext, DetailsActivity.class);
        detailsIntent.putExtra(DetailsActivity.MOVIE, movie);
        detailsIntent.putExtra(DetailsActivity.NOTIFICATION_ID, id);

        TaskStackBuilder stackBuilder = TaskStackBuilder.create(mContext);
        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()));

        return stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
    }
}
复制代码

推荐方法是主要的方法,这里创建recommend Notification。 程序如下

1.声明NotificationManager 这是在RecommendationFactory的构造方法中完成的。

    public RecommendationFactory(Context context) {
        mContext = context;
        if (mNotificationManager == null) {
            mNotificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
        }
    }
复制代码

2.使用您的RecommendationBuilder类(它是usingNotificationCompat类的自定义类)来准备推荐。 每个功能的简要说明

  • Id - 此推荐卡的ID。
  • 优先级 - 设置优先级。高优先级卡更有可能在LeanbackLauncher的推荐行中显示。
  • 背景 - 设置背景位图,当用户选择推荐卡时将显示。
  • 标题 - 推荐卡的第一行文字。
  • 说明 - 推荐卡的第二行文字。
  • 位图 - 推荐卡的主要图像。
  • SmallIcon - 公司/图像图标。
  • FastLaneColor - 设置推荐卡中文本字段的背景颜色
  • 意图 - 设置PendingIntent(动作)以指示用户单击此推荐卡后会发生什么。在本示例中,buildPendingIntent方法用于创建意图。

3.通过创建RecommendationBuilder来创建通知。 4.使用NotificationManager通知此通知。 这些都是以recommend方法完成的。

    public void recommend(final int id, final Movie movie, final int priority) {
        Log.i(TAG, "recommend");
        /* Run in background thread, since bitmap loading must be done in background */
        new Thread(new Runnable() {
            @Override
            public void run() {
                Log.i(TAG, "recommendation in progress");
                Bitmap backgroundBitmap = prepareBitmap(movie.getCardImageUrl(), BACKGROUND_WIDTH, BACKGROUND_HEIGHT);
                Bitmap cardImageBitmap = prepareBitmap(movie.getCardImageUrl(), CARD_WIDTH, CARD_HEIGHT);
                PendingIntent intent = buildPendingIntent(movie, id);
                // 2 prepare recommendation
                RecommendationBuilder recommendationBuilder = new RecommendationBuilder(mContext)
                        .setSmallIcon(R.mipmap.ic_launcher)
                        .setBackground(backgroundBitmap)
                        .setId(id)
                        .setPriority(priority)
                        .setTitle(movie.getTitle())
                        .setDescription(movie.getDescription())
                        .setIntent(intent)
                        .setBitmap(cardImageBitmap)
                        .setFastLaneColor(mContext.getResources().getColor(R.color.fastlane_background));
                // 3 make notification
                Notification recommendNotification = recommendationBuilder.build();
                // 4 norify
                mNotificationManager.notify(id, recommendNotification);
            }}).start();
    }
复制代码

编译后运行

您可以点击“推荐”按钮发送推荐卡。

源代码在github上。

通过服务的方式运行推荐

推荐是应用程序建议用户浏览下一个动作的概念。 所以大多数时候,建议可能会在后台完成。 我将在剩下的时间跟进这一点。

官方文档推荐电视内容 - Android developers解释了在后台服务中如何推荐,Google的示例代码显示了后台服务使用的实际源代码实现。 在此示例源代码中,推荐服务将在获得“BOOT_COMPLETED”的意图之后启动,并且将每30分钟在后台执行推荐。

关注微信公众号,定期为你推荐移动开发相关文章。

转载于:https://juejin.im/post/5b714ae8f265da28364c30fa

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值