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