AppLinkCardView.java revision d41f0075a7d2ea826204e81fcec57d0aa57171a9
1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.tv.menu;
18
19import android.content.Context;
20import android.content.Intent;
21import android.content.pm.ApplicationInfo;
22import android.content.pm.PackageManager;
23import android.graphics.Bitmap;
24import android.graphics.Canvas;
25import android.graphics.drawable.BitmapDrawable;
26import android.graphics.drawable.Drawable;
27import android.support.annotation.Nullable;
28import android.support.v7.graphics.Palette;
29import android.text.TextUtils;
30import android.util.AttributeSet;
31import android.util.Log;
32import android.view.View;
33import android.widget.ImageView;
34import android.widget.TextView;
35
36import com.android.tv.MainActivity;
37import com.android.tv.R;
38import com.android.tv.data.Channel;
39import com.android.tv.util.BitmapUtils;
40import com.android.tv.util.ImageLoader;
41import com.android.tv.util.TvInputManagerHelper;
42
43/**
44 * A view to render an app link card.
45 */
46public class AppLinkCardView extends BaseCardView<Channel> {
47    private static final String TAG = MenuView.TAG;
48    private static final boolean DEBUG = MenuView.DEBUG;
49
50    private final int mCardImageWidth;
51    private final int mCardImageHeight;
52    private final int mIconWidth;
53    private final int mIconHeight;
54    private final int mIconPadding;
55    private final int mIconColorFilter;
56
57    private ImageView mImageView;
58    private View mGradientView;
59    private TextView mAppInfoView;
60    private View mMetaViewHolder;
61    private Channel mChannel;
62    private Intent mIntent;
63    private final PackageManager mPackageManager;
64    private final TvInputManagerHelper mTvInputManagerHelper;
65
66    public AppLinkCardView(Context context) {
67        this(context, null);
68    }
69
70    public AppLinkCardView(Context context, AttributeSet attrs) {
71        this(context, attrs, 0);
72    }
73
74    public AppLinkCardView(Context context, AttributeSet attrs, int defStyle) {
75        super(context, attrs, defStyle);
76
77        mCardImageWidth = getResources().getDimensionPixelSize(R.dimen.card_image_layout_width);
78        mCardImageHeight = getResources().getDimensionPixelSize(R.dimen.card_image_layout_height);
79        mIconWidth = getResources().getDimensionPixelSize(R.dimen.app_link_card_icon_width);
80        mIconHeight = getResources().getDimensionPixelSize(R.dimen.app_link_card_icon_height);
81        mIconPadding = getResources().getDimensionPixelOffset(R.dimen.app_link_card_icon_padding);
82        mPackageManager = context.getPackageManager();
83        mTvInputManagerHelper = ((MainActivity) context).getTvInputManagerHelper();
84        mIconColorFilter = getResources().getColor(R.color.app_link_card_icon_color_filter, null);
85    }
86
87    /**
88     * Returns the intent which will be started once this card is clicked.
89     */
90    public Intent getIntent() {
91        return mIntent;
92    }
93
94    @Override
95    public void onBind(Channel channel, boolean selected) {
96        if (DEBUG) {
97            Log.d(TAG, "onBind(channelName=" + channel.getDisplayName() + ", selected=" + selected
98                    + ")");
99        }
100        mChannel = channel;
101        ApplicationInfo appInfo = mTvInputManagerHelper.getTvInputAppInfo(mChannel.getInputId());
102        int linkType = mChannel.getAppLinkType(getContext());
103        mIntent = mChannel.getAppLinkIntent(getContext());
104
105        switch (linkType) {
106            case Channel.APP_LINK_TYPE_CHANNEL:
107                setText(mChannel.getAppLinkText());
108                mAppInfoView.setVisibility(VISIBLE);
109                mGradientView.setVisibility(VISIBLE);
110                mAppInfoView.setCompoundDrawablePadding(mIconPadding);
111                mAppInfoView.setCompoundDrawables(null, null, null, null);
112                mAppInfoView.setText(mPackageManager.getApplicationLabel(appInfo));
113                if (!TextUtils.isEmpty(mChannel.getAppLinkIconUri())) {
114                    mChannel.loadBitmap(getContext(), Channel.LOAD_IMAGE_TYPE_APP_LINK_ICON,
115                            mIconWidth, mIconHeight, createChannelLogoCallback(this, mChannel,
116                                    Channel.LOAD_IMAGE_TYPE_APP_LINK_ICON));
117                } else if (appInfo.icon != 0) {
118                    Drawable appIcon = mPackageManager.getApplicationIcon(appInfo);
119                    BitmapUtils.setColorFilterToDrawable(mIconColorFilter, appIcon);
120                    appIcon.setBounds(0, 0, mIconWidth, mIconHeight);
121                    mAppInfoView.setCompoundDrawables(appIcon, null, null, null);
122                }
123                break;
124            case Channel.APP_LINK_TYPE_APP:
125                setText(getContext().getString(
126                        R.string.channels_item_app_link_app_launcher,
127                        mPackageManager.getApplicationLabel(appInfo)));
128                mAppInfoView.setVisibility(GONE);
129                mGradientView.setVisibility(GONE);
130                break;
131            default:
132                mAppInfoView.setVisibility(GONE);
133                mGradientView.setVisibility(GONE);
134                Log.d(TAG, "Should not be here.");
135        }
136
137        if (mChannel.getAppLinkColor() == 0) {
138            mMetaViewHolder.setBackgroundResource(R.color.channel_card_meta_background);
139        } else {
140            mMetaViewHolder.setBackgroundColor(mChannel.getAppLinkColor());
141        }
142
143        if (!TextUtils.isEmpty(mChannel.getAppLinkPosterArtUri())) {
144            mImageView.setImageResource(R.drawable.ic_recent_thumbnail_default);
145            mChannel.loadBitmap(getContext(), Channel.LOAD_IMAGE_TYPE_APP_LINK_POSTER_ART,
146                    mCardImageWidth, mCardImageHeight, createChannelLogoCallback(this, mChannel,
147                            Channel.LOAD_IMAGE_TYPE_APP_LINK_POSTER_ART));
148        } else {
149            setCardImageWithBanner(appInfo);
150        }
151        // Call super.onBind() at the end intentionally. In order to correctly handle extension of
152        // text view, text should be set before calling super.onBind.
153        super.onBind(channel, selected);
154    }
155
156    private static ImageLoader.ImageLoaderCallback<AppLinkCardView> createChannelLogoCallback(
157            AppLinkCardView cardView, final Channel channel, final int type) {
158        return new ImageLoader.ImageLoaderCallback<AppLinkCardView>(cardView) {
159            @Override
160            public void onBitmapLoaded(AppLinkCardView cardView, @Nullable Bitmap bitmap) {
161                // mChannel can be changed before the image load finished.
162                if (!cardView.mChannel.hasSameReadOnlyInfo(channel)) {
163                    return;
164                }
165                cardView.updateChannelLogo(bitmap, type);
166            }
167        };
168    }
169
170    private void updateChannelLogo(@Nullable Bitmap bitmap, int type) {
171        if (type == Channel.LOAD_IMAGE_TYPE_APP_LINK_ICON) {
172            BitmapDrawable drawable = null;
173            if (bitmap != null) {
174                drawable = new BitmapDrawable(getResources(), bitmap);
175                if (bitmap.getWidth() > bitmap.getHeight()) {
176                    drawable.setBounds(0, 0, mIconWidth,
177                            mIconWidth * bitmap.getHeight() / bitmap.getWidth());
178                } else {
179                    drawable.setBounds(0, 0,
180                            mIconHeight * bitmap.getWidth() / bitmap.getHeight(),
181                            mIconHeight);
182                }
183            }
184            BitmapUtils.setColorFilterToDrawable(mIconColorFilter, drawable);
185            mAppInfoView.setCompoundDrawables(drawable, null, null, null);
186        } else if (type == Channel.LOAD_IMAGE_TYPE_APP_LINK_POSTER_ART) {
187            if (bitmap == null) {
188                setCardImageWithBanner(
189                        mTvInputManagerHelper.getTvInputAppInfo(mChannel.getInputId()));
190            } else {
191                mImageView.setImageBitmap(bitmap);
192                if (mChannel.getAppLinkColor() == 0) {
193                    extractAndSetMetaViewBackgroundColor(bitmap);
194                }
195            }
196        }
197    }
198
199    @Override
200    protected void onFinishInflate() {
201        super.onFinishInflate();
202        mImageView = (ImageView) findViewById(R.id.image);
203        mGradientView = findViewById(R.id.image_gradient);
204        mAppInfoView = (TextView) findViewById(R.id.app_info);
205        mMetaViewHolder = findViewById(R.id.app_link_text_holder);
206    }
207
208    // Try to set the card image with following order:
209    // 1) Provided poster art image, 2) Activity banner, 3) Activity icon, 4) Application banner,
210    // 5) Application icon, and 6) default image.
211    private void setCardImageWithBanner(ApplicationInfo appInfo) {
212        Drawable banner = null;
213        if (mIntent != null) {
214            try {
215                banner = mPackageManager.getActivityBanner(mIntent);
216                if (banner == null) {
217                    banner = mPackageManager.getActivityIcon(mIntent);
218                }
219            } catch (PackageManager.NameNotFoundException e) {
220                // do nothing.
221            }
222        }
223
224        if (banner == null && appInfo != null) {
225            if (appInfo.banner != 0) {
226                banner = mPackageManager.getApplicationBanner(appInfo);
227            }
228            if (banner == null && appInfo.icon != 0) {
229                banner = mPackageManager.getApplicationIcon(appInfo);
230            }
231        }
232
233        if (banner == null) {
234            mImageView.setImageResource(R.drawable.ic_recent_thumbnail_default);
235            mImageView.setBackgroundResource(R.color.channel_card);
236        } else {
237            Bitmap bitmap =
238                    Bitmap.createBitmap(mCardImageWidth, mCardImageHeight, Bitmap.Config.ARGB_8888);
239            Canvas canvas = new Canvas(bitmap);
240            banner.setBounds(0, 0, mCardImageWidth, mCardImageHeight);
241            banner.draw(canvas);
242            mImageView.setImageDrawable(banner);
243            if (mChannel.getAppLinkColor() == 0) {
244                extractAndSetMetaViewBackgroundColor(bitmap);
245            }
246        }
247    }
248
249    private void extractAndSetMetaViewBackgroundColor(Bitmap bitmap) {
250        new Palette.Builder(bitmap).generate(new Palette.PaletteAsyncListener() {
251            @Override
252            public void onGenerated(Palette palette) {
253                mMetaViewHolder.setBackgroundColor(palette.getDarkVibrantColor(
254                        getResources().getColor(R.color.channel_card_meta_background, null)));
255            }
256        });
257    }
258}
259