• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.tv.menu;
18 
19 import android.content.Context;
20 import android.content.Intent;
21 import android.content.pm.ApplicationInfo;
22 import android.content.pm.PackageManager;
23 import android.graphics.Bitmap;
24 import android.graphics.Canvas;
25 import android.graphics.drawable.BitmapDrawable;
26 import android.graphics.drawable.Drawable;
27 import android.support.annotation.Nullable;
28 import android.support.v7.graphics.Palette;
29 import android.text.TextUtils;
30 import android.util.AttributeSet;
31 import android.util.Log;
32 import android.view.View;
33 import android.widget.ImageView;
34 import android.widget.TextView;
35 
36 import com.android.tv.MainActivity;
37 import com.android.tv.R;
38 import com.android.tv.data.Channel;
39 import com.android.tv.util.BitmapUtils;
40 import com.android.tv.util.ImageLoader;
41 import com.android.tv.util.TvInputManagerHelper;
42 
43 /**
44  * A view to render an app link card.
45  */
46 public 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 
AppLinkCardView(Context context)66     public AppLinkCardView(Context context) {
67         this(context, null);
68     }
69 
AppLinkCardView(Context context, AttributeSet attrs)70     public AppLinkCardView(Context context, AttributeSet attrs) {
71         this(context, attrs, 0);
72     }
73 
AppLinkCardView(Context context, AttributeSet attrs, int defStyle)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      */
getIntent()90     public Intent getIntent() {
91         return mIntent;
92     }
93 
94     @Override
onBind(Channel channel, boolean selected)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 
createChannelLogoCallback( AppLinkCardView cardView, final Channel channel, final int type)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