1/*
2 * Copyright (C) 2016 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
17package com.android.tv.common.recording;
18
19import static android.media.tv.TvContract.RecordedPrograms;
20
21import android.content.ContentUris;
22import android.content.ContentValues;
23import android.content.Context;
24import android.database.Cursor;
25import android.media.tv.TvContract;
26import android.net.Uri;
27import android.support.annotation.Nullable;
28import android.text.TextUtils;
29
30import com.android.tv.common.R;
31
32import java.util.Arrays;
33import java.util.Comparator;
34import java.util.Objects;
35
36/**
37 * Immutable instance of {@link android.media.tv.TvContract.RecordedPrograms}.
38 */
39public class RecordedProgram {
40    public static final int ID_NOT_SET = -1;
41
42    public final static String[] PROJECTION = {
43            // These are in exactly the order listed in RecordedPrograms
44            RecordedPrograms._ID,
45            RecordedPrograms.COLUMN_INPUT_ID,
46            RecordedPrograms.COLUMN_CHANNEL_ID,
47            RecordedPrograms.COLUMN_TITLE,
48            RecordedPrograms.COLUMN_SEASON_DISPLAY_NUMBER,
49            RecordedPrograms.COLUMN_SEASON_TITLE,
50            RecordedPrograms.COLUMN_EPISODE_DISPLAY_NUMBER,
51            RecordedPrograms.COLUMN_EPISODE_TITLE,
52            RecordedPrograms.COLUMN_START_TIME_UTC_MILLIS,
53            RecordedPrograms.COLUMN_END_TIME_UTC_MILLIS,
54            RecordedPrograms.COLUMN_BROADCAST_GENRE,
55            RecordedPrograms.COLUMN_CANONICAL_GENRE,
56            RecordedPrograms.COLUMN_SHORT_DESCRIPTION,
57            RecordedPrograms.COLUMN_LONG_DESCRIPTION,
58            RecordedPrograms.COLUMN_VIDEO_WIDTH,
59            RecordedPrograms.COLUMN_VIDEO_HEIGHT,
60            RecordedPrograms.COLUMN_AUDIO_LANGUAGE,
61            RecordedPrograms.COLUMN_CONTENT_RATING,
62            RecordedPrograms.COLUMN_POSTER_ART_URI,
63            RecordedPrograms.COLUMN_THUMBNAIL_URI,
64            RecordedPrograms.COLUMN_SEARCHABLE,
65            RecordedPrograms.COLUMN_RECORDING_DATA_URI,
66            RecordedPrograms.COLUMN_RECORDING_DATA_BYTES,
67            RecordedPrograms.COLUMN_RECORDING_DURATION_MILLIS,
68            RecordedPrograms.COLUMN_RECORDING_EXPIRE_TIME_UTC_MILLIS,
69            RecordedPrograms.COLUMN_INTERNAL_PROVIDER_DATA,
70            RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG1,
71            RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG2,
72            RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG3,
73            RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG4,
74            RecordedPrograms.COLUMN_VERSION_NUMBER,
75    };
76
77    public static final RecordedProgram fromCursor(Cursor cursor) {
78        int index = 0;
79        return builder()
80                .setId(cursor.getLong(index++))
81                .setInputId(cursor.getString(index++))
82                .setChannelId(cursor.getLong(index++))
83                .setTitle(cursor.getString(index++))
84                .setSeasonNumber(cursor.getString(index++))
85                .setSeasonTitle(cursor.getString(index++))
86                .setEpisodeNumber(cursor.getString(index++))
87                .setEpisodeTitle(cursor.getString(index++))
88                .setStartTimeUtcMillis(cursor.getLong(index++))
89                .setEndTimeUtcMillis(cursor.getLong(index++))
90                .setBroadcastGenres(cursor.getString(index++))
91                .setCanonicalGenres(cursor.getString(index++))
92                .setShortDescription(cursor.getString(index++))
93                .setLongDescription(cursor.getString(index++))
94                .setVideoWidth(cursor.getInt(index++))
95                .setVideoHeight(cursor.getInt(index++))
96                .setAudioLanguage(cursor.getString(index++))
97                .setContentRating(cursor.getString(index++))
98                .setPosterArt(cursor.getString(index++))
99                .setThumbnail(cursor.getString(index++))
100                .setSearchable(cursor.getInt(index++) == 1)
101                .setDataUri(cursor.getString(index++))
102                .setDataBytes(cursor.getLong(index++))
103                .setDurationMillis(cursor.getLong(index++))
104                .setExpireTimeUtcMillis(cursor.getLong(index++))
105                .setInternalProviderData(cursor.getBlob(index++))
106                .setInternalProviderFlag1(cursor.getInt(index++))
107                .setInternalProviderFlag2(cursor.getInt(index++))
108                .setInternalProviderFlag3(cursor.getInt(index++))
109                .setInternalProviderFlag4(cursor.getInt(index++))
110                .setVersionNumber(cursor.getInt(index++))
111                .build();
112    }
113
114    public static ContentValues toValues(RecordedProgram recordedProgram) {
115        ContentValues values = new ContentValues();
116        if (recordedProgram.mId != ID_NOT_SET) {
117            values.put(RecordedPrograms._ID, recordedProgram.mId);
118        }
119        values.put(RecordedPrograms.COLUMN_INPUT_ID, recordedProgram.mInputId);
120        values.put(RecordedPrograms.COLUMN_CHANNEL_ID, recordedProgram.mChannelId);
121        values.put(RecordedPrograms.COLUMN_TITLE, recordedProgram.mTitle);
122        values.put(RecordedPrograms.COLUMN_SEASON_DISPLAY_NUMBER, recordedProgram.mSeasonNumber);
123        values.put(RecordedPrograms.COLUMN_SEASON_TITLE, recordedProgram.mSeasonTitle);
124        values.put(RecordedPrograms.COLUMN_EPISODE_DISPLAY_NUMBER, recordedProgram.mEpisodeNumber);
125        values.put(RecordedPrograms.COLUMN_EPISODE_TITLE, recordedProgram.mTitle);
126        values.put(RecordedPrograms.COLUMN_START_TIME_UTC_MILLIS,
127                recordedProgram.mStartTimeUtcMillis);
128        values.put(RecordedPrograms.COLUMN_END_TIME_UTC_MILLIS, recordedProgram.mEndTimeUtcMillis);
129        values.put(RecordedPrograms.COLUMN_BROADCAST_GENRE,
130                safeEncode(recordedProgram.mBroadcastGenres));
131        values.put(RecordedPrograms.COLUMN_CANONICAL_GENRE,
132                safeEncode(recordedProgram.mCanonicalGenres));
133        values.put(RecordedPrograms.COLUMN_SHORT_DESCRIPTION, recordedProgram.mShortDescription);
134        values.put(RecordedPrograms.COLUMN_LONG_DESCRIPTION, recordedProgram.mLongDescription);
135        if (recordedProgram.mVideoWidth == 0) {
136            values.putNull(RecordedPrograms.COLUMN_VIDEO_WIDTH);
137        } else {
138            values.put(RecordedPrograms.COLUMN_VIDEO_WIDTH, recordedProgram.mVideoWidth);
139        }
140        if (recordedProgram.mVideoHeight == 0) {
141            values.putNull(RecordedPrograms.COLUMN_VIDEO_HEIGHT);
142        } else {
143            values.put(RecordedPrograms.COLUMN_VIDEO_HEIGHT, recordedProgram.mVideoHeight);
144        }
145        values.put(RecordedPrograms.COLUMN_AUDIO_LANGUAGE, recordedProgram.mAudioLanguage);
146        values.put(RecordedPrograms.COLUMN_CONTENT_RATING, recordedProgram.mContentRating);
147        values.put(RecordedPrograms.COLUMN_POSTER_ART_URI,
148                safeToString(recordedProgram.mPosterArt));
149        values.put(RecordedPrograms.COLUMN_THUMBNAIL_URI, safeToString(recordedProgram.mThumbnail));
150        values.put(RecordedPrograms.COLUMN_SEARCHABLE, recordedProgram.mSearchable ? 1 : 0);
151        values.put(RecordedPrograms.COLUMN_RECORDING_DATA_URI,
152                safeToString(recordedProgram.mDataUri));
153        values.put(RecordedPrograms.COLUMN_RECORDING_DATA_BYTES, recordedProgram.mDataBytes);
154        values.put(RecordedPrograms.COLUMN_RECORDING_DURATION_MILLIS,
155                recordedProgram.mDurationMillis);
156        values.put(RecordedPrograms.COLUMN_RECORDING_EXPIRE_TIME_UTC_MILLIS,
157                recordedProgram.mExpireTimeUtcMillis);
158        values.put(RecordedPrograms.COLUMN_INTERNAL_PROVIDER_DATA,
159                recordedProgram.mInternalProviderData);
160        values.put(RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG1,
161                recordedProgram.mInternalProviderFlag1);
162        values.put(RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG2,
163                recordedProgram.mInternalProviderFlag2);
164        values.put(RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG3,
165                recordedProgram.mInternalProviderFlag3);
166        values.put(RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG4,
167                recordedProgram.mInternalProviderFlag4);
168        values.put(RecordedPrograms.COLUMN_VERSION_NUMBER, recordedProgram.mVersionNumber);
169        return values;
170    }
171
172    public static class Builder{
173        private long mId = ID_NOT_SET;
174        private String mInputId;
175        private long mChannelId;
176        private String mTitle;
177        private String mSeasonNumber;
178        private String mSeasonTitle;
179        private String mEpisodeNumber;
180        private String mEpisodeTitle;
181        private long mStartTimeUtcMillis;
182        private long mEndTimeUtcMillis;
183        private String[] mBroadcastGenres;
184        private String[] mCanonicalGenres;
185        private String mShortDescription;
186        private String mLongDescription;
187        private int mVideoWidth;
188        private int mVideoHeight;
189        private String mAudioLanguage;
190        private String mContentRating;
191        private Uri mPosterArt;
192        private Uri mThumbnail;
193        private boolean mSearchable = true;
194        private Uri mDataUri;
195        private long mDataBytes;
196        private long mDurationMillis;
197        private long mExpireTimeUtcMillis;
198        private byte[] mInternalProviderData;
199        private int mInternalProviderFlag1;
200        private int mInternalProviderFlag2;
201        private int mInternalProviderFlag3;
202        private int mInternalProviderFlag4;
203        private int mVersionNumber;
204
205        public Builder setId(long id) {
206            mId = id;
207            return this;
208        }
209
210        public Builder setInputId(String inputId) {
211            mInputId = inputId;
212            return this;
213        }
214
215        public Builder setChannelId(long channelId) {
216            mChannelId = channelId;
217            return this;
218        }
219
220        public Builder setTitle(String title) {
221            mTitle = title;
222            return this;
223        }
224
225        public Builder setSeasonNumber(String seasonNumber) {
226            mSeasonNumber = seasonNumber;
227            return this;
228        }
229
230        public Builder setSeasonTitle(String seasonTitle) {
231            mSeasonTitle = seasonTitle;
232            return this;
233        }
234
235        public Builder setEpisodeNumber(String episodeNumber) {
236            mEpisodeNumber = episodeNumber;
237            return this;
238        }
239
240        public Builder setEpisodeTitle(String episodeTitle) {
241            mEpisodeTitle = episodeTitle;
242            return this;
243        }
244
245        public Builder setStartTimeUtcMillis(long startTimeUtcMillis) {
246            mStartTimeUtcMillis = startTimeUtcMillis;
247            return this;
248        }
249
250        public Builder setEndTimeUtcMillis(long endTimeUtcMillis) {
251            mEndTimeUtcMillis = endTimeUtcMillis;
252            return this;
253        }
254
255        public Builder setBroadcastGenres(String broadcastGenres) {
256            if (TextUtils.isEmpty(broadcastGenres)) {
257                mBroadcastGenres = null;
258                return this;
259            }
260            return setBroadcastGenres(TvContract.Programs.Genres.decode(broadcastGenres));
261        }
262
263        private Builder setBroadcastGenres(String[] broadcastGenres) {
264            mBroadcastGenres = broadcastGenres;
265            return this;
266        }
267
268        public Builder setCanonicalGenres(String canonicalGenres) {
269            if (TextUtils.isEmpty(canonicalGenres)) {
270                mCanonicalGenres = null;
271                return this;
272            }
273            return setCanonicalGenres(TvContract.Programs.Genres.decode(canonicalGenres));
274        }
275
276        private Builder setCanonicalGenres(String[] canonicalGenres) {
277            mCanonicalGenres = canonicalGenres;
278            return this;
279        }
280
281        public Builder setShortDescription(String shortDescription) {
282            mShortDescription = shortDescription;
283            return this;
284        }
285
286        public Builder setLongDescription(String longDescription) {
287            mLongDescription = longDescription;
288            return this;
289        }
290
291        public Builder setVideoWidth(int videoWidth) {
292            mVideoWidth = videoWidth;
293            return this;
294        }
295
296        public Builder setVideoHeight(int videoHeight) {
297            mVideoHeight = videoHeight;
298            return this;
299        }
300
301        public Builder setAudioLanguage(String audioLanguage) {
302            mAudioLanguage = audioLanguage;
303            return this;
304        }
305
306        public Builder setContentRating(String contentRating) {
307            mContentRating = contentRating;
308            return this;
309        }
310
311        private Uri toUri(String uriString) {
312            try {
313                return uriString == null ? null : Uri.parse(uriString);
314            } catch (Exception e) {
315                return null;
316            }
317        }
318
319        public Builder setPosterArt(String posterArtUri) {
320            return setPosterArt(toUri(posterArtUri));
321        }
322
323        public Builder setPosterArt(Uri posterArt) {
324            mPosterArt = posterArt;
325            return this;
326        }
327
328        public Builder setThumbnail(String thumbnailUri) {
329            return setThumbnail(toUri(thumbnailUri));
330        }
331
332        public Builder setThumbnail(Uri thumbnail) {
333            mThumbnail = thumbnail;
334            return this;
335        }
336
337        public Builder setSearchable(boolean searchable) {
338            mSearchable = searchable;
339            return this;
340        }
341
342        public Builder setDataUri(String dataUri) {
343            return setDataUri(toUri(dataUri));
344        }
345
346        public Builder setDataUri(Uri dataUri) {
347            mDataUri = dataUri;
348            return this;
349        }
350
351        public Builder setDataBytes(long dataBytes) {
352            mDataBytes = dataBytes;
353            return this;
354        }
355
356        public Builder setDurationMillis(long durationMillis) {
357            mDurationMillis = durationMillis;
358            return this;
359        }
360
361        public Builder setExpireTimeUtcMillis(long expireTimeUtcMillis) {
362            mExpireTimeUtcMillis = expireTimeUtcMillis;
363            return this;
364        }
365
366        public Builder setInternalProviderData(byte[] internalProviderData) {
367            mInternalProviderData = internalProviderData;
368            return this;
369        }
370
371        public Builder setInternalProviderFlag1(int internalProviderFlag1) {
372            mInternalProviderFlag1 = internalProviderFlag1;
373            return this;
374        }
375
376        public Builder setInternalProviderFlag2(int internalProviderFlag2) {
377            mInternalProviderFlag2 = internalProviderFlag2;
378            return this;
379        }
380
381        public Builder setInternalProviderFlag3(int internalProviderFlag3) {
382            mInternalProviderFlag3 = internalProviderFlag3;
383            return this;
384        }
385
386        public Builder setInternalProviderFlag4(int internalProviderFlag4) {
387            mInternalProviderFlag4 = internalProviderFlag4;
388            return this;
389        }
390
391        public Builder setVersionNumber(int versionNumber) {
392            mVersionNumber = versionNumber;
393            return this;
394        }
395
396        public RecordedProgram build() {
397            return new RecordedProgram(mId, mInputId, mChannelId, mTitle, mSeasonNumber,
398                    mSeasonTitle, mEpisodeNumber, mEpisodeTitle, mStartTimeUtcMillis,
399                    mEndTimeUtcMillis, mBroadcastGenres, mCanonicalGenres, mShortDescription,
400                    mLongDescription, mVideoWidth, mVideoHeight, mAudioLanguage, mContentRating,
401                    mPosterArt, mThumbnail, mSearchable, mDataUri, mDataBytes, mDurationMillis,
402                    mExpireTimeUtcMillis, mInternalProviderData, mInternalProviderFlag1,
403                    mInternalProviderFlag2, mInternalProviderFlag3, mInternalProviderFlag4,
404                    mVersionNumber);
405        }
406    }
407
408    public static Builder builder() { return new Builder(); }
409
410    public static Builder buildFrom(RecordedProgram orig) {
411        return builder()
412                .setId(orig.getId())
413                .setInputId(orig.getInputId())
414                .setChannelId(orig.getChannelId())
415                .setTitle(orig.getTitle())
416                .setSeasonNumber(orig.getSeasonNumber())
417                .setSeasonTitle(orig.getSeasonTitle())
418                .setEpisodeNumber(orig.getEpisodeNumber())
419                .setEpisodeTitle(orig.getEpisodeTitle())
420                .setStartTimeUtcMillis(orig.getStartTimeUtcMillis())
421                .setEndTimeUtcMillis(orig.getEndTimeUtcMillis())
422                .setBroadcastGenres(orig.getBroadcastGenres())
423                .setCanonicalGenres(orig.getCanonicalGenres())
424                .setShortDescription(orig.getShortDescription())
425                .setLongDescription(orig.getLongDescription())
426                .setVideoWidth(orig.getVideoWidth())
427                .setVideoHeight(orig.getVideoHeight())
428                .setAudioLanguage(orig.getAudioLanguage())
429                .setContentRating(orig.getContentRating())
430                .setPosterArt(orig.getPosterArt())
431                .setThumbnail(orig.getThumbnail())
432                .setSearchable(orig.isSearchable())
433                .setInternalProviderData(orig.getInternalProviderData())
434                .setInternalProviderFlag1(orig.getInternalProviderFlag1())
435                .setInternalProviderFlag2(orig.getInternalProviderFlag2())
436                .setInternalProviderFlag3(orig.getInternalProviderFlag3())
437                .setInternalProviderFlag4(orig.getInternalProviderFlag4())
438                .setVersionNumber(orig.getVersionNumber());
439    }
440
441    public static final Comparator<RecordedProgram> START_TIME_THEN_ID_COMPARATOR
442            = new Comparator<RecordedProgram>() {
443        @Override
444        public int compare(RecordedProgram lhs, RecordedProgram rhs) {
445            int res = Long.compare(lhs.getStartTimeUtcMillis(), rhs.getStartTimeUtcMillis());
446            if (res != 0) {
447                return res;
448            }
449            return Long.compare(lhs.mId, rhs.mId);
450        }
451    };
452
453    private final long mId;
454    private final String mInputId;
455    private final long mChannelId;
456    private final String mTitle;
457    private final String mSeasonNumber;
458    private final String mSeasonTitle;
459    private final String mEpisodeNumber;
460    private final String mEpisodeTitle;
461    private final long mStartTimeUtcMillis;
462    private final long mEndTimeUtcMillis;
463    private final String[] mBroadcastGenres;
464    private final String[] mCanonicalGenres;
465    private final String mShortDescription;
466    private final String mLongDescription;
467    private final int mVideoWidth;
468    private final int mVideoHeight;
469    private final String mAudioLanguage;
470    private final String mContentRating;
471    private final Uri mPosterArt;
472    private final Uri mThumbnail;
473    private final boolean mSearchable;
474    private final Uri mDataUri;
475    private final long mDataBytes;
476    private final long mDurationMillis;
477    private final long mExpireTimeUtcMillis;
478    private final byte[] mInternalProviderData;
479    private final int mInternalProviderFlag1;
480    private final int mInternalProviderFlag2;
481    private final int mInternalProviderFlag3;
482    private final int mInternalProviderFlag4;
483    private final int mVersionNumber;
484
485    private RecordedProgram(long id, String inputId, long channelId, String title,
486            String seasonNumber, String seasonTitle, String episodeNumber, String episodeTitle,
487            long startTimeUtcMillis, long endTimeUtcMillis, String[] broadcastGenres,
488            String[] canonicalGenres, String shortDescription, String longDescription,
489            int videoWidth, int videoHeight, String audioLanguage, String contentRating,
490            Uri posterArt, Uri thumbnail, boolean searchable, Uri dataUri, long dataBytes,
491            long durationMillis, long expireTimeUtcMillis, byte[] internalProviderData,
492            int internalProviderFlag1, int internalProviderFlag2, int internalProviderFlag3,
493            int internalProviderFlag4, int versionNumber) {
494        mId = id;
495        mInputId = inputId;
496        mChannelId = channelId;
497        mTitle = title;
498        mSeasonNumber = seasonNumber;
499        mSeasonTitle = seasonTitle;
500        mEpisodeNumber = episodeNumber;
501        mEpisodeTitle = episodeTitle;
502        mStartTimeUtcMillis = startTimeUtcMillis;
503        mEndTimeUtcMillis = endTimeUtcMillis;
504        mBroadcastGenres = broadcastGenres;
505        mCanonicalGenres = canonicalGenres;
506        mShortDescription = shortDescription;
507        mLongDescription = longDescription;
508        mVideoWidth = videoWidth;
509        mVideoHeight = videoHeight;
510
511        mAudioLanguage = audioLanguage;
512        mContentRating = contentRating;
513        mPosterArt = posterArt;
514        mThumbnail = thumbnail;
515        mSearchable = searchable;
516        mDataUri = dataUri;
517        mDataBytes = dataBytes;
518        mDurationMillis = durationMillis;
519        mExpireTimeUtcMillis = expireTimeUtcMillis;
520        mInternalProviderData = internalProviderData;
521        mInternalProviderFlag1 = internalProviderFlag1;
522        mInternalProviderFlag2 = internalProviderFlag2;
523        mInternalProviderFlag3 = internalProviderFlag3;
524        mInternalProviderFlag4 = internalProviderFlag4;
525        mVersionNumber = versionNumber;
526    }
527
528    public String getAudioLanguage() {
529        return mAudioLanguage;
530    }
531
532    public String[] getBroadcastGenres() {
533        return mBroadcastGenres;
534    }
535
536    public String[] getCanonicalGenres() {
537        return mCanonicalGenres;
538    }
539
540    public long getChannelId() {
541        return mChannelId;
542    }
543
544    public String getContentRating() {
545        return mContentRating;
546    }
547
548    public Uri getDataUri() {
549        return mDataUri;
550    }
551
552    public long getDataBytes() {
553        return mDataBytes;
554    }
555
556    public long getDurationMillis() {
557        return mDurationMillis;
558    }
559
560    public long getEndTimeUtcMillis() {
561        return mEndTimeUtcMillis;
562    }
563
564    public String getEpisodeNumber() {
565        return mEpisodeNumber;
566    }
567
568    public String getEpisodeTitle() {
569        return mEpisodeTitle;
570    }
571
572    public String getEpisodeDisplayTitle(Context context) {
573        if (!TextUtils.isEmpty(mSeasonNumber) && !TextUtils.isEmpty(mEpisodeNumber)
574                && !TextUtils.isEmpty(mEpisodeTitle)) {
575            return String.format(context.getResources().getString(R.string.episode_format),
576                    mSeasonNumber, mEpisodeNumber, mEpisodeTitle);
577        }
578        return mEpisodeTitle;
579    }
580
581    public long getExpireTimeUtcMillis() {
582        return mExpireTimeUtcMillis;
583    }
584
585    public long getId() {
586        return mId;
587    }
588
589    public String getInputId() {
590        return mInputId;
591    }
592
593    public byte[] getInternalProviderData() {
594        return mInternalProviderData;
595    }
596
597    public int getInternalProviderFlag1() {
598        return mInternalProviderFlag1;
599    }
600
601    public int getInternalProviderFlag2() {
602        return mInternalProviderFlag2;
603    }
604
605    public int getInternalProviderFlag3() {
606        return mInternalProviderFlag3;
607    }
608
609    public int getInternalProviderFlag4() {
610        return mInternalProviderFlag4;
611    }
612
613    public String getLongDescription() {
614        return mLongDescription;
615    }
616
617    public Uri getPosterArt() {
618        return mPosterArt;
619    }
620
621    public boolean isSearchable() {
622        return mSearchable;
623    }
624
625    public String getSeasonNumber() {
626        return mSeasonNumber;
627    }
628
629    public String getSeasonTitle() {
630        return mSeasonTitle;
631    }
632
633    public String getShortDescription() {
634        return mShortDescription;
635    }
636
637    public long getStartTimeUtcMillis() {
638        return mStartTimeUtcMillis;
639    }
640
641    public Uri getThumbnail() {
642        return mThumbnail;
643    }
644
645    public String getTitle() {
646        return mTitle;
647    }
648
649    public Uri getUri() {
650        return ContentUris.withAppendedId(RecordedPrograms.CONTENT_URI, mId);
651    }
652
653    public int getVersionNumber() {
654        return mVersionNumber;
655    }
656
657    public int getVideoHeight() {
658        return mVideoHeight;
659    }
660
661    public int getVideoWidth() {
662        return mVideoWidth;
663    }
664
665    /**
666     * Compares everything except {@link #getInternalProviderData()}
667     */
668    @Override
669    public boolean equals(Object o) {
670        if (this == o) return true;
671        if (o == null || getClass() != o.getClass()) return false;
672        RecordedProgram that = (RecordedProgram) o;
673        return Objects.equals(mId, that.mId) &&
674                Objects.equals(mChannelId, that.mChannelId) &&
675                Objects.equals(mSeasonNumber, that.mSeasonNumber) &&
676                Objects.equals(mSeasonTitle, that.mSeasonTitle) &&
677                Objects.equals(mEpisodeNumber, that.mEpisodeNumber) &&
678                Objects.equals(mStartTimeUtcMillis, that.mStartTimeUtcMillis) &&
679                Objects.equals(mEndTimeUtcMillis, that.mEndTimeUtcMillis) &&
680                Objects.equals(mVideoWidth, that.mVideoWidth) &&
681                Objects.equals(mVideoHeight, that.mVideoHeight) &&
682                Objects.equals(mSearchable, that.mSearchable) &&
683                Objects.equals(mDataBytes, that.mDataBytes) &&
684                Objects.equals(mDurationMillis, that.mDurationMillis) &&
685                Objects.equals(mExpireTimeUtcMillis, that.mExpireTimeUtcMillis) &&
686                Objects.equals(mInternalProviderFlag1, that.mInternalProviderFlag1) &&
687                Objects.equals(mInternalProviderFlag2, that.mInternalProviderFlag2) &&
688                Objects.equals(mInternalProviderFlag3, that.mInternalProviderFlag3) &&
689                Objects.equals(mInternalProviderFlag4, that.mInternalProviderFlag4) &&
690                Objects.equals(mVersionNumber, that.mVersionNumber) &&
691                Objects.equals(mTitle, that.mTitle) &&
692                Objects.equals(mEpisodeTitle, that.mEpisodeTitle) &&
693                Arrays.equals(mBroadcastGenres, that.mBroadcastGenres) &&
694                Arrays.equals(mCanonicalGenres, that.mCanonicalGenres) &&
695                Objects.equals(mShortDescription, that.mShortDescription) &&
696                Objects.equals(mLongDescription, that.mLongDescription) &&
697                Objects.equals(mAudioLanguage, that.mAudioLanguage) &&
698                Objects.equals(mContentRating, that.mContentRating) &&
699                Objects.equals(mPosterArt, that.mPosterArt) &&
700                Objects.equals(mThumbnail, that.mThumbnail);
701    }
702
703    /**
704     * Hashes based on the ID.
705     */
706    @Override
707    public int hashCode() {
708        return Objects.hash(mId);
709    }
710
711    @Override
712    public String toString() {
713        return "RecordedProgram"
714                + "[" +  mId +
715                "]{ mInputId=" + mInputId +
716                ", mChannelId='" + mChannelId + '\'' +
717                ", mTitle='" + mTitle + '\'' +
718                ", mEpisodeNumber=" + mEpisodeNumber +
719                ", mEpisodeTitle='" + mEpisodeTitle + '\'' +
720                ", mStartTimeUtcMillis=" + mStartTimeUtcMillis +
721                ", mEndTimeUtcMillis=" + mEndTimeUtcMillis +
722                ", mBroadcastGenres=" +
723                        (mBroadcastGenres != null ? Arrays.toString(mBroadcastGenres) : "null") +
724                ", mCanonicalGenres=" +
725                        (mCanonicalGenres != null ? Arrays.toString(mCanonicalGenres) : "null") +
726                ", mShortDescription='" + mShortDescription + '\'' +
727                ", mLongDescription='" + mLongDescription + '\'' +
728                ", mVideoHeight=" + mVideoHeight +
729                ", mVideoWidth=" + mVideoWidth +
730                ", mAudioLanguage='" + mAudioLanguage + '\'' +
731                ", mContentRating='" + mContentRating + '\'' +
732                ", mPosterArt=" + mPosterArt +
733                ", mThumbnail=" + mThumbnail +
734                ", mSearchable=" + mSearchable +
735                ", mDataUri=" + mDataUri +
736                ", mDataBytes=" + mDataBytes +
737                ", mDurationMillis=" + mDurationMillis +
738                ", mExpireTimeUtcMillis=" + mExpireTimeUtcMillis +
739                ", mInternalProviderData.length=" +
740                        (mInternalProviderData == null ? "null" : mInternalProviderData.length) +
741                ", mInternalProviderFlag1=" + mInternalProviderFlag1 +
742                ", mInternalProviderFlag2=" + mInternalProviderFlag2 +
743                ", mInternalProviderFlag3=" + mInternalProviderFlag3 +
744                ", mInternalProviderFlag4=" + mInternalProviderFlag4 +
745                ", mSeasonNumber=" + mSeasonNumber +
746                ", mSeasonTitle=" + mSeasonTitle +
747                ", mVersionNumber=" + mVersionNumber +
748                '}';
749    }
750
751    @Nullable
752    private static String safeToString(@Nullable Object o) {
753        return o == null ? null : o.toString();
754    }
755
756    @Nullable
757    private static String safeEncode(@Nullable String[] genres) {
758        return genres == null ? null : TvContract.Programs.Genres.encode(genres);
759    }
760}
761