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