AppLinkCardView.java revision 07b043dc3db83d6d20f0e8513b946830ab00e37b
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.v7.graphics.Palette;
28import android.text.TextUtils;
29import android.util.AttributeSet;
30import android.util.Log;
31import android.view.View;
32import android.view.ViewGroup;
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.TvInputManagerHelper;
41
42/**
43 * A view to render an app link card.
44 */
45public class AppLinkCardView extends BaseCardView<Channel> implements Channel.LoadImageCallback {
46    private static final String TAG = MenuView.TAG;
47    private static final boolean DEBUG = MenuView.DEBUG;
48
49    private final float mCardHeight;
50    private final float mExtendedCardHeight;
51    private final float mTextViewHeight;
52    private final float mExtendedTextViewCardHeight;
53    private final int mCardImageWidth;
54    private final int mCardImageHeight;
55    private final int mIconWidth;
56    private final int mIconHeight;
57    private final int mIconPadding;
58    private final int mIconColorFilter;
59
60    private ImageView mImageView;
61    private View mGradientView;
62    private TextView mAppInfoView;
63    private TextView mMetaViewFocused;
64    private TextView mMetaViewUnfocused;
65    private View mMetaViewHolder;
66    private Channel mChannel;
67    private Intent mIntent;
68    private boolean mExtendViewOnFocus;
69    private final PackageManager mPackageManager;
70    private final TvInputManagerHelper mTvInputManagerHelper;
71
72    public AppLinkCardView(Context context) {
73        this(context, null);
74    }
75
76    public AppLinkCardView(Context context, AttributeSet attrs) {
77        this(context, attrs, 0);
78    }
79
80    public AppLinkCardView(Context context, AttributeSet attrs, int defStyle) {
81        super(context, attrs, defStyle);
82
83        mCardImageWidth = getResources().getDimensionPixelSize(R.dimen.card_image_layout_width);
84        mCardImageHeight = getResources().getDimensionPixelSize(R.dimen.card_image_layout_height);
85        mCardHeight = getResources().getDimensionPixelSize(R.dimen.card_layout_height);
86        mExtendedCardHeight = getResources().getDimensionPixelOffset(
87                R.dimen.card_layout_height_extended);
88        mIconWidth = getResources().getDimensionPixelSize(R.dimen.app_link_card_icon_width);
89        mIconHeight = getResources().getDimensionPixelSize(R.dimen.app_link_card_icon_height);
90        mIconPadding = getResources().getDimensionPixelOffset(R.dimen.app_link_card_icon_padding);
91        mPackageManager = context.getPackageManager();
92        mTvInputManagerHelper = ((MainActivity) context).getTvInputManagerHelper();
93        mTextViewHeight = getResources().getDimensionPixelSize(
94                R.dimen.card_meta_layout_height);
95        mExtendedTextViewCardHeight = getResources().getDimensionPixelOffset(
96                R.dimen.card_meta_layout_height_extended);
97        mIconColorFilter = getResources().getColor(R.color.app_link_card_icon_color_filter, null);
98    }
99
100    /**
101     * Returns the intent which will be started once this card is clicked.
102     */
103    public Intent getIntent() {
104        return mIntent;
105    }
106
107    @Override
108    public void onBind(Channel channel, boolean selected) {
109        if (DEBUG) {
110            Log.d(TAG, "onBind(channel=" + channel.getDisplayName() + ", selected=" + selected
111                    + ")");
112        }
113        mChannel = channel;
114        ApplicationInfo appInfo = mTvInputManagerHelper.getTvInputAppInfo(mChannel.getInputId());
115        int linkType = mChannel.getAppLinkType(getContext());
116        mIntent = mChannel.getAppLinkIntent(getContext());
117
118        switch (linkType) {
119            case Channel.APP_LINK_TYPE_CHANNEL:
120                setMetaViewText(mChannel.getAppLinkText());
121                mAppInfoView.setVisibility(VISIBLE);
122                mGradientView.setVisibility(VISIBLE);
123                mAppInfoView.setCompoundDrawablePadding(mIconPadding);
124                mAppInfoView.setCompoundDrawables(null, null, null, null);
125                mAppInfoView.setText(mPackageManager.getApplicationLabel(appInfo));
126                if (!TextUtils.isEmpty(mChannel.getAppLinkIconUri())) {
127                    mChannel.loadBitmap(getContext(), Channel.LOAD_IMAGE_TYPE_APP_LINK_ICON,
128                            mIconWidth, mIconHeight, this);
129                } else if (appInfo.icon != 0) {
130                    Drawable appIcon = mPackageManager.getApplicationIcon(appInfo);
131                    BitmapUtils.setColorFilterToDrawable(mIconColorFilter, appIcon);
132                    appIcon.setBounds(0, 0, mIconWidth, mIconHeight);
133                    mAppInfoView.setCompoundDrawables(appIcon, null, null, null);
134                }
135                break;
136            case Channel.APP_LINK_TYPE_APP:
137                setMetaViewText(getContext().getString(
138                        R.string.channels_item_app_link_app_launcher,
139                        mPackageManager.getApplicationLabel(appInfo)));
140                mAppInfoView.setVisibility(GONE);
141                mGradientView.setVisibility(GONE);
142                break;
143            default:
144                mAppInfoView.setVisibility(GONE);
145                mGradientView.setVisibility(GONE);
146                Log.d(TAG, "Should not be here.");
147        }
148
149        if (mChannel.getAppLinkColor() == 0) {
150            mMetaViewHolder.setBackgroundResource(R.color.channel_card_meta_background);
151        } else {
152            mMetaViewHolder.setBackgroundColor(mChannel.getAppLinkColor());
153        }
154
155        if (!TextUtils.isEmpty(mChannel.getAppLinkPosterArtUri())) {
156            mImageView.setImageResource(R.drawable.ic_recent_thumbnail_default);
157            mChannel.loadBitmap(getContext(), Channel.LOAD_IMAGE_TYPE_APP_LINK_POSTER_ART,
158                    mCardImageWidth, mCardImageHeight, this);
159        } else {
160            setCardImageWithBanner(appInfo);
161        }
162
163        mMetaViewFocused.measure(MeasureSpec.makeMeasureSpec(mCardImageWidth, MeasureSpec.EXACTLY),
164                MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
165        mExtendViewOnFocus = mMetaViewFocused.getLineCount() > 1;
166        if (mExtendViewOnFocus) {
167            setMetaViewFocusedAlpha(selected ? 1f : 0f);
168        } else {
169            setMetaViewFocusedAlpha(1f);
170        }
171
172        // Call super.onBind() at the end in order to make getCardHeight() return a proper value.
173        super.onBind(channel, selected);
174    }
175
176    @Override
177    public void onLoadImageFinished(Channel channel, int type, Bitmap bitmap) {
178        // mChannel can be changed before the image load finished.
179        if (!mChannel.hasSameReadOnlyInfo(channel)) {
180            return;
181        }
182        if (type == Channel.LOAD_IMAGE_TYPE_APP_LINK_ICON) {
183            BitmapDrawable drawable = null;
184            if (bitmap != null) {
185                drawable = new BitmapDrawable(getResources(), bitmap);
186                if (bitmap.getWidth() > bitmap.getHeight()) {
187                    drawable.setBounds(0, 0, mIconWidth,
188                            mIconWidth * bitmap.getHeight() / bitmap.getWidth());
189                } else {
190                    drawable.setBounds(0, 0, mIconHeight * bitmap.getWidth() / bitmap.getHeight(),
191                            mIconHeight);
192                }
193            }
194            BitmapUtils.setColorFilterToDrawable(mIconColorFilter, drawable);
195            mAppInfoView.setCompoundDrawables(drawable, null, null, null);
196        } else if (type == Channel.LOAD_IMAGE_TYPE_APP_LINK_POSTER_ART) {
197            if (bitmap == null) {
198                setCardImageWithBanner(
199                        mTvInputManagerHelper.getTvInputAppInfo(mChannel.getInputId()));
200            } else {
201                mImageView.setImageBitmap(bitmap);
202                if (mChannel.getAppLinkColor() == 0) {
203                    extractAndSetMetaViewBackgroundColor(bitmap);
204                }
205            }
206        }
207    }
208
209    @Override
210    protected void onFinishInflate() {
211        mImageView = (ImageView) findViewById(R.id.image);
212        mGradientView = findViewById(R.id.image_gradient);
213        mAppInfoView = (TextView) findViewById(R.id.app_info);
214        mMetaViewHolder = findViewById(R.id.app_link_text_holder);
215        mMetaViewFocused = (TextView) findViewById(R.id.app_link_text_focused);
216        mMetaViewUnfocused = (TextView) findViewById(R.id.app_link_text_unfocused);
217    }
218
219    @Override
220    protected void onFocusAnimationStart(boolean selected) {
221        if (mExtendViewOnFocus) {
222            setMetaViewFocusedAlpha(selected ? 1f : 0f);
223        }
224    }
225
226    @Override
227    protected void onSetFocusAnimatedValue(float animatedValue) {
228        super.onSetFocusAnimatedValue(animatedValue);
229        if (mExtendViewOnFocus) {
230            ViewGroup.LayoutParams params = mMetaViewUnfocused.getLayoutParams();
231            params.height = Math.round(mTextViewHeight
232                    + (mExtendedTextViewCardHeight - mTextViewHeight) * animatedValue);
233            setMetaViewLayoutParams(params);
234            setMetaViewFocusedAlpha(animatedValue);
235        }
236    }
237
238    @Override
239    protected float getCardHeight() {
240        return (mExtendViewOnFocus && isFocused()) ? mExtendedCardHeight : mCardHeight;
241    }
242
243    // Try to set the card image with following order:
244    // 1) Provided poster art image, 2) Activity banner, 3) Application banner,
245    // 4) Activity logo, 5) Application logo, and 6) default image.
246    private void setCardImageWithBanner(ApplicationInfo appInfo) {
247        Drawable banner = null;
248        try {
249            banner = mPackageManager.getActivityBanner(mIntent);
250        } catch (PackageManager.NameNotFoundException e) {
251            // do nothing.
252        }
253        if (banner == null && appInfo != null && appInfo.banner != 0) {
254            banner = mPackageManager.getApplicationBanner(appInfo);
255        }
256        if (banner == null) {
257            try {
258                banner = mPackageManager.getActivityLogo(mIntent);
259            } catch (PackageManager.NameNotFoundException e) {
260                // do nothing.
261            }
262        }
263        if (banner == null && appInfo != null && appInfo.logo != 0) {
264            banner = mPackageManager.getApplicationLogo(appInfo);
265        }
266
267        if (banner == null) {
268            mImageView.setImageResource(R.drawable.ic_recent_thumbnail_default);
269            mImageView.setBackgroundResource(R.color.channel_card);
270        } else {
271            Bitmap bitmap =
272                    Bitmap.createBitmap(mCardImageWidth, mCardImageHeight, Bitmap.Config.ARGB_8888);
273            Canvas canvas = new Canvas(bitmap);
274            banner.setBounds(0, 0, mCardImageWidth, mCardImageHeight);
275            banner.draw(canvas);
276            mImageView.setImageDrawable(banner);
277            if (mChannel.getAppLinkColor() == 0) {
278                extractAndSetMetaViewBackgroundColor(bitmap);
279            }
280        }
281    }
282
283    private void extractAndSetMetaViewBackgroundColor(Bitmap bitmap) {
284        new Palette.Builder(bitmap).generate(new Palette.PaletteAsyncListener() {
285            @Override
286            public void onGenerated(Palette palette) {
287                mMetaViewHolder.setBackgroundColor(palette.getDarkVibrantColor(
288                        getResources().getColor(R.color.channel_card_meta_background, null)));
289            }
290        });
291    }
292
293    private void setMetaViewLayoutParams(ViewGroup.LayoutParams params) {
294        mMetaViewFocused.setLayoutParams(params);
295        mMetaViewUnfocused.setLayoutParams(params);
296    }
297
298    private void setMetaViewText(String text) {
299        mMetaViewFocused.setText(text);
300        mMetaViewUnfocused.setText(text);
301    }
302
303    private void setMetaViewFocusedAlpha(float focusedAlpha) {
304        mMetaViewFocused.setAlpha(focusedAlpha);
305        mMetaViewUnfocused.setAlpha(1f - focusedAlpha);
306    }
307}
308