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