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