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