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