ChannelBannerView.java revision 0adb8cd2332d2e61a7ba9fabe14fdfcf0d44da27
1/* 2 * Copyright (C) 2014 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 17package com.android.tv.ui; 18 19import android.content.Context; 20import android.content.pm.PackageManager; 21import android.content.pm.PackageManager.NameNotFoundException; 22import android.content.pm.ServiceInfo; 23import android.database.ContentObserver; 24import android.graphics.Bitmap; 25import android.graphics.Paint; 26import android.graphics.Rect; 27import android.graphics.drawable.Drawable; 28import android.media.tv.TvContract; 29import android.media.tv.TvInputInfo; 30import android.net.Uri; 31import android.os.Handler; 32import android.text.TextUtils; 33import android.text.format.DateFormat; 34import android.util.AttributeSet; 35import android.util.LruCache; 36import android.util.TypedValue; 37import android.view.View; 38import android.widget.ImageView; 39import android.widget.ProgressBar; 40import android.widget.RelativeLayout; 41import android.widget.TextView; 42 43import com.android.tv.R; 44import com.android.tv.data.Channel; 45import com.android.tv.data.ChannelMap; 46import com.android.tv.data.Program; 47import com.android.tv.data.StreamInfo; 48import com.android.tv.util.Utils; 49 50/** 51 * A view to render channel banner. 52 */ 53public class ChannelBannerView extends RelativeLayout implements Channel.LoadLogoCallback { 54 private static final int CACHE_SIZE = 10; 55 private TextView mClosedCaptionTextView; 56 private TextView mResolutionTextView; 57 private TextView mAspectRatioTextView; 58 private TextView mAudioChannelTextView; 59 private ProgressBar mRemainingTimeView; 60 private TextView mProgrameDescriptionTextView; 61 private TextView mChannelTextView; 62 private TextView mChannelNameTextView; 63 private ImageView mTvInputLogoImageView; 64 private ImageView mChannelLogoImageView; 65 private TextView mProgramTextView; 66 private TextView mProgramTimeTextView; 67 private View mAnchorView; 68 private Uri mCurrentChannelUri; 69 private final LruCache<TvInputInfo, Drawable> mChannelInfoLogoCache = 70 new LruCache<TvInputInfo, Drawable> (CACHE_SIZE) { 71 @Override 72 protected Drawable create(TvInputInfo info) { 73 try { 74 PackageManager pm = getContext().getPackageManager(); 75 ServiceInfo serviceInfo = pm.getServiceInfo(info.getComponent(), 0); 76 if (serviceInfo != null) { 77 return serviceInfo.loadLogo(pm); 78 } 79 } catch (NameNotFoundException e) { 80 } 81 return null; 82 } 83 }; 84 85 private final ContentObserver mProgramUpdateObserver = new ContentObserver(new Handler()) { 86 @Override 87 public void onChange(boolean selfChange, Uri uri) { 88 updateProgramInfo(); 89 } 90 }; 91 92 public ChannelBannerView(Context context) { 93 this(context, null); 94 } 95 96 public ChannelBannerView(Context context, AttributeSet attrs) { 97 super(context, attrs, 0); 98 } 99 100 public ChannelBannerView(Context context, AttributeSet attrs, int defStyle) { 101 super(context, attrs, defStyle); 102 } 103 104 @Override 105 protected void onAttachedToWindow() { 106 super.onAttachedToWindow(); 107 getContext().getContentResolver().registerContentObserver(TvContract.Programs.CONTENT_URI, 108 true, mProgramUpdateObserver); 109 } 110 111 @Override 112 protected void onDetachedFromWindow() { 113 getContext().getContentResolver().unregisterContentObserver(mProgramUpdateObserver); 114 super.onDetachedFromWindow(); 115 } 116 117 @Override 118 protected void onFinishInflate() { 119 super.onFinishInflate(); 120 121 mClosedCaptionTextView = (TextView) findViewById(R.id.closed_caption); 122 mResolutionTextView = (TextView) findViewById(R.id.resolution); 123 mAspectRatioTextView = (TextView) findViewById(R.id.aspect_ratio); 124 mAudioChannelTextView = (TextView) findViewById(R.id.audio_channel); 125 mRemainingTimeView = (ProgressBar) findViewById(R.id.remaining_time); 126 mChannelTextView = (TextView) findViewById(R.id.channel_text); 127 mChannelNameTextView = (TextView) findViewById(R.id.channel_name); 128 mTvInputLogoImageView = (ImageView) findViewById(R.id.tvinput_logo); 129 mChannelLogoImageView = (ImageView) findViewById(R.id.channel_logo); 130 mProgramTimeTextView = (TextView) findViewById(R.id.program_time_text); 131 mProgrameDescriptionTextView = (TextView) findViewById(R.id.program_description); 132 mProgramTextView = (TextView) findViewById(R.id.program_text); 133 mAnchorView = findViewById(R.id.anchor); 134 } 135 136 public void updateViews(ChannelMap channelMap, StreamInfo info) { 137 if (channelMap == null || !channelMap.isLoadFinished()) { 138 return; 139 } 140 141 TvInputInfo inputInfo = (info == null) ? null : info.getCurrentTvInputInfo(); 142 Drawable tvInputLogo = (inputInfo == null) ? null : mChannelInfoLogoCache.get(inputInfo); 143 if (tvInputLogo != null) { 144 mTvInputLogoImageView.setVisibility(View.VISIBLE); 145 mTvInputLogoImageView.setImageDrawable(tvInputLogo); 146 } else { 147 mTvInputLogoImageView.setVisibility(View.GONE); 148 } 149 150 if (info.hasClosedCaption()) { 151 mClosedCaptionTextView.setVisibility(View.VISIBLE); 152 mClosedCaptionTextView.setText("CC"); 153 mClosedCaptionTextView.setVisibility(View.VISIBLE); 154 } else { 155 mClosedCaptionTextView.setVisibility(View.GONE); 156 } 157 if (info.getVideoDefinitionLevel() != StreamInfo.VIDEO_DEFINITION_LEVEL_UNKNOWN) { 158 mResolutionTextView.setVisibility(View.VISIBLE); 159 mResolutionTextView.setText(Utils.getVideoDefinitionLevelString( 160 info.getVideoDefinitionLevel())); 161 mResolutionTextView.setVisibility(View.VISIBLE); 162 } else { 163 mResolutionTextView.setVisibility(View.GONE); 164 } 165 166 String aspectRatio = 167 Utils.getAspectRatioString(info.getVideoWidth(), info.getVideoHeight()); 168 if (!TextUtils.isEmpty(aspectRatio)) { 169 mAspectRatioTextView.setVisibility(View.VISIBLE); 170 mAspectRatioTextView.setText(aspectRatio); 171 } else { 172 mAspectRatioTextView.setVisibility(View.GONE); 173 } 174 175 if (!TextUtils.isEmpty(Utils.getAudioChannelString(info.getAudioChannelCount()))) { 176 mAudioChannelTextView.setVisibility(View.VISIBLE); 177 mAudioChannelTextView.setText(Utils.getAudioChannelString(info.getAudioChannelCount())); 178 } else { 179 mAudioChannelTextView.setVisibility(View.GONE); 180 } 181 182 String displayNumber = channelMap.getCurrentDisplayNumber(); 183 if (displayNumber == null) { 184 displayNumber = ""; 185 } 186 187 if (displayNumber.length() <= 3) { 188 updateTextView( 189 mChannelTextView, 190 R.dimen.channel_banner_title_large_text_size, 191 R.dimen.channel_banner_title_large_margin_top); 192 } else if (displayNumber.length() <= 4) { 193 updateTextView( 194 mChannelTextView, 195 R.dimen.channel_banner_title_medium_text_size, 196 R.dimen.channel_banner_title_medium_margin_top); 197 } else { 198 updateTextView( 199 mChannelTextView, 200 R.dimen.channel_banner_title_small_text_size, 201 R.dimen.channel_banner_title_small_margin_top); 202 } 203 mChannelTextView.setText(displayNumber); 204 205 String displayName = channelMap.getCurrentDisplayName(); 206 if (displayName == null) { 207 displayName = ""; 208 } 209 mChannelNameTextView.setText(displayName); 210 211 mCurrentChannelUri = channelMap.getCurrentChannelUri(); 212 channelMap.getCurrentChannel().loadLogo(getContext(), this); 213 214 updateProgramInfo(); 215 } 216 217 private void updateTextView(TextView textView, int sizeRes, int marginTopRes) { 218 float textSize = getContext().getResources().getDimension(sizeRes); 219 if (textView.getTextSize() != textSize) { 220 textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize); 221 } 222 updateTopMargin(textView, marginTopRes); 223 } 224 225 private void updateTopMargin(View view, int marginTopRes) { 226 LayoutParams lp = (LayoutParams) view.getLayoutParams(); 227 int topMargin = (int) getContext().getResources().getDimension(marginTopRes); 228 if (lp.topMargin != topMargin) { 229 lp.topMargin = topMargin; 230 view.setLayoutParams(lp); 231 } 232 } 233 234 @Override 235 public void onLoadLogoFinished(Channel channel, Bitmap logo) { 236 if (logo == null) { 237 mChannelLogoImageView.setVisibility(View.GONE); 238 } else { 239 mChannelLogoImageView.setImageBitmap(logo); 240 mChannelLogoImageView.setVisibility(View.VISIBLE); 241 } 242 } 243 244 private String getFormattedTimeString(long time) { 245 return DateFormat.format( 246 getContext().getString(R.string.channel_banner_time_format), time).toString(); 247 } 248 249 public void updateProgramInfo() { 250 if (mCurrentChannelUri == null) { 251 handleNoProgramInformation(); 252 return; 253 } 254 255 Program program = Utils.getCurrentProgram(getContext(), mCurrentChannelUri); 256 if (program == null) { 257 handleNoProgramInformation(); 258 return; 259 } 260 261 String title = program.getTitle(); 262 if (!TextUtils.isEmpty(title)) { 263 int width = mProgramTextView.getWidth(); 264 if (width == 0) { 265 post(new Runnable() { 266 @Override 267 public void run() { 268 updateProgramInfo(); 269 } 270 }); 271 } 272 float largeTextSize = getContext().getResources().getDimension( 273 R.dimen.channel_banner_program_large_text_size); 274 int estimatedLineCount = estimateLineCount(title, largeTextSize, width); 275 boolean oneline = true; 276 if (estimatedLineCount > 1) { 277 updateTextView( 278 mProgramTextView, 279 R.dimen.channel_banner_program_medium_text_size, 280 R.dimen.channel_banner_program_medium_margin_top); 281 float mediumTextSize = getContext().getResources().getDimension( 282 R.dimen.channel_banner_program_medium_text_size); 283 if (estimateLineCount(title, mediumTextSize, width) > 1) { 284 oneline = false; 285 } 286 } else { 287 updateTextView( 288 mProgramTextView, 289 R.dimen.channel_banner_program_large_text_size, 290 R.dimen.channel_banner_program_large_margin_top); 291 } 292 updateTopMargin(mAnchorView, oneline 293 ? R.dimen.channel_banner_anchor_one_line_y 294 : R.dimen.channel_banner_anchor_two_line_y); 295 mProgramTextView.setText(title); 296 297 long startTime = program.getStartTimeUtcMillis(); 298 long endTime = program.getEndTimeUtcMillis(); 299 if (startTime > 0 && endTime > 0) { 300 mProgramTimeTextView.setVisibility(View.VISIBLE); 301 mRemainingTimeView.setVisibility(View.VISIBLE); 302 303 String startTimeText = getFormattedTimeString(startTime); 304 String endTimeText = getFormattedTimeString(endTime); 305 306 mProgramTimeTextView.setText(getContext().getString( 307 R.string.channel_banner_program_time_format, startTimeText, endTimeText)); 308 309 long currTime = System.currentTimeMillis(); 310 if (currTime <= startTime) { 311 mRemainingTimeView.setProgress(0); 312 } else if (currTime >= endTime) { 313 mRemainingTimeView.setProgress(100); 314 } else { 315 mRemainingTimeView.setProgress( 316 (int) (100 *(currTime - startTime) / (endTime - startTime))); 317 } 318 } else { 319 mProgramTimeTextView.setVisibility(View.GONE); 320 mRemainingTimeView.setVisibility(View.GONE); 321 } 322 } else { 323 mProgramTextView.setText(getContext().getString(R.string.channel_banner_no_title)); 324 mProgramTimeTextView.setVisibility(View.GONE); 325 mRemainingTimeView.setVisibility(View.GONE); 326 } 327 if (!TextUtils.isEmpty(program.getDescription())) { 328 mProgrameDescriptionTextView.setVisibility(View.VISIBLE); 329 mProgrameDescriptionTextView.setText(program.getDescription()); 330 } else { 331 mProgrameDescriptionTextView.setVisibility(View.GONE); 332 } 333 } 334 335 private void handleNoProgramInformation() { 336 mProgramTextView.setText(getContext().getString(R.string.channel_banner_no_title)); 337 mProgramTimeTextView.setVisibility(View.GONE); 338 mRemainingTimeView.setVisibility(View.GONE); 339 mProgrameDescriptionTextView.setVisibility(View.GONE); 340 } 341 342 private int estimateLineCount(String str, float textSize, int width) { 343 if (width == 0) { 344 return -1; 345 } 346 Rect bounds = new Rect(); 347 Paint paint = new Paint(); 348 paint.setTextSize(textSize); 349 paint.getTextBounds(str, 0, str.length(), bounds); 350 return (bounds.width() + width - 1) / width; 351 } 352} 353