1/*
2 * Copyright (C) 2017 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 */
16package androidx.tvprovider.media.tv;
17
18import static androidx.annotation.RestrictTo.Scope.LIBRARY;
19import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
20
21import android.content.ContentValues;
22import android.database.Cursor;
23import android.media.tv.TvContentRating;
24import android.net.Uri;
25import android.os.Build;
26
27import androidx.annotation.IntDef;
28import androidx.annotation.RestrictTo;
29import androidx.tvprovider.media.tv.TvContractCompat.BaseTvColumns;
30import androidx.tvprovider.media.tv.TvContractCompat.ProgramColumns;
31import androidx.tvprovider.media.tv.TvContractCompat.Programs;
32import androidx.tvprovider.media.tv.TvContractCompat.Programs.Genres.Genre;
33
34import java.lang.annotation.Retention;
35import java.lang.annotation.RetentionPolicy;
36
37/**
38 * Base class for derived classes that want to have common fields for programs defined in
39 * {@link TvContractCompat}.
40 * @hide
41 */
42@RestrictTo(LIBRARY)
43public abstract class BaseProgram {
44    /**
45     * @hide
46     */
47    @RestrictTo(LIBRARY_GROUP)
48    public static final String[] PROJECTION = getProjection();
49
50    private static final long INVALID_LONG_VALUE = -1;
51    private static final int INVALID_INT_VALUE = -1;
52    private static final int IS_SEARCHABLE = 1;
53
54    /** @hide */
55    @IntDef({
56            REVIEW_RATING_STYLE_UNKNOWN,
57            ProgramColumns.REVIEW_RATING_STYLE_STARS,
58            ProgramColumns.REVIEW_RATING_STYLE_THUMBS_UP_DOWN,
59            ProgramColumns.REVIEW_RATING_STYLE_PERCENTAGE,
60    })
61    @Retention(RetentionPolicy.SOURCE)
62    @RestrictTo(LIBRARY_GROUP)
63    @interface ReviewRatingStyle {}
64
65    /**
66     * The unknown review rating style.
67     */
68    private static final int REVIEW_RATING_STYLE_UNKNOWN = -1;
69
70    /** @hide */
71    @RestrictTo(LIBRARY_GROUP)
72    protected ContentValues mValues;
73
74    /* package-private */
75    BaseProgram(Builder builder) {
76        mValues = builder.mValues;
77    }
78
79    /**
80     * @return The ID for the program.
81     * @see androidx.tvprovider.media.tv.TvContractCompat.BaseTvColumns#_ID
82     */
83    public long getId() {
84        Long l = mValues.getAsLong(BaseTvColumns._ID);
85        return l == null ? INVALID_LONG_VALUE : l;
86    }
87
88    /**
89     * @return The package name for the program.
90     * @see androidx.tvprovider.media.tv.TvContractCompat.BaseTvColumns#COLUMN_PACKAGE_NAME
91     * @hide
92     */
93    @RestrictTo(LIBRARY_GROUP)
94    public String getPackageName() {
95        return mValues.getAsString(BaseTvColumns.COLUMN_PACKAGE_NAME);
96    }
97
98    /**
99     * @return The title for the program.
100     * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_TITLE
101     */
102    public String getTitle() {
103        return mValues.getAsString(Programs.COLUMN_TITLE);
104    }
105
106    /**
107     * @return The episode title for the program.
108     * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_EPISODE_TITLE
109     */
110    public String getEpisodeTitle() {
111        return mValues.getAsString(Programs.COLUMN_EPISODE_TITLE);
112    }
113
114    /**
115     * @return The season display number for the program.
116     * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_SEASON_DISPLAY_NUMBER
117     */
118    public String getSeasonNumber() {
119        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
120            return mValues.getAsString(Programs.COLUMN_SEASON_DISPLAY_NUMBER);
121        } else {
122            return mValues.getAsString(Programs.COLUMN_SEASON_NUMBER);
123        }
124    }
125
126    /**
127     * @return The episode display number for the program.
128     * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_EPISODE_DISPLAY_NUMBER
129     */
130    public String getEpisodeNumber() {
131        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
132            return mValues.getAsString(Programs.COLUMN_EPISODE_DISPLAY_NUMBER);
133        } else {
134            return mValues.getAsString(Programs.COLUMN_EPISODE_NUMBER);
135        }
136    }
137
138    /**
139     * @return The short description for the program.
140     * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_SHORT_DESCRIPTION
141     */
142    public String getDescription() {
143        return mValues.getAsString(Programs.COLUMN_SHORT_DESCRIPTION);
144    }
145
146    /**
147     * @return The long description for the program.
148     * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_LONG_DESCRIPTION
149     */
150    public String getLongDescription() {
151        return mValues.getAsString(Programs.COLUMN_LONG_DESCRIPTION);
152    }
153
154    /**
155     * @return The video width for the program.
156     * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_VIDEO_WIDTH
157     */
158    public int getVideoWidth() {
159        Integer i = mValues.getAsInteger(Programs.COLUMN_VIDEO_WIDTH);
160        return i == null ? INVALID_INT_VALUE : i;
161    }
162
163    /**
164     * @return The video height for the program.
165     * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_VIDEO_HEIGHT
166     */
167    public int getVideoHeight() {
168        Integer i = mValues.getAsInteger(Programs.COLUMN_VIDEO_HEIGHT);
169        return i == null ? INVALID_INT_VALUE : i;
170    }
171
172    /**
173     * @return The canonical genre for the program.
174     * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_CANONICAL_GENRE
175     */
176    public @Genre String[] getCanonicalGenres() {
177        return Programs.Genres.decode(mValues.getAsString(Programs.COLUMN_CANONICAL_GENRE));
178    }
179
180    /**
181     * @return The content rating for the program.
182     * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_CONTENT_RATING
183     */
184    public TvContentRating[] getContentRatings() {
185        return TvContractUtils.stringToContentRatings(mValues.getAsString(
186                Programs.COLUMN_CONTENT_RATING));
187    }
188
189    /**
190     * @return The poster art URI for the program.
191     * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_POSTER_ART_URI
192     */
193    public Uri getPosterArtUri() {
194        String uri = mValues.getAsString(Programs.COLUMN_POSTER_ART_URI);
195        return uri == null ? null : Uri.parse(uri);
196    }
197
198    /**
199     * @return The thumbnail URI for the program.
200     * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_THUMBNAIL_URI
201     */
202    public Uri getThumbnailUri() {
203        String uri = mValues.getAsString(Programs.COLUMN_POSTER_ART_URI);
204        return uri == null ? null : Uri.parse(uri);
205    }
206
207    /**
208     * @return The internal provider data for the program.
209     * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_INTERNAL_PROVIDER_DATA
210     */
211    public byte[] getInternalProviderDataByteArray() {
212        return mValues.getAsByteArray(Programs.COLUMN_INTERNAL_PROVIDER_DATA);
213    }
214
215    /**
216     * @return The audio languages for the program.
217     * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_AUDIO_LANGUAGE
218     */
219    public String[] getAudioLanguages() {
220        return TvContractUtils.stringToAudioLanguages(mValues.getAsString(
221                Programs.COLUMN_AUDIO_LANGUAGE));
222    }
223
224    /**
225     * @return Whether the program is searchable or not.
226     * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_SEARCHABLE
227     */
228    public boolean isSearchable() {
229        Integer i = mValues.getAsInteger(Programs.COLUMN_SEARCHABLE);
230        return i == null || i == IS_SEARCHABLE;
231    }
232
233    /**
234     * @return The first internal provider flag for the program.
235     * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_INTERNAL_PROVIDER_FLAG1
236     */
237    public Long getInternalProviderFlag1() {
238        return mValues.getAsLong(Programs.COLUMN_INTERNAL_PROVIDER_FLAG1);
239    }
240
241    /**
242     * @return The second internal provider flag for the program.
243     * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_INTERNAL_PROVIDER_FLAG2
244     */
245    public Long getInternalProviderFlag2() {
246        return mValues.getAsLong(Programs.COLUMN_INTERNAL_PROVIDER_FLAG2);
247    }
248
249    /**
250     * @return The third internal provider flag for the program.
251     * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_INTERNAL_PROVIDER_FLAG3
252     */
253    public Long getInternalProviderFlag3() {
254        return mValues.getAsLong(Programs.COLUMN_INTERNAL_PROVIDER_FLAG3);
255    }
256
257    /**
258     * @return The forth internal provider flag for the program.
259     * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_INTERNAL_PROVIDER_FLAG4
260     */
261    public Long getInternalProviderFlag4() {
262        return mValues.getAsLong(Programs.COLUMN_INTERNAL_PROVIDER_FLAG4);
263    }
264
265    /**
266     * @return The season title for the program.
267     * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_SEASON_TITLE
268     */
269    public String getSeasonTitle() {
270        return mValues.getAsString(Programs.COLUMN_SEASON_TITLE);
271    }
272
273    /**
274     * @return The review rating style for the program.
275     * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_REVIEW_RATING_STYLE
276     */
277    public @ReviewRatingStyle int getReviewRatingStyle() {
278        Integer i = mValues.getAsInteger(Programs.COLUMN_REVIEW_RATING_STYLE);
279        return i == null ? REVIEW_RATING_STYLE_UNKNOWN : i;
280    }
281
282    /**
283     * @return The review rating for the program.
284     * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_REVIEW_RATING
285     */
286    public String getReviewRating() {
287        return mValues.getAsString(Programs.COLUMN_REVIEW_RATING);
288    }
289
290    @Override
291    public int hashCode() {
292        return mValues.hashCode();
293    }
294
295    @Override
296    public boolean equals(Object other) {
297        if (!(other instanceof BaseProgram)) {
298            return false;
299        }
300        return mValues.equals(((BaseProgram) other).mValues);
301    }
302
303    @Override
304    public String toString() {
305        return "BaseProgram{" + mValues.toString() + "}";
306    }
307
308    /**
309     * @return The fields of the BaseProgram in {@link ContentValues} format to be easily inserted
310     * into the TV Input Framework database.
311     */
312    public ContentValues toContentValues() {
313        ContentValues values = new ContentValues(mValues);
314        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
315            values.remove(ProgramColumns.COLUMN_SEARCHABLE);
316            values.remove(ProgramColumns.COLUMN_INTERNAL_PROVIDER_FLAG1);
317            values.remove(ProgramColumns.COLUMN_INTERNAL_PROVIDER_FLAG2);
318            values.remove(ProgramColumns.COLUMN_INTERNAL_PROVIDER_FLAG3);
319            values.remove(ProgramColumns.COLUMN_INTERNAL_PROVIDER_FLAG4);
320        }
321        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
322            values.remove(ProgramColumns.COLUMN_SEASON_TITLE);
323        }
324        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
325            values.remove(ProgramColumns.COLUMN_REVIEW_RATING_STYLE);
326            values.remove(ProgramColumns.COLUMN_REVIEW_RATING);
327        }
328        return values;
329    }
330
331    /**
332     * Sets the fields in the cursor to the given builder instance.
333     *
334     * @param cursor A row from the TV Input Framework database.
335     * @param builder A Builder to set the fields.
336     */
337    static void setFieldsFromCursor(Cursor cursor, Builder builder) {
338        // TODO: Add additional API which does not use costly getColumnIndex().
339        int index;
340        if ((index = cursor.getColumnIndex(BaseTvColumns._ID)) >= 0 && !cursor.isNull(index)) {
341            builder.setId(cursor.getLong(index));
342        }
343        if ((index = cursor.getColumnIndex(BaseTvColumns.COLUMN_PACKAGE_NAME)) >= 0
344                && !cursor.isNull(index)) {
345            builder.setPackageName(cursor.getString(index));
346        }
347        if ((index = cursor.getColumnIndex(ProgramColumns.COLUMN_TITLE)) >= 0
348                && !cursor.isNull(index)) {
349            builder.setTitle(cursor.getString(index));
350        }
351        if ((index = cursor.getColumnIndex(ProgramColumns.COLUMN_EPISODE_TITLE)) >= 0
352                && !cursor.isNull(index)) {
353            builder.setEpisodeTitle(cursor.getString(index));
354        }
355        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
356            if ((index =
357                    cursor.getColumnIndex(ProgramColumns.COLUMN_SEASON_DISPLAY_NUMBER)) >= 0
358                    && !cursor.isNull(index)) {
359                builder.setSeasonNumber(cursor.getString(index), INVALID_INT_VALUE);
360            }
361        } else {
362            if ((index = cursor.getColumnIndex(Programs.COLUMN_SEASON_NUMBER)) >= 0
363                    && !cursor.isNull(index)) {
364                builder.setSeasonNumber(cursor.getInt(index));
365            }
366        }
367        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
368            if ((index =
369                    cursor.getColumnIndex(ProgramColumns.COLUMN_EPISODE_DISPLAY_NUMBER)) >= 0
370                    && !cursor.isNull(index)) {
371                builder.setEpisodeNumber(cursor.getString(index), INVALID_INT_VALUE);
372            }
373        } else {
374            if ((index = cursor.getColumnIndex(Programs.COLUMN_EPISODE_NUMBER)) >= 0
375                    && !cursor.isNull(index)) {
376                builder.setEpisodeNumber(cursor.getInt(index));
377            }
378        }
379        if ((index = cursor.getColumnIndex(ProgramColumns.COLUMN_SHORT_DESCRIPTION)) >= 0
380                && !cursor.isNull(index)) {
381            builder.setDescription(cursor.getString(index));
382        }
383        if ((index = cursor.getColumnIndex(ProgramColumns.COLUMN_LONG_DESCRIPTION)) >= 0
384                && !cursor.isNull(index)) {
385            builder.setLongDescription(cursor.getString(index));
386        }
387        if ((index = cursor.getColumnIndex(ProgramColumns.COLUMN_POSTER_ART_URI)) >= 0
388                && !cursor.isNull(index)) {
389            builder.setPosterArtUri(Uri.parse(cursor.getString(index)));
390        }
391        if ((index = cursor.getColumnIndex(ProgramColumns.COLUMN_THUMBNAIL_URI)) >= 0
392                && !cursor.isNull(index)) {
393            builder.setThumbnailUri(Uri.parse(cursor.getString(index)));
394        }
395        if ((index = cursor.getColumnIndex(ProgramColumns.COLUMN_AUDIO_LANGUAGE)) >= 0
396                && !cursor.isNull(index)) {
397            builder.setAudioLanguages(
398                    TvContractUtils.stringToAudioLanguages(cursor.getString(index)));
399        }
400        if ((index = cursor.getColumnIndex(ProgramColumns.COLUMN_CANONICAL_GENRE)) >= 0
401                && !cursor.isNull(index)) {
402            builder.setCanonicalGenres(Programs.Genres.decode(
403                    cursor.getString(index)));
404        }
405        if ((index = cursor.getColumnIndex(ProgramColumns.COLUMN_CONTENT_RATING)) >= 0
406                && !cursor.isNull(index)) {
407            builder.setContentRatings(
408                    TvContractUtils.stringToContentRatings(cursor.getString(index)));
409        }
410        if ((index = cursor.getColumnIndex(ProgramColumns.COLUMN_VIDEO_WIDTH)) >= 0
411                && !cursor.isNull(index)) {
412            builder.setVideoWidth((int) cursor.getLong(index));
413        }
414        if ((index = cursor.getColumnIndex(ProgramColumns.COLUMN_VIDEO_HEIGHT)) >= 0
415                && !cursor.isNull(index)) {
416            builder.setVideoHeight((int) cursor.getLong(index));
417        }
418        if ((index = cursor.getColumnIndex(ProgramColumns.COLUMN_INTERNAL_PROVIDER_DATA)) >= 0
419                && !cursor.isNull(index)) {
420            builder.setInternalProviderData(cursor.getBlob(index));
421        }
422        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
423            if ((index = cursor.getColumnIndex(ProgramColumns.COLUMN_SEARCHABLE)) >= 0
424                    && !cursor.isNull(index)) {
425                builder.setSearchable(cursor.getInt(index) == IS_SEARCHABLE);
426            }
427            if ((index =
428                    cursor.getColumnIndex(ProgramColumns.COLUMN_INTERNAL_PROVIDER_FLAG1)) >= 0
429                    && !cursor.isNull(index)) {
430                builder.setInternalProviderFlag1(cursor.getLong(index));
431            }
432            if ((index =
433                    cursor.getColumnIndex(ProgramColumns.COLUMN_INTERNAL_PROVIDER_FLAG2)) >= 0
434                    && !cursor.isNull(index)) {
435                builder.setInternalProviderFlag2(cursor.getLong(index));
436            }
437            if ((index =
438                    cursor.getColumnIndex(ProgramColumns.COLUMN_INTERNAL_PROVIDER_FLAG3)) >= 0
439                    && !cursor.isNull(index)) {
440                builder.setInternalProviderFlag3(cursor.getLong(index));
441            }
442            if ((index =
443                    cursor.getColumnIndex(ProgramColumns.COLUMN_INTERNAL_PROVIDER_FLAG4)) >= 0
444                    && !cursor.isNull(index)) {
445                builder.setInternalProviderFlag4(cursor.getLong(index));
446            }
447        }
448        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
449            if ((index = cursor.getColumnIndex(ProgramColumns.COLUMN_SEASON_TITLE)) >= 0
450                    && !cursor.isNull(index)) {
451                builder.setSeasonTitle(cursor.getString(index));
452            }
453        }
454        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
455            if ((index = cursor.getColumnIndex(
456                    ProgramColumns.COLUMN_REVIEW_RATING_STYLE)) >= 0
457                    && !cursor.isNull(index)) {
458                builder.setReviewRatingStyle(cursor.getInt(index));
459            }
460            if ((index = cursor.getColumnIndex(ProgramColumns.COLUMN_REVIEW_RATING)) >= 0
461                    && !cursor.isNull(index)) {
462                builder.setReviewRating(cursor.getString(index));
463            }
464        }
465    }
466
467    private static String[] getProjection() {
468        String[] baseColumns = new String[] {
469                BaseTvColumns._ID,
470                BaseTvColumns.COLUMN_PACKAGE_NAME,
471                ProgramColumns.COLUMN_TITLE,
472                ProgramColumns.COLUMN_EPISODE_TITLE,
473                (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
474                        ? ProgramColumns.COLUMN_SEASON_DISPLAY_NUMBER
475                        : Programs.COLUMN_SEASON_NUMBER,
476                (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
477                        ? ProgramColumns.COLUMN_EPISODE_DISPLAY_NUMBER
478                        : Programs.COLUMN_EPISODE_NUMBER,
479                ProgramColumns.COLUMN_SHORT_DESCRIPTION,
480                ProgramColumns.COLUMN_LONG_DESCRIPTION,
481                ProgramColumns.COLUMN_POSTER_ART_URI,
482                ProgramColumns.COLUMN_THUMBNAIL_URI,
483                ProgramColumns.COLUMN_AUDIO_LANGUAGE,
484                ProgramColumns.COLUMN_CANONICAL_GENRE,
485                ProgramColumns.COLUMN_CONTENT_RATING,
486                ProgramColumns.COLUMN_VIDEO_WIDTH,
487                ProgramColumns.COLUMN_VIDEO_HEIGHT,
488                ProgramColumns.COLUMN_INTERNAL_PROVIDER_DATA
489        };
490        String[] marshmallowColumns = new String[] {
491                ProgramColumns.COLUMN_SEARCHABLE,
492                ProgramColumns.COLUMN_INTERNAL_PROVIDER_FLAG1,
493                ProgramColumns.COLUMN_INTERNAL_PROVIDER_FLAG2,
494                ProgramColumns.COLUMN_INTERNAL_PROVIDER_FLAG3,
495                ProgramColumns.COLUMN_INTERNAL_PROVIDER_FLAG4,
496        };
497        String[] nougatColumns = new String[] {
498                ProgramColumns.COLUMN_SEASON_TITLE,
499        };
500        String[] oColumns = new String[] {
501                ProgramColumns.COLUMN_REVIEW_RATING,
502                ProgramColumns.COLUMN_REVIEW_RATING_STYLE,
503        };
504        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
505            return CollectionUtils.concatAll(baseColumns, marshmallowColumns, nougatColumns,
506                oColumns);
507        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
508            return CollectionUtils.concatAll(baseColumns, marshmallowColumns, nougatColumns);
509        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
510            return CollectionUtils.concatAll(baseColumns, marshmallowColumns);
511        } else {
512            return baseColumns;
513        }
514    }
515
516    /**
517     * This Builder class simplifies the creation of a {@link BaseProgram} object.
518     *
519     * @param <T> The Builder of the derived classe.
520     */
521    public abstract static class Builder<T extends Builder> {
522        /** @hide */
523        @RestrictTo(LIBRARY_GROUP)
524        protected ContentValues mValues;
525
526        /**
527         * Creates a new Builder object.
528         */
529        public Builder() {
530            mValues = new ContentValues();
531        }
532
533        /**
534         * Creates a new Builder object with values copied from another Program.
535         * @param other The Program you're copying from.
536         */
537        public Builder(BaseProgram other) {
538            mValues = new ContentValues(other.mValues);
539        }
540
541        /**
542         * Sets a unique id for this program.
543         *
544         * @param programId The ID for the program.
545         * @return This Builder object to allow for chaining of calls to builder methods.
546         * @see androidx.tvprovider.media.tv.TvContractCompat.BaseTvColumns#_ID
547         */
548        public T setId(long programId) {
549            mValues.put(BaseTvColumns._ID, programId);
550            return (T) this;
551        }
552
553        /**
554         * Sets the package name for this program.
555         *
556         * @param packageName The package name for the program.
557         * @return This Builder object to allow for chaining of calls to builder methods.
558         * @see androidx.tvprovider.media.tv.TvContractCompat.BaseTvColumns#COLUMN_PACKAGE_NAME
559         * @hide
560         */
561        @RestrictTo(LIBRARY_GROUP)
562        public T setPackageName(String packageName) {
563            mValues.put(BaseTvColumns.COLUMN_PACKAGE_NAME, packageName);
564            return (T) this;
565        }
566
567        /**
568         * Sets the title of this program. For a series, this is the series title.
569         *
570         * @param title The title for the program.
571         * @return This Builder object to allow for chaining of calls to builder methods.
572         * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_TITLE
573         */
574        public T setTitle(String title) {
575            mValues.put(Programs.COLUMN_TITLE, title);
576            return (T) this;
577        }
578
579        /**
580         * Sets the title of this particular episode for a series.
581         *
582         * @param episodeTitle The episode title for the program.
583         * @return This Builder object to allow for chaining of calls to builder methods.
584         * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_EPISODE_TITLE
585         */
586        public T setEpisodeTitle(String episodeTitle) {
587            mValues.put(Programs.COLUMN_EPISODE_TITLE, episodeTitle);
588            return (T) this;
589        }
590
591        /**
592         * Sets the season number for this episode for a series.
593         *
594         * @param seasonNumber The season display number for the program.
595         * @return This Builder object to allow for chaining of calls to builder methods.
596         * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_SEASON_DISPLAY_NUMBER
597         */
598        public T setSeasonNumber(int seasonNumber) {
599            setSeasonNumber(String.valueOf(seasonNumber), seasonNumber);
600            return (T) this;
601        }
602
603        /**
604         * Sets the season number for this episode for a series.
605         *
606         * @param seasonNumber The season display number for the program.
607         * @param numericalSeasonNumber An integer value for
608         * {@link androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_SEASON_NUMBER}
609         * which will be used for API Level 23 and below.
610         * @return This Builder object to allow for chaining of calls to builder methods.
611         * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_SEASON_DISPLAY_NUMBER
612         * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_SEASON_NUMBER
613         */
614        public T setSeasonNumber(String seasonNumber, int numericalSeasonNumber) {
615            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
616                mValues.put(Programs.COLUMN_SEASON_DISPLAY_NUMBER, seasonNumber);
617            } else {
618                mValues.put(Programs.COLUMN_SEASON_NUMBER, numericalSeasonNumber);
619            }
620            return (T) this;
621        }
622
623        /**
624         * Sets the episode number in a season for this episode for a series.
625         *
626         * @param episodeNumber The value of episode display number for the program.
627         * @return This Builder object to allow for chaining of calls to builder methods.
628         * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_EPISODE_DISPLAY_NUMBER
629         */
630        public T setEpisodeNumber(int episodeNumber) {
631            setEpisodeNumber(String.valueOf(episodeNumber), episodeNumber);
632            return (T) this;
633        }
634
635        /**
636         * Sets the episode number in a season for this episode for a series.
637         *
638         * @param episodeNumber The value of episode display number for the program.
639         * @param numericalEpisodeNumber An integer value for
640         * {@link androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_EPISODE_NUMBER}
641         * which will be used for API Level 23 and below.
642         * @return This Builder object to allow for chaining of calls to builder methods.
643         * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_EPISODE_DISPLAY_NUMBER
644         * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_EPISODE_NUMBER
645         */
646        public T setEpisodeNumber(String episodeNumber, int numericalEpisodeNumber) {
647            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
648                mValues.put(Programs.COLUMN_EPISODE_DISPLAY_NUMBER, episodeNumber);
649            } else {
650                mValues.put(Programs.COLUMN_EPISODE_NUMBER, numericalEpisodeNumber);
651            }
652            return (T) this;
653        }
654
655        /**
656         * Sets a brief description of the program. For a series, this would be a brief description
657         * of the episode.
658         *
659         * @param description The short description for the program.
660         * @return This Builder object to allow for chaining of calls to builder methods.
661         * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_SHORT_DESCRIPTION
662         */
663        public T setDescription(String description) {
664            mValues.put(Programs.COLUMN_SHORT_DESCRIPTION, description);
665            return (T) this;
666        }
667
668        /**
669         * Sets a longer description of a program if one exists.
670         *
671         * @param longDescription The long description for the program.
672         * @return This Builder object to allow for chaining of calls to builder methods.
673         * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_LONG_DESCRIPTION
674         */
675        public T setLongDescription(String longDescription) {
676            mValues.put(Programs.COLUMN_LONG_DESCRIPTION, longDescription);
677            return (T) this;
678        }
679
680        /**
681         * Sets the video width of the program.
682         *
683         * @param width The video width for the program.
684         * @return This Builder object to allow for chaining of calls to builder methods.
685         * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_VIDEO_WIDTH
686         */
687        public T setVideoWidth(int width) {
688            mValues.put(Programs.COLUMN_VIDEO_WIDTH, width);
689            return (T) this;
690        }
691
692        /**
693         * Sets the video height of the program.
694         *
695         * @param height The video height for the program.
696         * @return This Builder object to allow for chaining of calls to builder methods.
697         * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_VIDEO_HEIGHT
698         */
699        public T setVideoHeight(int height) {
700            mValues.put(Programs.COLUMN_VIDEO_HEIGHT, height);
701            return (T) this;
702        }
703
704        /**
705         * Sets the content ratings for this program.
706         *
707         * @param contentRatings An array of {@link android.media.tv.TvContentRating} that apply to
708         *                       this program  which will be flattened to a String to store in
709         *                       a database.
710         * @return This Builder object to allow for chaining of calls to builder methods.
711         * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_CONTENT_RATING
712         */
713        public T setContentRatings(TvContentRating[] contentRatings) {
714            mValues.put(Programs.COLUMN_CONTENT_RATING,
715                    TvContractUtils.contentRatingsToString(contentRatings));
716            return (T) this;
717        }
718
719        /**
720         * Sets the large poster art of the program.
721         *
722         * @param posterArtUri The poster art URI for the program.
723         * @return This Builder object to allow for chaining of calls to builder methods.
724         * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_POSTER_ART_URI
725         */
726        public T setPosterArtUri(Uri posterArtUri) {
727            mValues.put(Programs.COLUMN_POSTER_ART_URI,
728                    posterArtUri == null ? null : posterArtUri.toString());
729            return (T) this;
730        }
731
732        /**
733         * Sets a small thumbnail of the program.
734         *
735         * @param thumbnailUri The thumbnail URI for the program.
736         * @return This Builder object to allow for chaining of calls to builder methods.
737         * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_THUMBNAIL_URI
738         */
739        public T setThumbnailUri(Uri thumbnailUri) {
740            mValues.put(Programs.COLUMN_THUMBNAIL_URI,
741                    thumbnailUri == null ? null : thumbnailUri.toString());
742            return (T) this;
743        }
744
745        /**
746         * Sets the genres of the program.
747         *
748         * @param genres An array of
749         * {@link androidx.tvprovider.media.tv.TvContractCompat.Programs.Genres}
750         * that apply to the program which will be flattened to a String to store in a database.
751         * @return This Builder object to allow for chaining of calls to builder methods.
752         * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_CANONICAL_GENRE
753         */
754        public T setCanonicalGenres(@Genre String[] genres) {
755            mValues.put(Programs.COLUMN_CANONICAL_GENRE, Programs.Genres.encode(genres));
756            return (T) this;
757        }
758
759        /**
760         * Sets the internal provider data for the program as raw bytes.
761         *
762         * @param data The internal provider data for the program.
763         * @return This Builder object to allow for chaining of calls to builder methods.
764         * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_INTERNAL_PROVIDER_DATA
765         */
766        public T setInternalProviderData(byte[] data) {
767            mValues.put(ProgramColumns.COLUMN_INTERNAL_PROVIDER_DATA, data);
768            return (T) this;
769        }
770
771        /**
772         * Sets the available audio languages for this program as an array of strings.
773         *
774         * @param audioLanguages An array of audio languages, in ISO 639-1 or 639-2/T codes, that
775         *                       apply to this program which will be stored in a database.
776         * @return This Builder object to allow for chaining of calls to builder methods.
777         */
778        public T setAudioLanguages(String[] audioLanguages) {
779            mValues.put(ProgramColumns.COLUMN_AUDIO_LANGUAGE,
780                    TvContractUtils.audioLanguagesToString(audioLanguages));
781            return (T) this;
782        }
783
784        /**
785         * Sets whether this channel can be searched for in other applications.
786         *
787         * @param searchable Whether the program is searchable or not.
788         * @return This Builder object to allow for chaining of calls to builder methods.
789         * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_SEARCHABLE
790         */
791        public T setSearchable(boolean searchable) {
792            mValues.put(Programs.COLUMN_SEARCHABLE, searchable ? IS_SEARCHABLE : 0);
793            return (T) this;
794        }
795
796        /**
797         * Sets the internal provider flag1 for the program.
798         *
799         * @param flag The first internal provider flag for the program.
800         * @return This Builder object to allow for chaining of calls to builder methods.
801         * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_INTERNAL_PROVIDER_FLAG1
802         */
803        public T setInternalProviderFlag1(long flag) {
804            mValues.put(ProgramColumns.COLUMN_INTERNAL_PROVIDER_FLAG1, flag);
805            return (T) this;
806        }
807
808        /**
809         * Sets the internal provider flag2 for the program.
810         *
811         * @param flag The second internal provider flag for the program.
812         * @return This Builder object to allow for chaining of calls to builder methods.
813         * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_INTERNAL_PROVIDER_FLAG2
814         */
815        public T setInternalProviderFlag2(long flag) {
816            mValues.put(ProgramColumns.COLUMN_INTERNAL_PROVIDER_FLAG2, flag);
817            return (T) this;
818        }
819
820        /**
821         * Sets the internal provider flag3 for the program.
822         *
823         * @param flag The third internal provider flag for the program.
824         * @return This Builder object to allow for chaining of calls to builder methods.
825         * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_INTERNAL_PROVIDER_FLAG3
826         */
827        public T setInternalProviderFlag3(long flag) {
828            mValues.put(ProgramColumns.COLUMN_INTERNAL_PROVIDER_FLAG3, flag);
829            return (T) this;
830        }
831
832        /**
833         * Sets the internal provider flag4 for the program.
834         *
835         * @param flag The forth internal provider flag for the program.
836         * @return This Builder object to allow for chaining of calls to builder methods.
837         * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_INTERNAL_PROVIDER_FLAG4
838         */
839        public T setInternalProviderFlag4(long flag) {
840            mValues.put(ProgramColumns.COLUMN_INTERNAL_PROVIDER_FLAG4, flag);
841            return (T) this;
842        }
843
844        /**
845         * Sets the review rating score style used for {@link #setReviewRating}.
846         *
847         * @param reviewRatingStyle The reviewing rating style for the program.
848         * @return This Builder object to allow for chaining of calls to builder methods.
849         *
850         * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_REVIEW_RATING_STYLE
851         * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#REVIEW_RATING_STYLE_STARS
852         * @see androidx.tvprovider.media.tv.TvContractCompat
853         * .Programs#REVIEW_RATING_STYLE_THUMBS_UP_DOWN
854         * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#REVIEW_RATING_STYLE_PERCENTAGE
855         */
856        public T setReviewRatingStyle(@ReviewRatingStyle int reviewRatingStyle) {
857            mValues.put(ProgramColumns.COLUMN_REVIEW_RATING_STYLE, reviewRatingStyle);
858            return (T) this;
859        }
860
861        /**
862         * Sets the review rating score for this program.
863         *
864         * <p>The format of the value is dependent on the review rating style. If the style is
865         * based on "stars", the value should be a real number between 0.0 and 5.0. (e.g. "4.5")
866         * If the style is based on "thumbs up/down", the value should be two integers, one for
867         * thumbs-up count and the other for thumbs-down count, with a comma between them.
868         * (e.g. "200,40") If the style is base on "percentage", the value should be a
869         * real number between 0 and 100. (e.g. "99.9")
870         *
871         * @param reviewRating The value of the review rating for the program.
872         * @return This Builder object to allow for chaining of calls to builder methods.
873         *
874         * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_REVIEW_RATING
875         * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_REVIEW_RATING_STYLE
876         * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#REVIEW_RATING_STYLE_STARS
877         * @see androidx.tvprovider.media.tv.TvContractCompat
878         * .Programs#REVIEW_RATING_STYLE_THUMBS_UP_DOWN
879         * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#REVIEW_RATING_STYLE_PERCENTAGE
880         */
881        public T setReviewRating(String reviewRating) {
882            mValues.put(ProgramColumns.COLUMN_REVIEW_RATING, reviewRating);
883            return (T) this;
884        }
885
886        /**
887         * Sets a custom name for the season, if applicable.
888         *
889         * @param seasonTitle The season title for the program.
890         * @return This Builder object to allow for chaining of calls to builder methods.
891         * @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_SEASON_TITLE
892         */
893        public T setSeasonTitle(String seasonTitle) {
894            mValues.put(ProgramColumns.COLUMN_SEASON_TITLE, seasonTitle);
895            return (T) this;
896        }
897    }
898}
899