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