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.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      */
    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      */
    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      */
    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      */
    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 
    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 
    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 
    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 
    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          */
    290         public Builder setId(long id) {
    291             mId = id;
    292             return this;
    293         }
    294 
    295         /**
    296          * @see #getPriority() ()
    297          */
    298         public Builder setPriority(long priority) {
    299             mPriority = priority;
    300             return this;
    301         }
    302 
    303         /**
    304          * @see #getTitle()
    305          */
    306         public Builder setTitle(String title) {
    307             mTitle = title;
    308             return this;
    309         }
    310 
    311         /**
    312          * @see #getDescription()
    313          */
    314         public Builder setDescription(String description) {
    315             mDescription = description;
    316             return this;
    317         }
    318 
    319         /**
    320          * @see #getLongDescription()
    321          */
    322         public Builder setLongDescription(String longDescription) {
    323             mLongDescription = longDescription;
    324             return this;
    325         }
    326 
    327         /**
    328          * @see #getInputId()
    329          */
    330         public Builder setInputId(String inputId) {
    331             mInputId = inputId;
    332             return this;
    333         }
    334 
    335         /**
    336          * @see #getChannelId()
    337          */
    338         public Builder setChannelId(long channelId) {
    339             mChannelId = channelId;
    340             return this;
    341         }
    342 
    343         /**
    344          * @see #getSeriesId()
    345          */
    346         public Builder setSeriesId(String seriesId) {
    347             mSeriesId = seriesId;
    348             return this;
    349         }
    350 
    351         /**
    352          * @see #getStartFromSeason()
    353          */
    354         public Builder setStartFromSeason(int startFromSeason) {
    355             mStartFromSeason = startFromSeason;
    356             return this;
    357         }
    358 
    359         /**
    360          * @see #getChannelOption()
    361          */
    362         public Builder setChannelOption(@ChannelOption int option) {
    363             mChannelOption = option;
    364             return this;
    365         }
    366 
    367         /**
    368          * @see #getStartFromEpisode()
    369          */
    370         public Builder setStartFromEpisode(int startFromEpisode) {
    371             mStartFromEpisode = startFromEpisode;
    372             return this;
    373         }
    374 
    375         /**
    376          * @see #getCanonicalGenreIds()
    377          */
    378         public Builder setCanonicalGenreIds(String genres) {
    379             mCanonicalGenreIds = Utils.getCanonicalGenreIds(genres);
    380             return this;
    381         }
    382 
    383         /**
    384          * @see #getCanonicalGenreIds()
    385          */
    386         public Builder setCanonicalGenreIds(int[] canonicalGenreIds) {
    387             mCanonicalGenreIds = canonicalGenreIds;
    388             return this;
    389         }
    390 
    391         /**
    392          * @see #getPosterUri()
    393          */
    394         public Builder setPosterUri(String posterUri) {
    395             mPosterUri = posterUri;
    396             return this;
    397         }
    398 
    399         /**
    400          * @see #getPhotoUri()
    401          */
    402         public Builder setPhotoUri(String photoUri) {
    403             mPhotoUri = photoUri;
    404             return this;
    405         }
    406 
    407         /**
    408          * @see #getState()
    409          */
    410         public Builder setState(@SeriesState int state) {
    411             mState = state;
    412             return this;
    413         }
    414 
    415         /**
    416          * Creates a new {@link SeriesRecording}.
    417          */
    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 
    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      */
    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      */
    484     public long getChannelId() {
    485         return mChannelId;
    486     }
    487 
    488     /**
    489      * The id of this SeriesRecording.
    490      */
    491     public long getId() {
    492         return mId;
    493     }
    494 
    495     /**
    496      * Sets the ID.
    497      */
    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      */
    508     public long getPriority() {
    509         return mPriority;
    510     }
    511 
    512     /**
    513      * The series title.
    514      */
    515     public String getTitle() {
    516         return mTitle;
    517     }
    518 
    519     /**
    520      * The series description.
    521      */
    522     public String getDescription() {
    523         return mDescription;
    524     }
    525 
    526     /**
    527      * The long series description.
    528      */
    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      */
    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      */
    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      */
    554     public int getStartFromSeason() {
    555         return mStartFromSeason;
    556     }
    557 
    558     /**
    559      * Returns the channel recording option.
    560      */
    561     @ChannelOption public int getChannelOption() {
    562         return mChannelOption;
    563     }
    564 
    565     /**
    566      * Returns the canonical genre ID's.
    567      */
    568     public int[] getCanonicalGenreIds() {
    569         return mCanonicalGenreIds;
    570     }
    571 
    572     /**
    573      * Returns the poster URI.
    574      */
    575     public String getPosterUri() {
    576         return mPosterUri;
    577     }
    578 
    579     /**
    580      * Returns the photo URI.
    581      */
    582     public String getPhotoUri() {
    583         return mPhotoUri;
    584     }
    585 
    586     /**
    587      * Returns the state of series recording.
    588      */
    589     @SeriesState public int getState() {
    590         return mState;
    591     }
    592 
    593     /**
    594      * Checks whether the series recording is stopped or not.
    595      */
    596     public boolean isStopped() {
    597         return mState == STATE_SERIES_STOPPED;
    598     }
    599 
    600     @Override
    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
    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
    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 
    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
    670     public int describeContents() {
    671         return 0;
    672     }
    673 
    674     @Override
    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      */
    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      */
    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      */
    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