Home | History | Annotate | Download | only in browse
      1 /*
      2  * Copyright (C) 2016 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.dvr.ui.browse;
     18 
     19 import android.animation.ValueAnimator;
     20 import android.content.Context;
     21 import android.graphics.Bitmap;
     22 import android.graphics.Rect;
     23 import android.graphics.drawable.BitmapDrawable;
     24 import android.graphics.drawable.Drawable;
     25 import android.support.annotation.Nullable;
     26 import android.support.v17.leanback.widget.BaseCardView;
     27 import android.text.TextUtils;
     28 import android.view.LayoutInflater;
     29 import android.view.View;
     30 import android.widget.FrameLayout;
     31 import android.widget.ImageView;
     32 import android.widget.ProgressBar;
     33 import android.widget.TextView;
     34 import com.android.tv.R;
     35 import com.android.tv.dvr.data.RecordedProgram;
     36 import com.android.tv.ui.ViewUtils;
     37 import com.android.tv.util.images.ImageLoader;
     38 
     39 /**
     40  * A CardView for displaying info about a {@link com.android.tv.dvr.data.ScheduledRecording} or
     41  * {@link RecordedProgram} or {@link com.android.tv.dvr.data.SeriesRecording}.
     42  */
     43 public class RecordingCardView extends BaseCardView {
     44     // This value should be the same with
     45     // android.support.v17.leanback.widget.FocusHighlightHelper.BrowseItemFocusHighlight.DURATION_MS
     46     private static final int ANIMATION_DURATION = 150;
     47     private final ImageView mImageView;
     48     private final int mImageWidth;
     49     private final int mImageHeight;
     50     private String mImageUri;
     51     private final TextView mMajorContentView;
     52     private final TextView mMinorContentView;
     53     private final ProgressBar mProgressBar;
     54     private final View mAffiliatedIconContainer;
     55     private final ImageView mAffiliatedIcon;
     56     private final Drawable mDefaultImage;
     57     private final FrameLayout mTitleArea;
     58     private final TextView mFoldedTitleView;
     59     private final TextView mExpandedTitleView;
     60     private final ValueAnimator mExpandTitleAnimator;
     61     private final int mFoldedTitleHeight;
     62     private final int mExpandedTitleHeight;
     63     private final boolean mExpandTitleWhenFocused;
     64     private boolean mExpanded;
     65     private String mDetailBackgroundImageUri;
     66 
     67     public RecordingCardView(Context context) {
     68         this(context, false);
     69     }
     70 
     71     public RecordingCardView(Context context, boolean expandTitleWhenFocused) {
     72         this(
     73                 context,
     74                 context.getResources()
     75                         .getDimensionPixelSize(R.dimen.dvr_library_card_image_layout_width),
     76                 context.getResources()
     77                         .getDimensionPixelSize(R.dimen.dvr_library_card_image_layout_height),
     78                 expandTitleWhenFocused);
     79     }
     80 
     81     public RecordingCardView(
     82             Context context, int imageWidth, int imageHeight, boolean expandTitleWhenFocused) {
     83         super(context);
     84         // TODO(dvr): move these to the layout XML.
     85         setCardType(BaseCardView.CARD_TYPE_INFO_UNDER_WITH_EXTRA);
     86         setInfoVisibility(BaseCardView.CARD_REGION_VISIBLE_ALWAYS);
     87         setFocusable(true);
     88         setFocusableInTouchMode(true);
     89         mDefaultImage = getResources().getDrawable(R.drawable.dvr_default_poster, null);
     90 
     91         LayoutInflater inflater = LayoutInflater.from(getContext());
     92         inflater.inflate(R.layout.dvr_recording_card_view, this);
     93         mImageView = (ImageView) findViewById(R.id.image);
     94         mImageWidth = imageWidth;
     95         mImageHeight = imageHeight;
     96         mProgressBar = (ProgressBar) findViewById(R.id.recording_progress);
     97         mAffiliatedIconContainer = findViewById(R.id.affiliated_icon_container);
     98         mAffiliatedIcon = (ImageView) findViewById(R.id.affiliated_icon);
     99         mMajorContentView = (TextView) findViewById(R.id.content_major);
    100         mMinorContentView = (TextView) findViewById(R.id.content_minor);
    101         mTitleArea = (FrameLayout) findViewById(R.id.title_area);
    102         mFoldedTitleView = (TextView) findViewById(R.id.title_one_line);
    103         mExpandedTitleView = (TextView) findViewById(R.id.title_two_lines);
    104         mFoldedTitleHeight =
    105                 getResources().getDimensionPixelSize(R.dimen.dvr_library_card_folded_title_height);
    106         mExpandedTitleHeight =
    107                 getResources()
    108                         .getDimensionPixelSize(R.dimen.dvr_library_card_expanded_title_height);
    109         mExpandTitleAnimator = ValueAnimator.ofFloat(0.0f, 1.0f).setDuration(ANIMATION_DURATION);
    110         mExpandTitleAnimator.addUpdateListener(
    111                 new ValueAnimator.AnimatorUpdateListener() {
    112                     @Override
    113                     public void onAnimationUpdate(ValueAnimator valueAnimator) {
    114                         float value = (Float) valueAnimator.getAnimatedValue();
    115                         mExpandedTitleView.setAlpha(value);
    116                         mFoldedTitleView.setAlpha(1.0f - value);
    117                         ViewUtils.setLayoutHeight(
    118                                 mTitleArea,
    119                                 (int)
    120                                         (mFoldedTitleHeight
    121                                                 + (mExpandedTitleHeight - mFoldedTitleHeight)
    122                                                         * value));
    123                     }
    124                 });
    125         mExpandTitleWhenFocused = expandTitleWhenFocused;
    126     }
    127 
    128     @Override
    129     protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
    130         super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
    131         // Preload the background image going to be used in detail fragments here to prevent
    132         // loading and drawing background images during activity transitions.
    133         if (gainFocus) {
    134             if (!TextUtils.isEmpty(mDetailBackgroundImageUri)) {
    135                 ImageLoader.loadBitmap(
    136                         getContext(),
    137                         mDetailBackgroundImageUri,
    138                         Integer.MAX_VALUE,
    139                         Integer.MAX_VALUE,
    140                         null);
    141             }
    142         }
    143         if (mExpandTitleWhenFocused) {
    144             if (gainFocus) {
    145                 expandTitle(true, true);
    146             } else {
    147                 expandTitle(false, true);
    148             }
    149         }
    150     }
    151 
    152     /**
    153      * Expands/folds the title area to show program title with two/one lines.
    154      *
    155      * @param expand {@code true} to expand the title area, or {@code false} to fold it.
    156      * @param withAnimation {@code true} to expand/fold with animation.
    157      */
    158     public void expandTitle(boolean expand, boolean withAnimation) {
    159         if (expand != mExpanded && mFoldedTitleView.getLayout().getEllipsisCount(0) > 0) {
    160             if (withAnimation) {
    161                 if (expand) {
    162                     mExpandTitleAnimator.start();
    163                 } else {
    164                     mExpandTitleAnimator.reverse();
    165                 }
    166             } else {
    167                 if (expand) {
    168                     mFoldedTitleView.setAlpha(0.0f);
    169                     mExpandedTitleView.setAlpha(1.0f);
    170                     ViewUtils.setLayoutHeight(mTitleArea, mExpandedTitleHeight);
    171                 } else {
    172                     mFoldedTitleView.setAlpha(1.0f);
    173                     mExpandedTitleView.setAlpha(0.0f);
    174                     ViewUtils.setLayoutHeight(mTitleArea, mFoldedTitleHeight);
    175                 }
    176             }
    177             mExpanded = expand;
    178         }
    179     }
    180 
    181     void setTitle(CharSequence title) {
    182         mFoldedTitleView.setText(title);
    183         mExpandedTitleView.setText(title);
    184     }
    185 
    186     void setContent(CharSequence majorContent, CharSequence minorContent) {
    187         if (!TextUtils.isEmpty(majorContent)) {
    188             mMajorContentView.setText(majorContent);
    189             mMajorContentView.setVisibility(View.VISIBLE);
    190         } else {
    191             mMajorContentView.setVisibility(View.GONE);
    192         }
    193         if (!TextUtils.isEmpty(minorContent)) {
    194             mMinorContentView.setText(minorContent);
    195             mMinorContentView.setVisibility(View.VISIBLE);
    196         } else {
    197             mMinorContentView.setVisibility(View.GONE);
    198         }
    199     }
    200 
    201     /** Sets progress bar. If progress is {@code null}, hides progress bar. */
    202     void setProgressBar(Integer progress) {
    203         if (progress == null) {
    204             mProgressBar.setVisibility(View.GONE);
    205         } else {
    206             mProgressBar.setProgress(progress);
    207             mProgressBar.setVisibility(View.VISIBLE);
    208         }
    209     }
    210 
    211     /** Sets the color of progress bar. */
    212     void setProgressBarColor(int color) {
    213         mProgressBar.getProgressDrawable().setTint(color);
    214     }
    215 
    216     /**
    217      * Sets the image URI of the poster should be shown on the card view.
    218      *
    219      * @param isChannelLogo {@code true} if the image is from channels' logo.
    220      */
    221     void setImageUri(String uri, boolean isChannelLogo) {
    222         if (isChannelLogo) {
    223             mImageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
    224         } else {
    225             mImageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
    226         }
    227         mImageUri = uri;
    228         if (TextUtils.isEmpty(uri)) {
    229             mImageView.setImageDrawable(mDefaultImage);
    230         } else {
    231             ImageLoader.loadBitmap(
    232                     getContext(),
    233                     uri,
    234                     mImageWidth,
    235                     mImageHeight,
    236                     new RecordingCardImageLoaderCallback(this, uri));
    237         }
    238     }
    239 
    240     /** Sets the {@link Drawable} of the poster should be shown on the card view. */
    241     public void setImage(Drawable image) {
    242         if (image != null) {
    243             mImageView.setImageDrawable(image);
    244         }
    245     }
    246 
    247     /**
    248      * Sets the affiliated icon of the card view, which will be displayed at the lower-right corner
    249      * of the poster.
    250      */
    251     public void setAffiliatedIcon(int imageResId) {
    252         if (imageResId > 0) {
    253             mAffiliatedIconContainer.setVisibility(View.VISIBLE);
    254             mAffiliatedIcon.setImageResource(imageResId);
    255         } else {
    256             mAffiliatedIconContainer.setVisibility(View.INVISIBLE);
    257         }
    258     }
    259 
    260     /**
    261      * Sets the background image URI of the card view, which will be displayed as background when
    262      * the view is clicked and shows its details fragment.
    263      */
    264     public void setDetailBackgroundImageUri(String uri) {
    265         mDetailBackgroundImageUri = uri;
    266     }
    267 
    268     /** Returns image view. */
    269     public ImageView getImageView() {
    270         return mImageView;
    271     }
    272 
    273     private static class RecordingCardImageLoaderCallback
    274             extends ImageLoader.ImageLoaderCallback<RecordingCardView> {
    275         private final String mUri;
    276 
    277         RecordingCardImageLoaderCallback(RecordingCardView referent, String uri) {
    278             super(referent);
    279             mUri = uri;
    280         }
    281 
    282         @Override
    283         public void onBitmapLoaded(RecordingCardView view, @Nullable Bitmap bitmap) {
    284             if (bitmap == null || !mUri.equals(view.mImageUri)) {
    285                 view.mImageView.setImageDrawable(view.mDefaultImage);
    286             } else {
    287                 view.mImageView.setImageDrawable(new BitmapDrawable(view.getResources(), bitmap));
    288             }
    289         }
    290     }
    291 
    292     public void reset() {
    293         mFoldedTitleView.setText(null);
    294         mExpandedTitleView.setText(null);
    295         setContent(null, null);
    296         mImageView.setImageDrawable(mDefaultImage);
    297     }
    298 }
    299