• 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.database.Cursor;
21 import android.os.Parcel;
22 import android.os.Parcelable;
23 import android.support.annotation.IntDef;
24 import android.support.annotation.VisibleForTesting;
25 import android.text.TextUtils;
26 
27 import com.android.tv.data.BaseProgram;
28 import com.android.tv.data.Program;
29 import com.android.tv.dvr.DvrScheduleManager;
30 import com.android.tv.dvr.provider.DvrContract.SeriesRecordings;
31 import com.android.tv.util.Utils;
32 
33 import java.lang.annotation.Retention;
34 import java.lang.annotation.RetentionPolicy;
35 import java.util.Arrays;
36 import java.util.Collection;
37 import java.util.Comparator;
38 import java.util.Objects;
39 
40 /**
41  * Schedules the recording of a Series of Programs.
42  *
43  * <p>
44  * Contains the data needed to create new ScheduleRecordings as the programs become available in
45  * the EPG.
46  */
47 public class SeriesRecording implements Parcelable {
48     /**
49      * Indicates that the ID is not assigned yet.
50      */
51     public static final long ID_NOT_SET = 0;
52 
53     /**
54      * The default priority of this recording.
55      */
56     public static final long DEFAULT_PRIORITY = Long.MAX_VALUE >> 1;
57 
58     @Retention(RetentionPolicy.SOURCE)
59     @IntDef(flag = true,
60             value = {OPTION_CHANNEL_ONE, OPTION_CHANNEL_ALL})
61     public @interface ChannelOption {}
62     /**
63      * An option which indicates that the episodes in one channel are recorded.
64      */
65     public static final int OPTION_CHANNEL_ONE = 0;
66     /**
67      * An option which indicates that the episodes in all the channels are recorded.
68      */
69     public static final int OPTION_CHANNEL_ALL = 1;
70 
71     @Retention(RetentionPolicy.SOURCE)
72     @IntDef(flag = true,
73             value = {STATE_SERIES_NORMAL, STATE_SERIES_STOPPED})
74     public @interface SeriesState {}
75 
76     /**
77      * The state indicates that the series recording is a normal one.
78      */
79     public static final int STATE_SERIES_NORMAL = 0;
80 
81     /**
82      * The state indicates that the series recording is stopped.
83      */
84     public static final int STATE_SERIES_STOPPED = 1;
85 
86     /**
87      * Compare priority in descending order.
88      */
89     public static final Comparator<SeriesRecording> PRIORITY_COMPARATOR =
90             new Comparator<SeriesRecording>() {
91         @Override
92         public int compare(SeriesRecording lhs, SeriesRecording rhs) {
93             int value = Long.compare(rhs.mPriority, lhs.mPriority);
94             if (value == 0) {
95                 // New recording has the higher priority.
96                 value = Long.compare(rhs.mId, lhs.mId);
97             }
98             return value;
99         }
100     };
101 
102     /**
103      * Compare ID in ascending order.
104      */
105     public static final Comparator<SeriesRecording> ID_COMPARATOR =
106             new Comparator<SeriesRecording>() {
107                 @Override
108                 public int compare(SeriesRecording lhs, SeriesRecording rhs) {
109                     return Long.compare(lhs.mId, rhs.mId);
110                 }
111             };
112 
113     /**
114      * Creates a new Builder with the values set from the series information of {@link BaseProgram}.
115      */
builder(String inputId, BaseProgram p)116     public static Builder builder(String inputId, BaseProgram p) {
117         return new Builder()
118                 .setInputId(inputId)
119                 .setSeriesId(p.getSeriesId())
120                 .setChannelId(p.getChannelId())
121                 .setTitle(p.getTitle())
122                 .setDescription(p.getDescription())
123                 .setLongDescription(p.getLongDescription())
124                 .setCanonicalGenreIds(p.getCanonicalGenreIds())
125                 .setPosterUri(p.getPosterArtUri())
126                 .setPhotoUri(p.getThumbnailUri());
127     }
128 
129     /**
130      * Creates a new Builder with the values set from an existing {@link SeriesRecording}.
131      */
buildFrom(SeriesRecording r)132     public static Builder buildFrom(SeriesRecording r) {
133         return new Builder()
134                 .setId(r.mId)
135                 .setInputId(r.getInputId())
136                 .setChannelId(r.getChannelId())
137                 .setPriority(r.getPriority())
138                 .setTitle(r.getTitle())
139                 .setDescription(r.getDescription())
140                 .setLongDescription(r.getLongDescription())
141                 .setSeriesId(r.getSeriesId())
142                 .setStartFromEpisode(r.getStartFromEpisode())
143                 .setStartFromSeason(r.getStartFromSeason())
144                 .setChannelOption(r.getChannelOption())
145                 .setCanonicalGenreIds(r.getCanonicalGenreIds())
146                 .setPosterUri(r.getPosterUri())
147                 .setPhotoUri(r.getPhotoUri())
148                 .setState(r.getState());
149     }
150 
151     /**
152      * Use this projection if you want to create {@link SeriesRecording} object using
153      * {@link #fromCursor}.
154      */
155     public static final String[] PROJECTION = {
156             // Columns must match what is read in fromCursor()
157             SeriesRecordings._ID,
158             SeriesRecordings.COLUMN_INPUT_ID,
159             SeriesRecordings.COLUMN_CHANNEL_ID,
160             SeriesRecordings.COLUMN_PRIORITY,
161             SeriesRecordings.COLUMN_TITLE,
162             SeriesRecordings.COLUMN_SHORT_DESCRIPTION,
163             SeriesRecordings.COLUMN_LONG_DESCRIPTION,
164             SeriesRecordings.COLUMN_SERIES_ID,
165             SeriesRecordings.COLUMN_START_FROM_EPISODE,
166             SeriesRecordings.COLUMN_START_FROM_SEASON,
167             SeriesRecordings.COLUMN_CHANNEL_OPTION,
168             SeriesRecordings.COLUMN_CANONICAL_GENRE,
169             SeriesRecordings.COLUMN_POSTER_URI,
170             SeriesRecordings.COLUMN_PHOTO_URI,
171             SeriesRecordings.COLUMN_STATE
172     };
173     /**
174      * Creates {@link SeriesRecording} object from the given {@link Cursor}.
175      */
fromCursor(Cursor c)176     public static SeriesRecording fromCursor(Cursor c) {
177         int index = -1;
178         return new Builder()
179                 .setId(c.getLong(++index))
180                 .setInputId(c.getString(++index))
181                 .setChannelId(c.getLong(++index))
182                 .setPriority(c.getLong(++index))
183                 .setTitle(c.getString(++index))
184                 .setDescription(c.getString(++index))
185                 .setLongDescription(c.getString(++index))
186                 .setSeriesId(c.getString(++index))
187                 .setStartFromEpisode(c.getInt(++index))
188                 .setStartFromSeason(c.getInt(++index))
189                 .setChannelOption(channelOption(c.getString(++index)))
190                 .setCanonicalGenreIds(c.getString(++index))
191                 .setPosterUri(c.getString(++index))
192                 .setPhotoUri(c.getString(++index))
193                 .setState(seriesRecordingState(c.getString(++index)))
194                 .build();
195     }
196 
197     /**
198      * Returns the ContentValues with keys as the columns specified in {@link SeriesRecordings}
199      * and the values from {@code r}.
200      */
toContentValues(SeriesRecording r)201     public static ContentValues toContentValues(SeriesRecording r) {
202         ContentValues values = new ContentValues();
203         if (r.getId() != ID_NOT_SET) {
204             values.put(SeriesRecordings._ID, r.getId());
205         } else {
206             values.putNull(SeriesRecordings._ID);
207         }
208         values.put(SeriesRecordings.COLUMN_INPUT_ID, r.getInputId());
209         values.put(SeriesRecordings.COLUMN_CHANNEL_ID, r.getChannelId());
210         values.put(SeriesRecordings.COLUMN_PRIORITY, r.getPriority());
211         values.put(SeriesRecordings.COLUMN_TITLE, r.getTitle());
212         values.put(SeriesRecordings.COLUMN_SHORT_DESCRIPTION, r.getDescription());
213         values.put(SeriesRecordings.COLUMN_LONG_DESCRIPTION, r.getLongDescription());
214         values.put(SeriesRecordings.COLUMN_SERIES_ID, r.getSeriesId());
215         values.put(SeriesRecordings.COLUMN_START_FROM_EPISODE, r.getStartFromEpisode());
216         values.put(SeriesRecordings.COLUMN_START_FROM_SEASON, r.getStartFromSeason());
217         values.put(SeriesRecordings.COLUMN_CHANNEL_OPTION,
218                 channelOption(r.getChannelOption()));
219         values.put(SeriesRecordings.COLUMN_CANONICAL_GENRE,
220                 Utils.getCanonicalGenre(r.getCanonicalGenreIds()));
221         values.put(SeriesRecordings.COLUMN_POSTER_URI, r.getPosterUri());
222         values.put(SeriesRecordings.COLUMN_PHOTO_URI, r.getPhotoUri());
223         values.put(SeriesRecordings.COLUMN_STATE, seriesRecordingState(r.getState()));
224         return values;
225     }
226 
channelOption(@hannelOption int option)227     private static String channelOption(@ChannelOption int option) {
228         switch (option) {
229             case OPTION_CHANNEL_ONE:
230                 return SeriesRecordings.OPTION_CHANNEL_ONE;
231             case OPTION_CHANNEL_ALL:
232                 return SeriesRecordings.OPTION_CHANNEL_ALL;
233         }
234         return SeriesRecordings.OPTION_CHANNEL_ONE;
235     }
236 
channelOption(String option)237     @ChannelOption private static int channelOption(String option) {
238         switch (option) {
239             case SeriesRecordings.OPTION_CHANNEL_ONE:
240                 return OPTION_CHANNEL_ONE;
241             case SeriesRecordings.OPTION_CHANNEL_ALL:
242                 return OPTION_CHANNEL_ALL;
243         }
244         return OPTION_CHANNEL_ONE;
245     }
246 
seriesRecordingState(@eriesState int state)247     private static String seriesRecordingState(@SeriesState int state) {
248         switch (state) {
249             case STATE_SERIES_NORMAL:
250                 return SeriesRecordings.STATE_SERIES_NORMAL;
251             case STATE_SERIES_STOPPED:
252                 return SeriesRecordings.STATE_SERIES_STOPPED;
253         }
254         return SeriesRecordings.STATE_SERIES_NORMAL;
255     }
256 
seriesRecordingState(String state)257     @SeriesState private static int seriesRecordingState(String state) {
258         switch (state) {
259             case SeriesRecordings.STATE_SERIES_NORMAL:
260                 return STATE_SERIES_NORMAL;
261             case SeriesRecordings.STATE_SERIES_STOPPED:
262                 return STATE_SERIES_STOPPED;
263         }
264         return STATE_SERIES_NORMAL;
265     }
266 
267     /**
268      * Builder for {@link SeriesRecording}.
269      */
270     public static class Builder {
271         private long mId = ID_NOT_SET;
272         private long mPriority = DvrScheduleManager.DEFAULT_SERIES_PRIORITY;
273         private String mTitle;
274         private String mDescription;
275         private String mLongDescription;
276         private String mInputId;
277         private long mChannelId;
278         private String mSeriesId;
279         private int mStartFromSeason = SeriesRecordings.THE_BEGINNING;
280         private int mStartFromEpisode = SeriesRecordings.THE_BEGINNING;
281         private int mChannelOption = OPTION_CHANNEL_ONE;
282         private int[] mCanonicalGenreIds;
283         private String mPosterUri;
284         private String mPhotoUri;
285         private int mState = SeriesRecording.STATE_SERIES_NORMAL;
286 
287         /**
288          * @see #getId()
289          */
setId(long id)290         public Builder setId(long id) {
291             mId = id;
292             return this;
293         }
294 
295         /**
296          * @see #getPriority() ()
297          */
setPriority(long priority)298         public Builder setPriority(long priority) {
299             mPriority = priority;
300             return this;
301         }
302 
303         /**
304          * @see #getTitle()
305          */
setTitle(String title)306         public Builder setTitle(String title) {
307             mTitle = title;
308             return this;
309         }
310 
311         /**
312          * @see #getDescription()
313          */
setDescription(String description)314         public Builder setDescription(String description) {
315             mDescription = description;
316             return this;
317         }
318 
319         /**
320          * @see #getLongDescription()
321          */
setLongDescription(String longDescription)322         public Builder setLongDescription(String longDescription) {
323             mLongDescription = longDescription;
324             return this;
325         }
326 
327         /**
328          * @see #getInputId()
329          */
setInputId(String inputId)330         public Builder setInputId(String inputId) {
331             mInputId = inputId;
332             return this;
333         }
334 
335         /**
336          * @see #getChannelId()
337          */
setChannelId(long channelId)338         public Builder setChannelId(long channelId) {
339             mChannelId = channelId;
340             return this;
341         }
342 
343         /**
344          * @see #getSeriesId()
345          */
setSeriesId(String seriesId)346         public Builder setSeriesId(String seriesId) {
347             mSeriesId = seriesId;
348             return this;
349         }
350 
351         /**
352          * @see #getStartFromSeason()
353          */
setStartFromSeason(int startFromSeason)354         public Builder setStartFromSeason(int startFromSeason) {
355             mStartFromSeason = startFromSeason;
356             return this;
357         }
358 
359         /**
360          * @see #getChannelOption()
361          */
setChannelOption(@hannelOption int option)362         public Builder setChannelOption(@ChannelOption int option) {
363             mChannelOption = option;
364             return this;
365         }
366 
367         /**
368          * @see #getStartFromEpisode()
369          */
setStartFromEpisode(int startFromEpisode)370         public Builder setStartFromEpisode(int startFromEpisode) {
371             mStartFromEpisode = startFromEpisode;
372             return this;
373         }
374 
375         /**
376          * @see #getCanonicalGenreIds()
377          */
setCanonicalGenreIds(String genres)378         public Builder setCanonicalGenreIds(String genres) {
379             mCanonicalGenreIds = Utils.getCanonicalGenreIds(genres);
380             return this;
381         }
382 
383         /**
384          * @see #getCanonicalGenreIds()
385          */
setCanonicalGenreIds(int[] canonicalGenreIds)386         public Builder setCanonicalGenreIds(int[] canonicalGenreIds) {
387             mCanonicalGenreIds = canonicalGenreIds;
388             return this;
389         }
390 
391         /**
392          * @see #getPosterUri()
393          */
setPosterUri(String posterUri)394         public Builder setPosterUri(String posterUri) {
395             mPosterUri = posterUri;
396             return this;
397         }
398 
399         /**
400          * @see #getPhotoUri()
401          */
setPhotoUri(String photoUri)402         public Builder setPhotoUri(String photoUri) {
403             mPhotoUri = photoUri;
404             return this;
405         }
406 
407         /**
408          * @see #getState()
409          */
setState(@eriesState int state)410         public Builder setState(@SeriesState int state) {
411             mState = state;
412             return this;
413         }
414 
415         /**
416          * Creates a new {@link SeriesRecording}.
417          */
build()418         public SeriesRecording build() {
419             return new SeriesRecording(mId, mPriority, mTitle, mDescription, mLongDescription,
420                     mInputId, mChannelId, mSeriesId, mStartFromSeason, mStartFromEpisode,
421                     mChannelOption, mCanonicalGenreIds, mPosterUri, mPhotoUri, mState);
422         }
423     }
424 
fromParcel(Parcel in)425     public static SeriesRecording fromParcel(Parcel in) {
426         return new Builder()
427                 .setId(in.readLong())
428                 .setPriority(in.readLong())
429                 .setTitle(in.readString())
430                 .setDescription(in.readString())
431                 .setLongDescription(in.readString())
432                 .setInputId(in.readString())
433                 .setChannelId(in.readLong())
434                 .setSeriesId(in.readString())
435                 .setStartFromSeason(in.readInt())
436                 .setStartFromEpisode(in.readInt())
437                 .setChannelOption(in.readInt())
438                 .setCanonicalGenreIds(in.createIntArray())
439                 .setPosterUri(in.readString())
440                 .setPhotoUri(in.readString())
441                 .setState(in.readInt())
442                 .build();
443     }
444 
445     public static final Parcelable.Creator<SeriesRecording> CREATOR =
446             new Parcelable.Creator<SeriesRecording>() {
447         @Override
448         public SeriesRecording createFromParcel(Parcel in) {
449           return SeriesRecording.fromParcel(in);
450         }
451 
452         @Override
453         public SeriesRecording[] newArray(int size) {
454           return new SeriesRecording[size];
455         }
456     };
457 
458     private long mId;
459     private final long mPriority;
460     private final String mTitle;
461     private final String mDescription;
462     private final String mLongDescription;
463     private final String mInputId;
464     private final long mChannelId;
465     private final String mSeriesId;
466     private final int mStartFromSeason;
467     private final int mStartFromEpisode;
468     @ChannelOption private final int mChannelOption;
469     private final int[] mCanonicalGenreIds;
470     private final String mPosterUri;
471     private final String mPhotoUri;
472     @SeriesState private int mState;
473 
474     /**
475      * The input id of this SeriesRecording.
476      */
getInputId()477     public String getInputId() {
478         return mInputId;
479     }
480 
481     /**
482      * The channelId to match. The channel ID might not be valid when the channel option is "ALL".
483      */
getChannelId()484     public long getChannelId() {
485         return mChannelId;
486     }
487 
488     /**
489      * The id of this SeriesRecording.
490      */
getId()491     public long getId() {
492         return mId;
493     }
494 
495     /**
496      * Sets the ID.
497      */
setId(long id)498     public void setId(long id) {
499         mId = id;
500     }
501 
502     /**
503      * The priority of this recording.
504      *
505      * <p> The highest number is recorded first. If there is a tie in mPriority then the higher mId
506      * wins.
507      */
getPriority()508     public long getPriority() {
509         return mPriority;
510     }
511 
512     /**
513      * The series title.
514      */
getTitle()515     public String getTitle() {
516         return mTitle;
517     }
518 
519     /**
520      * The series description.
521      */
getDescription()522     public String getDescription() {
523         return mDescription;
524     }
525 
526     /**
527      * The long series description.
528      */
getLongDescription()529     public String getLongDescription() {
530         return mLongDescription;
531     }
532 
533     /**
534      * SeriesId when not null is used to match programs instead of using title and channelId.
535      *
536      * <p>SeriesId is an opaque but stable string.
537      */
getSeriesId()538     public String getSeriesId() {
539         return mSeriesId;
540     }
541 
542     /**
543      * If not == {@link SeriesRecordings#THE_BEGINNING} and seasonNumber == startFromSeason then
544      * only record episodes with a episodeNumber >= this
545      */
getStartFromEpisode()546     public int getStartFromEpisode() {
547         return mStartFromEpisode;
548     }
549 
550     /**
551      * If not == {@link SeriesRecordings#THE_BEGINNING} then only record episodes with a
552      * seasonNumber >= this
553      */
getStartFromSeason()554     public int getStartFromSeason() {
555         return mStartFromSeason;
556     }
557 
558     /**
559      * Returns the channel recording option.
560      */
getChannelOption()561     @ChannelOption public int getChannelOption() {
562         return mChannelOption;
563     }
564 
565     /**
566      * Returns the canonical genre ID's.
567      */
getCanonicalGenreIds()568     public int[] getCanonicalGenreIds() {
569         return mCanonicalGenreIds;
570     }
571 
572     /**
573      * Returns the poster URI.
574      */
getPosterUri()575     public String getPosterUri() {
576         return mPosterUri;
577     }
578 
579     /**
580      * Returns the photo URI.
581      */
getPhotoUri()582     public String getPhotoUri() {
583         return mPhotoUri;
584     }
585 
586     /**
587      * Returns the state of series recording.
588      */
getState()589     @SeriesState public int getState() {
590         return mState;
591     }
592 
593     /**
594      * Checks whether the series recording is stopped or not.
595      */
isStopped()596     public boolean isStopped() {
597         return mState == STATE_SERIES_STOPPED;
598     }
599 
600     @Override
equals(Object o)601     public boolean equals(Object o) {
602         if (this == o) return true;
603         if (!(o instanceof SeriesRecording)) return false;
604         SeriesRecording that = (SeriesRecording) o;
605         return mPriority == that.mPriority
606                 && mChannelId == that.mChannelId
607                 && mStartFromSeason == that.mStartFromSeason
608                 && mStartFromEpisode == that.mStartFromEpisode
609                 && Objects.equals(mId, that.mId)
610                 && Objects.equals(mTitle, that.mTitle)
611                 && Objects.equals(mDescription, that.mDescription)
612                 && Objects.equals(mLongDescription, that.mLongDescription)
613                 && Objects.equals(mSeriesId, that.mSeriesId)
614                 && mChannelOption == that.mChannelOption
615                 && Arrays.equals(mCanonicalGenreIds, that.mCanonicalGenreIds)
616                 && Objects.equals(mPosterUri, that.mPosterUri)
617                 && Objects.equals(mPhotoUri, that.mPhotoUri)
618                 && mState == that.mState;
619     }
620 
621     @Override
hashCode()622     public int hashCode() {
623         return Objects.hash(mPriority, mChannelId, mStartFromSeason, mStartFromEpisode, mId,
624                 mTitle, mDescription, mLongDescription, mSeriesId, mChannelOption,
625                 mCanonicalGenreIds, mPosterUri, mPhotoUri, mState);
626     }
627 
628     @Override
toString()629     public String toString() {
630         return "SeriesRecording{" +
631                 "inputId=" + mInputId +
632                 ", channelId=" + mChannelId +
633                 ", id='" + mId + '\'' +
634                 ", priority=" + mPriority +
635                 ", title='" + mTitle + '\'' +
636                 ", description='" + mDescription + '\'' +
637                 ", longDescription='" + mLongDescription + '\'' +
638                 ", startFromSeason=" + mStartFromSeason +
639                 ", startFromEpisode=" + mStartFromEpisode +
640                 ", channelOption=" + mChannelOption +
641                 ", canonicalGenreIds=" + Arrays.toString(mCanonicalGenreIds) +
642                 ", posterUri=" + mPosterUri +
643                 ", photoUri=" + mPhotoUri +
644                 ", state=" + mState +
645                 '}';
646     }
647 
SeriesRecording(long id, long priority, String title, String description, String longDescription, String inputId, long channelId, String seriesId, int startFromSeason, int startFromEpisode, int channelOption, int[] canonicalGenreIds, String posterUri, String photoUri, int state)648     private SeriesRecording(long id, long priority, String title, String description,
649             String longDescription, String inputId, long channelId, String seriesId,
650             int startFromSeason, int startFromEpisode, int channelOption, int[] canonicalGenreIds,
651             String posterUri, String photoUri, int state) {
652         this.mId = id;
653         this.mPriority = priority;
654         this.mTitle = title;
655         this.mDescription = description;
656         this.mLongDescription = longDescription;
657         this.mInputId = inputId;
658         this.mChannelId = channelId;
659         this.mSeriesId = seriesId;
660         this.mStartFromSeason = startFromSeason;
661         this.mStartFromEpisode = startFromEpisode;
662         this.mChannelOption = channelOption;
663         this.mCanonicalGenreIds = canonicalGenreIds;
664         this.mPosterUri = posterUri;
665         this.mPhotoUri = photoUri;
666         this.mState = state;
667     }
668 
669     @Override
describeContents()670     public int describeContents() {
671         return 0;
672     }
673 
674     @Override
writeToParcel(Parcel out, int paramInt)675     public void writeToParcel(Parcel out, int paramInt) {
676         out.writeLong(mId);
677         out.writeLong(mPriority);
678         out.writeString(mTitle);
679         out.writeString(mDescription);
680         out.writeString(mLongDescription);
681         out.writeString(mInputId);
682         out.writeLong(mChannelId);
683         out.writeString(mSeriesId);
684         out.writeInt(mStartFromSeason);
685         out.writeInt(mStartFromEpisode);
686         out.writeInt(mChannelOption);
687         out.writeIntArray(mCanonicalGenreIds);
688         out.writeString(mPosterUri);
689         out.writeString(mPhotoUri);
690         out.writeInt(mState);
691     }
692 
693     /**
694      * Returns an array containing all of the elements in the list.
695      */
toArray(Collection<SeriesRecording> series)696     public static SeriesRecording[] toArray(Collection<SeriesRecording> series) {
697         return series.toArray(new SeriesRecording[series.size()]);
698     }
699 
700     /**
701      * Returns {@code true} if the {@code program} is part of the series and meets the season and
702      * episode constraints.
703      */
matchProgram(Program program)704     public boolean matchProgram(Program program) {
705         return matchProgram(program, mChannelOption);
706     }
707 
708     /**
709      * Returns {@code true} if the {@code program} is part of the series and meets the season and
710      * episode constraints. It checks the channel option only if {@code checkChannelOption} is
711      * {@code true}.
712      */
matchProgram(Program program, @ChannelOption int channelOption)713     public boolean matchProgram(Program program, @ChannelOption int channelOption) {
714         String seriesId = program.getSeriesId();
715         long channelId = program.getChannelId();
716         String seasonNumber = program.getSeasonNumber();
717         String episodeNumber = program.getEpisodeNumber();
718         if (!mSeriesId.equals(seriesId) || (channelOption == SeriesRecording.OPTION_CHANNEL_ONE
719                 && mChannelId != channelId)) {
720             return false;
721         }
722         // Season number and episode number matches if
723         // start_season_number < program_season_number
724         // || (start_season_number == program_season_number
725         // && start_episode_number <= program_episode_number).
726         if (mStartFromSeason == SeriesRecordings.THE_BEGINNING
727                 || TextUtils.isEmpty(seasonNumber)) {
728             return true;
729         } else {
730             int intSeasonNumber;
731             try {
732                 intSeasonNumber = Integer.valueOf(seasonNumber);
733             } catch (NumberFormatException e) {
734                 return true;
735             }
736             if (intSeasonNumber > mStartFromSeason) {
737                 return true;
738             } else if (intSeasonNumber < mStartFromSeason) {
739                 return false;
740             }
741         }
742         if (mStartFromEpisode == SeriesRecordings.THE_BEGINNING
743                 || TextUtils.isEmpty(episodeNumber)) {
744             return true;
745         } else {
746             int intEpisodeNumber;
747             try {
748                 intEpisodeNumber = Integer.valueOf(episodeNumber);
749             } catch (NumberFormatException e) {
750                 return true;
751             }
752             return intEpisodeNumber >= mStartFromEpisode;
753         }
754     }
755 }
756