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.content.Context; 20 import android.media.tv.TvContract; 21 import android.support.annotation.Nullable; 22 import android.text.TextUtils; 23 import com.android.tv.R; 24 import com.android.tv.TvSingletons; 25 import com.android.tv.data.api.Channel; 26 import com.android.tv.dvr.data.RecordedProgram; 27 import com.android.tv.dvr.data.ScheduledRecording; 28 import com.android.tv.dvr.data.SeriesRecording; 29 import com.android.tv.dvr.ui.DvrUiHelper; 30 31 /** A class for details content. */ 32 class DetailsContent { 33 /** Constant for invalid time. */ 34 public static final long INVALID_TIME = -1; 35 36 private CharSequence mTitle; 37 private long mStartTimeUtcMillis; 38 private long mEndTimeUtcMillis; 39 private String mDescription; 40 private String mLogoImageUri; 41 private String mBackgroundImageUri; 42 private boolean mUsingChannelLogo; 43 createFromRecordedProgram( Context context, RecordedProgram recordedProgram)44 static DetailsContent createFromRecordedProgram( 45 Context context, RecordedProgram recordedProgram) { 46 return new DetailsContent.Builder() 47 .setChannelId(recordedProgram.getChannelId()) 48 .setProgramTitle(recordedProgram.getTitle()) 49 .setSeasonNumber(recordedProgram.getSeasonNumber()) 50 .setEpisodeNumber(recordedProgram.getEpisodeNumber()) 51 .setStartTimeUtcMillis(recordedProgram.getStartTimeUtcMillis()) 52 .setEndTimeUtcMillis(recordedProgram.getEndTimeUtcMillis()) 53 .setDescription( 54 TextUtils.isEmpty(recordedProgram.getLongDescription()) 55 ? recordedProgram.getDescription() 56 : recordedProgram.getLongDescription()) 57 .setPosterArtUri(recordedProgram.getPosterArtUri()) 58 .setThumbnailUri(recordedProgram.getThumbnailUri()) 59 .build(context); 60 } 61 createFromSeriesRecording( Context context, SeriesRecording seriesRecording)62 static DetailsContent createFromSeriesRecording( 63 Context context, SeriesRecording seriesRecording) { 64 return new DetailsContent.Builder() 65 .setChannelId(seriesRecording.getChannelId()) 66 .setTitle(seriesRecording.getTitle()) 67 .setDescription( 68 TextUtils.isEmpty(seriesRecording.getLongDescription()) 69 ? seriesRecording.getDescription() 70 : seriesRecording.getLongDescription()) 71 .setPosterArtUri(seriesRecording.getPosterUri()) 72 .setThumbnailUri(seriesRecording.getPhotoUri()) 73 .build(context); 74 } 75 createFromScheduledRecording( Context context, ScheduledRecording scheduledRecording)76 static DetailsContent createFromScheduledRecording( 77 Context context, ScheduledRecording scheduledRecording) { 78 Channel channel = 79 TvSingletons.getSingletons(context) 80 .getChannelDataManager() 81 .getChannel(scheduledRecording.getChannelId()); 82 String description = 83 !TextUtils.isEmpty(scheduledRecording.getProgramDescription()) 84 ? scheduledRecording.getProgramDescription() 85 : scheduledRecording.getProgramLongDescription(); 86 if (TextUtils.isEmpty(description)) { 87 description = channel != null ? channel.getDescription() : null; 88 } 89 return new DetailsContent.Builder() 90 .setChannelId(scheduledRecording.getChannelId()) 91 .setProgramTitle(scheduledRecording.getProgramTitle()) 92 .setSeasonNumber(scheduledRecording.getSeasonNumber()) 93 .setEpisodeNumber(scheduledRecording.getEpisodeNumber()) 94 .setStartTimeUtcMillis(scheduledRecording.getStartTimeMs()) 95 .setEndTimeUtcMillis(scheduledRecording.getEndTimeMs()) 96 .setDescription(description) 97 .setPosterArtUri(scheduledRecording.getProgramPosterArtUri()) 98 .setThumbnailUri(scheduledRecording.getProgramThumbnailUri()) 99 .build(context); 100 } 101 createFromFailedScheduledRecording( Context context, ScheduledRecording scheduledRecording, String errMsg)102 static DetailsContent createFromFailedScheduledRecording( 103 Context context, ScheduledRecording scheduledRecording, String errMsg) { 104 Channel channel = 105 TvSingletons.getSingletons(context) 106 .getChannelDataManager() 107 .getChannel(scheduledRecording.getChannelId()); 108 String description; 109 if (scheduledRecording.getState() == ScheduledRecording.STATE_RECORDING_FAILED 110 && errMsg != null) { 111 description = errMsg 112 + " (Error code: " + scheduledRecording.getFailedReason() + ")"; 113 } else { 114 description = 115 !TextUtils.isEmpty(scheduledRecording.getProgramDescription()) 116 ? scheduledRecording.getProgramDescription() 117 : scheduledRecording.getProgramLongDescription(); 118 } 119 if (TextUtils.isEmpty(description)) { 120 description = channel != null ? channel.getDescription() : null; 121 } 122 return new DetailsContent.Builder() 123 .setChannelId(scheduledRecording.getChannelId()) 124 .setProgramTitle(scheduledRecording.getProgramTitle()) 125 .setSeasonNumber(scheduledRecording.getSeasonNumber()) 126 .setEpisodeNumber(scheduledRecording.getEpisodeNumber()) 127 .setStartTimeUtcMillis(scheduledRecording.getStartTimeMs()) 128 .setEndTimeUtcMillis(scheduledRecording.getEndTimeMs()) 129 .setDescription(description) 130 .setPosterArtUri(scheduledRecording.getProgramPosterArtUri()) 131 .setThumbnailUri(scheduledRecording.getProgramThumbnailUri()) 132 .build(context); 133 } 134 DetailsContent()135 private DetailsContent() {} 136 137 /** Returns title. */ getTitle()138 public CharSequence getTitle() { 139 return mTitle; 140 } 141 142 /** Returns start time. */ getStartTimeUtcMillis()143 public long getStartTimeUtcMillis() { 144 return mStartTimeUtcMillis; 145 } 146 147 /** Returns end time. */ getEndTimeUtcMillis()148 public long getEndTimeUtcMillis() { 149 return mEndTimeUtcMillis; 150 } 151 152 /** Returns description. */ getDescription()153 public String getDescription() { 154 return mDescription; 155 } 156 157 /** Returns Logo image URI as a String. */ getLogoImageUri()158 public String getLogoImageUri() { 159 return mLogoImageUri; 160 } 161 162 /** Returns background image URI as a String. */ getBackgroundImageUri()163 public String getBackgroundImageUri() { 164 return mBackgroundImageUri; 165 } 166 167 /** Returns if image URIs are from its channels' logo. */ isUsingChannelLogo()168 public boolean isUsingChannelLogo() { 169 return mUsingChannelLogo; 170 } 171 172 /** Copies other details content. */ copyFrom(DetailsContent other)173 public void copyFrom(DetailsContent other) { 174 if (this == other) { 175 return; 176 } 177 mTitle = other.mTitle; 178 mStartTimeUtcMillis = other.mStartTimeUtcMillis; 179 mEndTimeUtcMillis = other.mEndTimeUtcMillis; 180 mDescription = other.mDescription; 181 mLogoImageUri = other.mLogoImageUri; 182 mBackgroundImageUri = other.mBackgroundImageUri; 183 mUsingChannelLogo = other.mUsingChannelLogo; 184 } 185 186 /** A class for building details content. */ 187 public static final class Builder { 188 private final DetailsContent mDetailsContent; 189 190 private long mChannelId; 191 private String mProgramTitle; 192 private String mSeasonNumber; 193 private String mEpisodeNumber; 194 private String mPosterArtUri; 195 private String mThumbnailUri; 196 Builder()197 public Builder() { 198 mDetailsContent = new DetailsContent(); 199 mDetailsContent.mStartTimeUtcMillis = INVALID_TIME; 200 mDetailsContent.mEndTimeUtcMillis = INVALID_TIME; 201 } 202 203 /** Sets title. */ setTitle(CharSequence title)204 public Builder setTitle(CharSequence title) { 205 mDetailsContent.mTitle = title; 206 return this; 207 } 208 209 /** Sets start time. */ setStartTimeUtcMillis(long startTimeUtcMillis)210 public Builder setStartTimeUtcMillis(long startTimeUtcMillis) { 211 mDetailsContent.mStartTimeUtcMillis = startTimeUtcMillis; 212 return this; 213 } 214 215 /** Sets end time. */ setEndTimeUtcMillis(long endTimeUtcMillis)216 public Builder setEndTimeUtcMillis(long endTimeUtcMillis) { 217 mDetailsContent.mEndTimeUtcMillis = endTimeUtcMillis; 218 return this; 219 } 220 221 /** Sets description. */ setDescription(String description)222 public Builder setDescription(String description) { 223 mDetailsContent.mDescription = description; 224 return this; 225 } 226 227 /** Sets logo image URI as a String. */ setLogoImageUri(String logoImageUri)228 public Builder setLogoImageUri(String logoImageUri) { 229 mDetailsContent.mLogoImageUri = logoImageUri; 230 return this; 231 } 232 233 /** Sets background image URI as a String. */ setBackgroundImageUri(String backgroundImageUri)234 public Builder setBackgroundImageUri(String backgroundImageUri) { 235 mDetailsContent.mBackgroundImageUri = backgroundImageUri; 236 return this; 237 } 238 setProgramTitle(String programTitle)239 private Builder setProgramTitle(String programTitle) { 240 mProgramTitle = programTitle; 241 return this; 242 } 243 setSeasonNumber(String seasonNumber)244 private Builder setSeasonNumber(String seasonNumber) { 245 mSeasonNumber = seasonNumber; 246 return this; 247 } 248 setEpisodeNumber(String episodeNumber)249 private Builder setEpisodeNumber(String episodeNumber) { 250 mEpisodeNumber = episodeNumber; 251 return this; 252 } 253 setChannelId(long channelId)254 private Builder setChannelId(long channelId) { 255 mChannelId = channelId; 256 return this; 257 } 258 setPosterArtUri(String posterArtUri)259 private Builder setPosterArtUri(String posterArtUri) { 260 mPosterArtUri = posterArtUri; 261 return this; 262 } 263 setThumbnailUri(String thumbnailUri)264 private Builder setThumbnailUri(String thumbnailUri) { 265 mThumbnailUri = thumbnailUri; 266 return this; 267 } 268 createStyledTitle(Context context, Channel channel)269 private void createStyledTitle(Context context, Channel channel) { 270 CharSequence title = 271 DvrUiHelper.getStyledTitleWithEpisodeNumber( 272 context, 273 mProgramTitle, 274 mSeasonNumber, 275 mEpisodeNumber, 276 R.style.text_appearance_card_view_episode_number); 277 if (TextUtils.isEmpty(title)) { 278 mDetailsContent.mTitle = 279 channel != null 280 ? channel.getDisplayName() 281 : context.getResources().getString(R.string.no_program_information); 282 } else { 283 mDetailsContent.mTitle = title; 284 } 285 } 286 createImageUris(@ullable Channel channel)287 private void createImageUris(@Nullable Channel channel) { 288 mDetailsContent.mLogoImageUri = null; 289 mDetailsContent.mBackgroundImageUri = null; 290 mDetailsContent.mUsingChannelLogo = false; 291 if (!TextUtils.isEmpty(mPosterArtUri) && !TextUtils.isEmpty(mThumbnailUri)) { 292 mDetailsContent.mLogoImageUri = mPosterArtUri; 293 mDetailsContent.mBackgroundImageUri = mThumbnailUri; 294 } else if (!TextUtils.isEmpty(mPosterArtUri)) { 295 // thumbnailUri is empty 296 mDetailsContent.mLogoImageUri = mPosterArtUri; 297 mDetailsContent.mBackgroundImageUri = mPosterArtUri; 298 } else if (!TextUtils.isEmpty(mThumbnailUri)) { 299 // posterArtUri is empty 300 mDetailsContent.mLogoImageUri = mThumbnailUri; 301 mDetailsContent.mBackgroundImageUri = mThumbnailUri; 302 } 303 if (TextUtils.isEmpty(mDetailsContent.mLogoImageUri) && channel != null) { 304 String channelLogoUri = TvContract.buildChannelLogoUri(channel.getId()).toString(); 305 mDetailsContent.mLogoImageUri = channelLogoUri; 306 mDetailsContent.mBackgroundImageUri = channelLogoUri; 307 mDetailsContent.mUsingChannelLogo = true; 308 } 309 } 310 311 /** Builds details content. */ build(Context context)312 public DetailsContent build(Context context) { 313 Channel channel = 314 TvSingletons.getSingletons(context) 315 .getChannelDataManager() 316 .getChannel(mChannelId); 317 if (mDetailsContent.mTitle == null) { 318 createStyledTitle(context, channel); 319 } 320 if (mDetailsContent.mBackgroundImageUri == null 321 && mDetailsContent.mLogoImageUri == null) { 322 createImageUris(channel); 323 } 324 DetailsContent detailsContent = new DetailsContent(); 325 detailsContent.copyFrom(mDetailsContent); 326 return detailsContent; 327 } 328 } 329 } 330