Home | History | Annotate | Download | only in menu
      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 
     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