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