• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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.data;
18 
19 import android.content.ContentValues;
20 import android.content.Context;
21 import android.database.Cursor;
22 import android.os.Parcel;
23 import android.os.Parcelable;
24 import android.support.annotation.IntDef;
25 import android.text.TextUtils;
26 import android.util.Range;
27 
28 import com.android.tv.R;
29 import com.android.tv.TvApplication;
30 import com.android.tv.common.SoftPreconditions;
31 import com.android.tv.data.Channel;
32 import com.android.tv.data.Program;
33 import com.android.tv.dvr.DvrScheduleManager;
34 import com.android.tv.dvr.provider.DvrContract.Schedules;
35 import com.android.tv.util.CompositeComparator;
36 import com.android.tv.util.Utils;
37 
38 import java.lang.annotation.Retention;
39 import java.lang.annotation.RetentionPolicy;
40 import java.util.Collection;
41 import java.util.Comparator;
42 import java.util.Objects;
43 
44 /**
45  * A data class for one recording contents.
46  */
47 public final class ScheduledRecording implements Parcelable {
48     private static final String TAG = "ScheduledRecording";
49 
50     /**
51      * Indicates that the ID is not assigned yet.
52      */
53     public static final long ID_NOT_SET = 0;
54 
55     /**
56      * The default priority of the recording.
57      */
58     public static final long DEFAULT_PRIORITY = Long.MAX_VALUE >> 1;
59 
60     /**
61      * Compares the start time in ascending order.
62      */
63     public static final Comparator<ScheduledRecording> START_TIME_COMPARATOR
64             = new Comparator<ScheduledRecording>() {
65         @Override
66         public int compare(ScheduledRecording lhs, ScheduledRecording rhs) {
67             return Long.compare(lhs.mStartTimeMs, rhs.mStartTimeMs);
68         }
69     };
70 
71     /**
72      * Compares the end time in ascending order.
73      */
74     public static final Comparator<ScheduledRecording> END_TIME_COMPARATOR
75             = new Comparator<ScheduledRecording>() {
76         @Override
77         public int compare(ScheduledRecording lhs, ScheduledRecording rhs) {
78             return Long.compare(lhs.mEndTimeMs, rhs.mEndTimeMs);
79         }
80     };
81 
82     /**
83      * Compares ID in ascending order. The schedule with the larger ID was created later.
84      */
85     public static final Comparator<ScheduledRecording> ID_COMPARATOR
86             = new Comparator<ScheduledRecording>() {
87         @Override
88         public int compare(ScheduledRecording lhs, ScheduledRecording rhs) {
89             return Long.compare(lhs.mId, rhs.mId);
90         }
91     };
92 
93     /**
94      * Compares the priority in ascending order.
95      */
96     public static final Comparator<ScheduledRecording> PRIORITY_COMPARATOR
97             = new Comparator<ScheduledRecording>() {
98         @Override
99         public int compare(ScheduledRecording lhs, ScheduledRecording rhs) {
100             return Long.compare(lhs.mPriority, rhs.mPriority);
101         }
102     };
103 
104     /**
105      * Compares start time in ascending order and then priority in descending order and then ID in
106      * descending order.
107      */
108     public static final Comparator<ScheduledRecording> START_TIME_THEN_PRIORITY_THEN_ID_COMPARATOR
109             = new CompositeComparator<>(START_TIME_COMPARATOR, PRIORITY_COMPARATOR.reversed(),
110             ID_COMPARATOR.reversed());
111 
112     /**
113      * Builds scheduled recordings from programs.
114      */
builder(String inputId, Program p)115     public static Builder builder(String inputId, Program p) {
116         return new Builder()
117                 .setInputId(inputId)
118                 .setChannelId(p.getChannelId())
119                 .setStartTimeMs(p.getStartTimeUtcMillis()).setEndTimeMs(p.getEndTimeUtcMillis())
120                 .setProgramId(p.getId())
121                 .setProgramTitle(p.getTitle())
122                 .setSeasonNumber(p.getSeasonNumber())
123                 .setEpisodeNumber(p.getEpisodeNumber())
124                 .setEpisodeTitle(p.getEpisodeTitle())
125                 .setProgramDescription(p.getDescription())
126                 .setProgramLongDescription(p.getLongDescription())
127                 .setProgramPosterArtUri(p.getPosterArtUri())
128                 .setProgramThumbnailUri(p.getThumbnailUri())
129                 .setType(TYPE_PROGRAM);
130     }
131 
builder(String inputId, long channelId, long startTime, long endTime)132     public static Builder builder(String inputId, long channelId, long startTime, long endTime) {
133         return new Builder()
134                 .setInputId(inputId)
135                 .setChannelId(channelId)
136                 .setStartTimeMs(startTime)
137                 .setEndTimeMs(endTime)
138                 .setType(TYPE_TIMED);
139     }
140 
141     /**
142      * Creates a new Builder with the values set from the {@link RecordedProgram}.
143      */
builder(RecordedProgram p)144     public static Builder builder(RecordedProgram p) {
145         boolean isProgramRecording = !TextUtils.isEmpty(p.getTitle());
146         return new Builder()
147                 .setInputId(p.getInputId())
148                 .setChannelId(p.getChannelId())
149                 .setType(isProgramRecording ? TYPE_PROGRAM : TYPE_TIMED)
150                 .setStartTimeMs(p.getStartTimeUtcMillis())
151                 .setEndTimeMs(p.getEndTimeUtcMillis())
152                 .setProgramTitle(p.getTitle())
153                 .setSeasonNumber(p.getSeasonNumber())
154                 .setEpisodeNumber(p.getEpisodeNumber())
155                 .setEpisodeTitle(p.getEpisodeTitle())
156                 .setProgramDescription(p.getDescription())
157                 .setProgramLongDescription(p.getLongDescription())
158                 .setProgramPosterArtUri(p.getPosterArtUri())
159                 .setProgramThumbnailUri(p.getThumbnailUri())
160                 .setState(STATE_RECORDING_FINISHED);
161     }
162 
163     public static final class Builder {
164         private long mId = ID_NOT_SET;
165         private long mPriority = DvrScheduleManager.DEFAULT_PRIORITY;
166         private String mInputId;
167         private long mChannelId;
168         private long mProgramId = ID_NOT_SET;
169         private String mProgramTitle;
170         private @RecordingType int mType;
171         private long mStartTimeMs;
172         private long mEndTimeMs;
173         private String mSeasonNumber;
174         private String mEpisodeNumber;
175         private String mEpisodeTitle;
176         private String mProgramDescription;
177         private String mProgramLongDescription;
178         private String mProgramPosterArtUri;
179         private String mProgramThumbnailUri;
180         private @RecordingState int mState;
181         private long mSeriesRecordingId = ID_NOT_SET;
182 
Builder()183         private Builder() { }
184 
setId(long id)185         public Builder setId(long id) {
186             mId = id;
187             return this;
188         }
189 
setPriority(long priority)190         public Builder setPriority(long priority) {
191             mPriority = priority;
192             return this;
193         }
194 
setInputId(String inputId)195         public Builder setInputId(String inputId) {
196             mInputId = inputId;
197             return this;
198         }
199 
setChannelId(long channelId)200         public Builder setChannelId(long channelId) {
201             mChannelId = channelId;
202             return this;
203         }
204 
setProgramId(long programId)205         public Builder setProgramId(long programId) {
206             mProgramId = programId;
207             return this;
208         }
209 
setProgramTitle(String programTitle)210         public Builder setProgramTitle(String programTitle) {
211             mProgramTitle = programTitle;
212             return this;
213         }
214 
setType(@ecordingType int type)215         private Builder setType(@RecordingType int type) {
216             mType = type;
217             return this;
218         }
219 
setStartTimeMs(long startTimeMs)220         public Builder setStartTimeMs(long startTimeMs) {
221             mStartTimeMs = startTimeMs;
222             return this;
223         }
224 
setEndTimeMs(long endTimeMs)225         public Builder setEndTimeMs(long endTimeMs) {
226             mEndTimeMs = endTimeMs;
227             return this;
228         }
229 
setSeasonNumber(String seasonNumber)230         public Builder setSeasonNumber(String seasonNumber) {
231             mSeasonNumber = seasonNumber;
232             return this;
233         }
234 
setEpisodeNumber(String episodeNumber)235         public Builder setEpisodeNumber(String episodeNumber) {
236             mEpisodeNumber = episodeNumber;
237             return this;
238         }
239 
setEpisodeTitle(String episodeTitle)240         public Builder setEpisodeTitle(String episodeTitle) {
241             mEpisodeTitle = episodeTitle;
242             return this;
243         }
244 
setProgramDescription(String description)245         public Builder setProgramDescription(String description) {
246             mProgramDescription = description;
247             return this;
248         }
249 
setProgramLongDescription(String longDescription)250         public Builder setProgramLongDescription(String longDescription) {
251             mProgramLongDescription = longDescription;
252             return this;
253         }
254 
setProgramPosterArtUri(String programPosterArtUri)255         public Builder setProgramPosterArtUri(String programPosterArtUri) {
256             mProgramPosterArtUri = programPosterArtUri;
257             return this;
258         }
259 
setProgramThumbnailUri(String programThumbnailUri)260         public Builder setProgramThumbnailUri(String programThumbnailUri) {
261             mProgramThumbnailUri = programThumbnailUri;
262             return this;
263         }
264 
setState(@ecordingState int state)265         public Builder setState(@RecordingState int state) {
266             mState = state;
267             return this;
268         }
269 
setSeriesRecordingId(long seriesRecordingId)270         public Builder setSeriesRecordingId(long seriesRecordingId) {
271             mSeriesRecordingId = seriesRecordingId;
272             return this;
273         }
274 
build()275         public ScheduledRecording build() {
276             return new ScheduledRecording(mId, mPriority, mInputId, mChannelId, mProgramId,
277                     mProgramTitle, mType, mStartTimeMs, mEndTimeMs, mSeasonNumber, mEpisodeNumber,
278                     mEpisodeTitle, mProgramDescription, mProgramLongDescription,
279                     mProgramPosterArtUri, mProgramThumbnailUri, mState, mSeriesRecordingId);
280         }
281     }
282 
283     /**
284      * Creates {@link Builder} object from the given original {@code Recording}.
285      */
buildFrom(ScheduledRecording orig)286     public static Builder buildFrom(ScheduledRecording orig) {
287         return new Builder()
288                 .setId(orig.mId)
289                 .setInputId(orig.mInputId)
290                 .setChannelId(orig.mChannelId)
291                 .setEndTimeMs(orig.mEndTimeMs)
292                 .setSeriesRecordingId(orig.mSeriesRecordingId)
293                 .setPriority(orig.mPriority)
294                 .setProgramId(orig.mProgramId)
295                 .setProgramTitle(orig.mProgramTitle)
296                 .setStartTimeMs(orig.mStartTimeMs)
297                 .setSeasonNumber(orig.getSeasonNumber())
298                 .setEpisodeNumber(orig.getEpisodeNumber())
299                 .setEpisodeTitle(orig.getEpisodeTitle())
300                 .setProgramDescription(orig.getProgramDescription())
301                 .setProgramLongDescription(orig.getProgramLongDescription())
302                 .setProgramPosterArtUri(orig.getProgramPosterArtUri())
303                 .setProgramThumbnailUri(orig.getProgramThumbnailUri())
304                 .setState(orig.mState).setType(orig.mType);
305     }
306 
307     @Retention(RetentionPolicy.SOURCE)
308     @IntDef({STATE_RECORDING_NOT_STARTED, STATE_RECORDING_IN_PROGRESS, STATE_RECORDING_FINISHED,
309             STATE_RECORDING_FAILED, STATE_RECORDING_CLIPPED, STATE_RECORDING_DELETED,
310             STATE_RECORDING_CANCELED})
311     public @interface RecordingState {}
312     public static final int STATE_RECORDING_NOT_STARTED = 0;
313     public static final int STATE_RECORDING_IN_PROGRESS = 1;
314     public static final int STATE_RECORDING_FINISHED = 2;
315     public static final int STATE_RECORDING_FAILED = 3;
316     public static final int STATE_RECORDING_CLIPPED = 4;
317     public static final int STATE_RECORDING_DELETED = 5;
318     public static final int STATE_RECORDING_CANCELED = 6;
319 
320     @Retention(RetentionPolicy.SOURCE)
321     @IntDef({TYPE_TIMED, TYPE_PROGRAM})
322     public @interface RecordingType {}
323     /**
324      * Record with given time range.
325      */
326     public static final int TYPE_TIMED = 1;
327     /**
328      * Record with a given program.
329      */
330     public static final int TYPE_PROGRAM = 2;
331 
332     @RecordingType private final int mType;
333 
334     /**
335      * Use this projection if you want to create {@link ScheduledRecording} object using
336      * {@link #fromCursor}.
337      */
338     public static final String[] PROJECTION = {
339             // Columns must match what is read in #fromCursor
340             Schedules._ID,
341             Schedules.COLUMN_PRIORITY,
342             Schedules.COLUMN_TYPE,
343             Schedules.COLUMN_INPUT_ID,
344             Schedules.COLUMN_CHANNEL_ID,
345             Schedules.COLUMN_PROGRAM_ID,
346             Schedules.COLUMN_PROGRAM_TITLE,
347             Schedules.COLUMN_START_TIME_UTC_MILLIS,
348             Schedules.COLUMN_END_TIME_UTC_MILLIS,
349             Schedules.COLUMN_SEASON_NUMBER,
350             Schedules.COLUMN_EPISODE_NUMBER,
351             Schedules.COLUMN_EPISODE_TITLE,
352             Schedules.COLUMN_PROGRAM_DESCRIPTION,
353             Schedules.COLUMN_PROGRAM_LONG_DESCRIPTION,
354             Schedules.COLUMN_PROGRAM_POST_ART_URI,
355             Schedules.COLUMN_PROGRAM_THUMBNAIL_URI,
356             Schedules.COLUMN_STATE,
357             Schedules.COLUMN_SERIES_RECORDING_ID};
358 
359     /**
360      * Creates {@link ScheduledRecording} object from the given {@link Cursor}.
361      */
fromCursor(Cursor c)362     public static ScheduledRecording fromCursor(Cursor c) {
363         int index = -1;
364         return new Builder()
365                 .setId(c.getLong(++index))
366                 .setPriority(c.getLong(++index))
367                 .setType(recordingType(c.getString(++index)))
368                 .setInputId(c.getString(++index))
369                 .setChannelId(c.getLong(++index))
370                 .setProgramId(c.getLong(++index))
371                 .setProgramTitle(c.getString(++index))
372                 .setStartTimeMs(c.getLong(++index))
373                 .setEndTimeMs(c.getLong(++index))
374                 .setSeasonNumber(c.getString(++index))
375                 .setEpisodeNumber(c.getString(++index))
376                 .setEpisodeTitle(c.getString(++index))
377                 .setProgramDescription(c.getString(++index))
378                 .setProgramLongDescription(c.getString(++index))
379                 .setProgramPosterArtUri(c.getString(++index))
380                 .setProgramThumbnailUri(c.getString(++index))
381                 .setState(recordingState(c.getString(++index)))
382                 .setSeriesRecordingId(c.getLong(++index))
383                 .build();
384     }
385 
toContentValues(ScheduledRecording r)386     public static ContentValues toContentValues(ScheduledRecording r) {
387         ContentValues values = new ContentValues();
388         if (r.getId() != ID_NOT_SET) {
389             values.put(Schedules._ID, r.getId());
390         }
391         values.put(Schedules.COLUMN_INPUT_ID, r.getInputId());
392         values.put(Schedules.COLUMN_CHANNEL_ID, r.getChannelId());
393         values.put(Schedules.COLUMN_PROGRAM_ID, r.getProgramId());
394         values.put(Schedules.COLUMN_PROGRAM_TITLE, r.getProgramTitle());
395         values.put(Schedules.COLUMN_PRIORITY, r.getPriority());
396         values.put(Schedules.COLUMN_START_TIME_UTC_MILLIS, r.getStartTimeMs());
397         values.put(Schedules.COLUMN_END_TIME_UTC_MILLIS, r.getEndTimeMs());
398         values.put(Schedules.COLUMN_SEASON_NUMBER, r.getSeasonNumber());
399         values.put(Schedules.COLUMN_EPISODE_NUMBER, r.getEpisodeNumber());
400         values.put(Schedules.COLUMN_EPISODE_TITLE, r.getEpisodeTitle());
401         values.put(Schedules.COLUMN_PROGRAM_DESCRIPTION, r.getProgramDescription());
402         values.put(Schedules.COLUMN_PROGRAM_LONG_DESCRIPTION, r.getProgramLongDescription());
403         values.put(Schedules.COLUMN_PROGRAM_POST_ART_URI, r.getProgramPosterArtUri());
404         values.put(Schedules.COLUMN_PROGRAM_THUMBNAIL_URI, r.getProgramThumbnailUri());
405         values.put(Schedules.COLUMN_STATE, recordingState(r.getState()));
406         values.put(Schedules.COLUMN_TYPE, recordingType(r.getType()));
407         if (r.getSeriesRecordingId() != ID_NOT_SET) {
408             values.put(Schedules.COLUMN_SERIES_RECORDING_ID, r.getSeriesRecordingId());
409         } else {
410             values.putNull(Schedules.COLUMN_SERIES_RECORDING_ID);
411         }
412         return values;
413     }
414 
fromParcel(Parcel in)415     public static ScheduledRecording fromParcel(Parcel in) {
416         return new Builder()
417                 .setId(in.readLong())
418                 .setPriority(in.readLong())
419                 .setInputId(in.readString())
420                 .setChannelId(in.readLong())
421                 .setProgramId(in.readLong())
422                 .setProgramTitle(in.readString())
423                 .setType(in.readInt())
424                 .setStartTimeMs(in.readLong())
425                 .setEndTimeMs(in.readLong())
426                 .setSeasonNumber(in.readString())
427                 .setEpisodeNumber(in.readString())
428                 .setEpisodeTitle(in.readString())
429                 .setProgramDescription(in.readString())
430                 .setProgramLongDescription(in.readString())
431                 .setProgramPosterArtUri(in.readString())
432                 .setProgramThumbnailUri(in.readString())
433                 .setState(in.readInt())
434                 .setSeriesRecordingId(in.readLong())
435                 .build();
436     }
437 
438     public static final Parcelable.Creator<ScheduledRecording> CREATOR =
439             new Parcelable.Creator<ScheduledRecording>() {
440         @Override
441         public ScheduledRecording createFromParcel(Parcel in) {
442           return ScheduledRecording.fromParcel(in);
443         }
444 
445         @Override
446         public ScheduledRecording[] newArray(int size) {
447           return new ScheduledRecording[size];
448         }
449     };
450 
451     /**
452      * The ID internal to Live TV
453      */
454     private long mId;
455 
456     /**
457      * The priority of this recording.
458      *
459      * <p> The highest number is recorded first. If there is a tie in priority then the higher id
460      * wins.
461      */
462     private final long mPriority;
463 
464     private final String mInputId;
465     private final long mChannelId;
466     /**
467      * Optional id of the associated program.
468      */
469     private final long mProgramId;
470     private final String mProgramTitle;
471 
472     private final long mStartTimeMs;
473     private final long mEndTimeMs;
474     private final String mSeasonNumber;
475     private final String mEpisodeNumber;
476     private final String mEpisodeTitle;
477     private final String mProgramDescription;
478     private final String mProgramLongDescription;
479     private final String mProgramPosterArtUri;
480     private final String mProgramThumbnailUri;
481     @RecordingState private final int mState;
482     private final long mSeriesRecordingId;
483 
ScheduledRecording(long id, long priority, String inputId, long channelId, long programId, String programTitle, @RecordingType int type, long startTime, long endTime, String seasonNumber, String episodeNumber, String episodeTitle, String programDescription, String programLongDescription, String programPosterArtUri, String programThumbnailUri, @RecordingState int state, long seriesRecordingId)484     private ScheduledRecording(long id, long priority, String inputId, long channelId, long programId,
485             String programTitle, @RecordingType int type, long startTime, long endTime,
486             String seasonNumber, String episodeNumber, String episodeTitle,
487             String programDescription, String programLongDescription, String programPosterArtUri,
488             String programThumbnailUri, @RecordingState int state, long seriesRecordingId) {
489         mId = id;
490         mPriority = priority;
491         mInputId = inputId;
492         mChannelId = channelId;
493         mProgramId = programId;
494         mProgramTitle = programTitle;
495         mType = type;
496         mStartTimeMs = startTime;
497         mEndTimeMs = endTime;
498         mSeasonNumber = seasonNumber;
499         mEpisodeNumber = episodeNumber;
500         mEpisodeTitle = episodeTitle;
501         mProgramDescription = programDescription;
502         mProgramLongDescription = programLongDescription;
503         mProgramPosterArtUri = programPosterArtUri;
504         mProgramThumbnailUri = programThumbnailUri;
505         mState = state;
506         mSeriesRecordingId = seriesRecordingId;
507     }
508 
509     /**
510      * Returns recording schedule type. The possible types are {@link #TYPE_PROGRAM} and
511      * {@link #TYPE_TIMED}.
512      */
513     @RecordingType
getType()514     public int getType() {
515         return mType;
516     }
517 
518     /**
519      * Returns schedules' input id.
520      */
getInputId()521     public String getInputId() {
522         return mInputId;
523     }
524 
525     /**
526      * Returns recorded {@link Channel}.
527      */
getChannelId()528     public long getChannelId() {
529         return mChannelId;
530     }
531 
532     /**
533      * Return the optional program id
534      */
getProgramId()535     public long getProgramId() {
536         return mProgramId;
537     }
538 
539     /**
540      * Return the optional program Title
541      */
getProgramTitle()542     public String getProgramTitle() {
543         return mProgramTitle;
544     }
545 
546     /**
547      * Returns started time.
548      */
getStartTimeMs()549     public long getStartTimeMs() {
550         return mStartTimeMs;
551     }
552 
553     /**
554      * Returns ended time.
555      */
getEndTimeMs()556     public long getEndTimeMs() {
557         return mEndTimeMs;
558     }
559 
560     /**
561      * Returns the season number.
562      */
getSeasonNumber()563     public String getSeasonNumber() {
564         return mSeasonNumber;
565     }
566 
567     /**
568      * Returns the episode number.
569      */
getEpisodeNumber()570     public String getEpisodeNumber() {
571         return mEpisodeNumber;
572     }
573 
574     /**
575      * Returns the episode title.
576      */
getEpisodeTitle()577     public String getEpisodeTitle() {
578         return mEpisodeTitle;
579     }
580 
581     /**
582      * Returns the description of program.
583      */
getProgramDescription()584     public String getProgramDescription() {
585         return mProgramDescription;
586     }
587 
588     /**
589      * Returns the long description of program.
590      */
getProgramLongDescription()591     public String getProgramLongDescription() {
592         return mProgramLongDescription;
593     }
594 
595     /**
596      * Returns the poster uri of program.
597      */
getProgramPosterArtUri()598     public String getProgramPosterArtUri() {
599         return mProgramPosterArtUri;
600     }
601 
602     /**
603      * Returns the thumb nail uri of program.
604      */
getProgramThumbnailUri()605     public String getProgramThumbnailUri() {
606         return mProgramThumbnailUri;
607     }
608 
609     /**
610      * Returns duration.
611      */
getDuration()612     public long getDuration() {
613         return mEndTimeMs - mStartTimeMs;
614     }
615 
616     /**
617      * Returns the state. The possible states are {@link #STATE_RECORDING_NOT_STARTED},
618      * {@link #STATE_RECORDING_IN_PROGRESS}, {@link #STATE_RECORDING_FINISHED},
619      * {@link #STATE_RECORDING_FAILED}, {@link #STATE_RECORDING_CLIPPED} and
620      * {@link #STATE_RECORDING_DELETED}.
621      */
getState()622     @RecordingState public int getState() {
623         return mState;
624     }
625 
626     /**
627      * Returns the ID of the {@link SeriesRecording} including this schedule.
628      */
getSeriesRecordingId()629     public long getSeriesRecordingId() {
630         return mSeriesRecordingId;
631     }
632 
getId()633     public long getId() {
634         return mId;
635     }
636 
637     /**
638      * Sets the ID;
639      */
setId(long id)640     public void setId(long id) {
641         mId = id;
642     }
643 
getPriority()644     public long getPriority() {
645         return mPriority;
646     }
647 
648     /**
649      * Returns season number, episode number and episode title for display.
650      */
getEpisodeDisplayTitle(Context context)651     public String getEpisodeDisplayTitle(Context context) {
652         if (!TextUtils.isEmpty(mEpisodeNumber)) {
653             String episodeTitle = mEpisodeTitle == null ? "" : mEpisodeTitle;
654             if (TextUtils.equals(mSeasonNumber, "0")) {
655                 // Do not show "S0: ".
656                 return String.format(context.getResources().getString(
657                         R.string.display_episode_title_format_no_season_number),
658                         mEpisodeNumber, episodeTitle);
659             } else {
660                 return String.format(context.getResources().getString(
661                         R.string.display_episode_title_format),
662                         mSeasonNumber, mEpisodeNumber, episodeTitle);
663             }
664         }
665         return mEpisodeTitle;
666     }
667 
668     /**
669      * Returns the program's display title, if the program title is not null, returns program title.
670      * Otherwise returns the channel name.
671      */
getProgramDisplayTitle(Context context)672     public String getProgramDisplayTitle(Context context) {
673         if (!TextUtils.isEmpty(mProgramTitle)) {
674             return mProgramTitle;
675         }
676         Channel channel = TvApplication.getSingletons(context).getChannelDataManager()
677                 .getChannel(mChannelId);
678         return channel != null ? channel.getDisplayName()
679                 : context.getString(R.string.no_program_information);
680     }
681 
682     /**
683      * Converts a string to a @RecordingType int, defaulting to {@link #TYPE_TIMED}.
684      */
recordingType(String type)685     private static @RecordingType int recordingType(String type) {
686         switch (type) {
687             case Schedules.TYPE_TIMED:
688                 return TYPE_TIMED;
689             case Schedules.TYPE_PROGRAM:
690                 return TYPE_PROGRAM;
691             default:
692                 SoftPreconditions.checkArgument(false, TAG, "Unknown recording type " + type);
693                 return TYPE_TIMED;
694         }
695     }
696 
697     /**
698      * Converts a @RecordingType int to a string, defaulting to {@link Schedules#TYPE_TIMED}.
699      */
recordingType(@ecordingType int type)700     private static String recordingType(@RecordingType int type) {
701         switch (type) {
702             case TYPE_TIMED:
703                 return Schedules.TYPE_TIMED;
704             case TYPE_PROGRAM:
705                 return Schedules.TYPE_PROGRAM;
706             default:
707                 SoftPreconditions.checkArgument(false, TAG, "Unknown recording type " + type);
708                 return Schedules.TYPE_TIMED;
709         }
710     }
711 
712     /**
713      * Converts a string to a @RecordingState int, defaulting to
714      * {@link #STATE_RECORDING_NOT_STARTED}.
715      */
recordingState(String state)716     private static @RecordingState int recordingState(String state) {
717         switch (state) {
718             case Schedules.STATE_RECORDING_NOT_STARTED:
719                 return STATE_RECORDING_NOT_STARTED;
720             case Schedules.STATE_RECORDING_IN_PROGRESS:
721                 return STATE_RECORDING_IN_PROGRESS;
722             case Schedules.STATE_RECORDING_FINISHED:
723                 return STATE_RECORDING_FINISHED;
724             case Schedules.STATE_RECORDING_FAILED:
725                 return STATE_RECORDING_FAILED;
726             case Schedules.STATE_RECORDING_CLIPPED:
727                 return STATE_RECORDING_CLIPPED;
728             case Schedules.STATE_RECORDING_DELETED:
729                 return STATE_RECORDING_DELETED;
730             case Schedules.STATE_RECORDING_CANCELED:
731                 return STATE_RECORDING_CANCELED;
732             default:
733                 SoftPreconditions.checkArgument(false, TAG, "Unknown recording state" + state);
734                 return STATE_RECORDING_NOT_STARTED;
735         }
736     }
737 
738     /**
739      * Converts a @RecordingState int to string, defaulting to
740      * {@link Schedules#STATE_RECORDING_NOT_STARTED}.
741      */
recordingState(@ecordingState int state)742     private static String recordingState(@RecordingState int state) {
743         switch (state) {
744             case STATE_RECORDING_NOT_STARTED:
745                 return Schedules.STATE_RECORDING_NOT_STARTED;
746             case STATE_RECORDING_IN_PROGRESS:
747                 return Schedules.STATE_RECORDING_IN_PROGRESS;
748             case STATE_RECORDING_FINISHED:
749                 return Schedules.STATE_RECORDING_FINISHED;
750             case STATE_RECORDING_FAILED:
751                 return Schedules.STATE_RECORDING_FAILED;
752             case STATE_RECORDING_CLIPPED:
753                 return Schedules.STATE_RECORDING_CLIPPED;
754             case STATE_RECORDING_DELETED:
755                 return Schedules.STATE_RECORDING_DELETED;
756             case STATE_RECORDING_CANCELED:
757                 return Schedules.STATE_RECORDING_CANCELED;
758             default:
759                 SoftPreconditions.checkArgument(false, TAG, "Unknown recording state" + state);
760                 return Schedules.STATE_RECORDING_NOT_STARTED;
761         }
762     }
763 
764     /**
765      * Checks if the {@code period} overlaps with the recording time.
766      */
isOverLapping(Range<Long> period)767     public boolean isOverLapping(Range<Long> period) {
768         return mStartTimeMs < period.getUpper() && mEndTimeMs > period.getLower();
769     }
770 
771     /**
772      * Checks if the {@code schedule} overlaps with this schedule.
773      */
isOverLapping(ScheduledRecording schedule)774     public boolean isOverLapping(ScheduledRecording schedule) {
775         return mStartTimeMs < schedule.getEndTimeMs() && mEndTimeMs > schedule.getStartTimeMs();
776     }
777 
778     @Override
toString()779     public String toString() {
780         return "ScheduledRecording[" + mId
781                 + "]"
782                 + "(inputId=" + mInputId
783                 + ",channelId=" + mChannelId
784                 + ",programId=" + mProgramId
785                 + ",programTitle=" + mProgramTitle
786                 + ",type=" + mType
787                 + ",startTime=" + Utils.toIsoDateTimeString(mStartTimeMs) + "(" + mStartTimeMs + ")"
788                 + ",endTime=" + Utils.toIsoDateTimeString(mEndTimeMs) + "(" + mEndTimeMs + ")"
789                 + ",seasonNumber=" + mSeasonNumber
790                 + ",episodeNumber=" + mEpisodeNumber
791                 + ",episodeTitle=" + mEpisodeTitle
792                 + ",programDescription=" + mProgramDescription
793                 + ",programLongDescription=" + mProgramLongDescription
794                 + ",programPosterArtUri=" + mProgramPosterArtUri
795                 + ",programThumbnailUri=" + mProgramThumbnailUri
796                 + ",state=" + mState
797                 + ",priority=" + mPriority
798                 + ",seriesRecordingId=" + mSeriesRecordingId
799                 + ")";
800     }
801 
802     @Override
describeContents()803     public int describeContents() {
804         return 0;
805     }
806 
807     @Override
writeToParcel(Parcel out, int paramInt)808     public void writeToParcel(Parcel out, int paramInt) {
809         out.writeLong(mId);
810         out.writeLong(mPriority);
811         out.writeString(mInputId);
812         out.writeLong(mChannelId);
813         out.writeLong(mProgramId);
814         out.writeString(mProgramTitle);
815         out.writeInt(mType);
816         out.writeLong(mStartTimeMs);
817         out.writeLong(mEndTimeMs);
818         out.writeString(mSeasonNumber);
819         out.writeString(mEpisodeNumber);
820         out.writeString(mEpisodeTitle);
821         out.writeString(mProgramDescription);
822         out.writeString(mProgramLongDescription);
823         out.writeString(mProgramPosterArtUri);
824         out.writeString(mProgramThumbnailUri);
825         out.writeInt(mState);
826         out.writeLong(mSeriesRecordingId);
827     }
828 
829     /**
830      * Returns {@code true} if the recording is not started yet, otherwise @{code false}.
831      */
isNotStarted()832     public boolean isNotStarted() {
833         return mState == STATE_RECORDING_NOT_STARTED;
834     }
835 
836     /**
837      * Returns {@code true} if the recording is in progress, otherwise @{code false}.
838      */
isInProgress()839     public boolean isInProgress() {
840         return mState == STATE_RECORDING_IN_PROGRESS;
841     }
842 
843     @Override
equals(Object obj)844     public boolean equals(Object obj) {
845         if (!(obj instanceof ScheduledRecording)) {
846             return false;
847         }
848         ScheduledRecording r = (ScheduledRecording) obj;
849         return mId == r.mId
850                 && mPriority == r.mPriority
851                 && mChannelId == r.mChannelId
852                 && mProgramId == r.mProgramId
853                 && Objects.equals(mProgramTitle, r.mProgramTitle)
854                 && mType == r.mType
855                 && mStartTimeMs == r.mStartTimeMs
856                 && mEndTimeMs == r.mEndTimeMs
857                 && Objects.equals(mSeasonNumber, r.mSeasonNumber)
858                 && Objects.equals(mEpisodeNumber, r.mEpisodeNumber)
859                 && Objects.equals(mEpisodeTitle, r.mEpisodeTitle)
860                 && Objects.equals(mProgramDescription, r.getProgramDescription())
861                 && Objects.equals(mProgramLongDescription, r.getProgramLongDescription())
862                 && Objects.equals(mProgramPosterArtUri, r.getProgramPosterArtUri())
863                 && Objects.equals(mProgramThumbnailUri, r.getProgramThumbnailUri())
864                 && mState == r.mState
865                 && mSeriesRecordingId == r.mSeriesRecordingId;
866     }
867 
868     @Override
hashCode()869     public int hashCode() {
870         return Objects.hash(mId, mPriority, mChannelId, mProgramId, mProgramTitle, mType,
871                 mStartTimeMs, mEndTimeMs, mSeasonNumber, mEpisodeNumber, mEpisodeTitle,
872                 mProgramDescription, mProgramLongDescription, mProgramPosterArtUri,
873                 mProgramThumbnailUri, mState, mSeriesRecordingId);
874     }
875 
876     /**
877      * Returns an array containing all of the elements in the list.
878      */
toArray(Collection<ScheduledRecording> schedules)879     public static ScheduledRecording[] toArray(Collection<ScheduledRecording> schedules) {
880         return schedules.toArray(new ScheduledRecording[schedules.size()]);
881     }
882 }
883