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