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_GROUP;
19
20import android.content.ContentValues;
21import android.content.Intent;
22import android.database.Cursor;
23import android.net.Uri;
24import android.os.Build;
25
26import androidx.annotation.RestrictTo;
27import androidx.tvprovider.media.tv.TvContractCompat.Channels;
28import androidx.tvprovider.media.tv.TvContractCompat.Channels.ServiceType;
29import androidx.tvprovider.media.tv.TvContractCompat.Channels.Type;
30import androidx.tvprovider.media.tv.TvContractCompat.Channels.VideoFormat;
31
32import java.net.URISyntaxException;
33import java.nio.charset.Charset;
34
35/**
36 * A convenience class to access {@link TvContractCompat.Channels} entries in the system content
37 * provider.
38 *
39 * <p>This class makes it easy to insert or retrieve a channel from the system content provider,
40 * which is defined in {@link TvContractCompat}.
41 *
42 * <p>Usage example when inserting a channel:
43 * <pre>
44 * Channel channel = new Channel.Builder()
45 *         .setDisplayName("Channel Name")
46 *         .setDescription("Channel description")
47 *         .setType(Channels.TYPE_PREVIEW)
48 *         // Set more attributes...
49 *         .build();
50 * Uri channelUri = getContentResolver().insert(Channels.CONTENT_URI, channel.toContentValues());
51 * </pre>
52 *
53 * <p>Usage example when retrieving a channel:
54 * <pre>
55 * Channel channel;
56 * try (Cursor cursor = resolver.query(channelUri, null, null, null, null)) {
57 *     if (cursor != null && cursor.getCount() != 0) {
58 *         cursor.moveToNext();
59 *         channel = Channel.fromCursor(cursor);
60 *     }
61 * }
62 * </pre>
63 *
64 * <p>Usage example when updating an existing channel:
65 * <pre>
66 * Channel updatedChannel = new Channel.Builder(channel)
67 *         .setDescription("New channel description")
68 *         .build();
69 * getContentResolver().update(TvContractCompat.buildChannelUri(updatedChannel.getId()),
70 *         updatedChannel.toContentValues(), null, null);
71 * </pre>
72 *
73 * <p>Usage example when deleting a channel:
74 * <pre>
75 * getContentResolver().delete(
76 *         TvContractCompat.buildChannelUri(existingChannel.getId()), null, null);
77 * </pre>
78 */
79public final class Channel {
80    /**
81     * @hide
82     */
83    @RestrictTo(LIBRARY_GROUP)
84    public static final String[] PROJECTION = getProjection();
85
86    private static final long INVALID_CHANNEL_ID = -1;
87    private static final int INVALID_INT_VALUE = -1;
88    private static final int IS_SEARCHABLE = 1;
89    private static final int IS_TRANSIENT = 1;
90    private static final int IS_BROWSABLE = 1;
91    private static final int IS_SYSTEM_APPROVED = 1;
92    private static final int IS_LOCKED = 1;
93
94    private ContentValues mValues;
95
96    private Channel(Builder builder) {
97        mValues = builder.mValues;
98    }
99
100    /**
101     * @return The value of {@link Channels#_ID} for the channel.
102     */
103    public long getId() {
104        Long l = mValues.getAsLong(Channels._ID);
105        return l == null ? INVALID_CHANNEL_ID : l;
106    }
107
108    /**
109     * @return The value of {@link Channels#COLUMN_PACKAGE_NAME} for the channel.
110     */
111    public String getPackageName() {
112        return mValues.getAsString(Channels.COLUMN_PACKAGE_NAME);
113    }
114
115    /**
116     * @return The value of {@link Channels#COLUMN_INPUT_ID} for the channel.
117     */
118    public String getInputId() {
119        return mValues.getAsString(Channels.COLUMN_INPUT_ID);
120    }
121
122    /**
123     * @return The value of {@link Channels#COLUMN_TYPE} for the channel.
124     */
125    public @Type String getType() {
126        return mValues.getAsString(Channels.COLUMN_TYPE);
127    }
128
129    /**
130     * @return The value of {@link Channels#COLUMN_DISPLAY_NUMBER} for the channel.
131     */
132    public String getDisplayNumber() {
133        return mValues.getAsString(Channels.COLUMN_DISPLAY_NUMBER);
134    }
135
136    /**
137     * @return The value of {@link Channels#COLUMN_DISPLAY_NAME} for the channel.
138     */
139    public String getDisplayName() {
140        return mValues.getAsString(Channels.COLUMN_DISPLAY_NAME);
141    }
142
143    /**
144     * @return The value of {@link Channels#COLUMN_DESCRIPTION} for the channel.
145     */
146    public String getDescription() {
147        return mValues.getAsString(Channels.COLUMN_DESCRIPTION);
148    }
149
150    /**
151     * @return The value of {@link Channels#COLUMN_VIDEO_FORMAT} for the channel.
152     */
153    public @VideoFormat String getVideoFormat() {
154        return mValues.getAsString(Channels.COLUMN_VIDEO_FORMAT);
155    }
156
157    /**
158     * @return The value of {@link Channels#COLUMN_ORIGINAL_NETWORK_ID} for the channel.
159     */
160    public int getOriginalNetworkId() {
161        Integer i = mValues.getAsInteger(Channels.COLUMN_ORIGINAL_NETWORK_ID);
162        return i == null ? INVALID_INT_VALUE : i;
163    }
164
165    /**
166     * @return The value of {@link Channels#COLUMN_TRANSPORT_STREAM_ID} for the channel.
167     */
168    public int getTransportStreamId() {
169        Integer i = mValues.getAsInteger(Channels.COLUMN_TRANSPORT_STREAM_ID);
170        return i == null ? INVALID_INT_VALUE : i;
171    }
172
173    /**
174     * @return The value of {@link Channels#COLUMN_SERVICE_ID} for the channel.
175     */
176    public int getServiceId() {
177        Integer i = mValues.getAsInteger(Channels.COLUMN_SERVICE_ID);
178        return i == null ? INVALID_INT_VALUE : i;
179    }
180
181    /**
182     * @return The value of {@link Channels#COLUMN_APP_LINK_TEXT} for the channel.
183     */
184    public String getAppLinkText() {
185        return mValues.getAsString(Channels.COLUMN_APP_LINK_TEXT);
186    }
187
188    /**
189     * @return The value of {@link Channels#COLUMN_APP_LINK_COLOR} for the channel.
190     */
191    public int getAppLinkColor() {
192        Integer i = mValues.getAsInteger(Channels.COLUMN_APP_LINK_COLOR);
193        return i == null ? INVALID_INT_VALUE : i;
194    }
195
196    /**
197     * @return The value of {@link Channels#COLUMN_APP_LINK_ICON_URI} for the channel.
198     */
199    public Uri getAppLinkIconUri() {
200        String uri = mValues.getAsString(Channels.COLUMN_APP_LINK_ICON_URI);
201        return uri == null ? null : Uri.parse(uri);
202    }
203
204    /**
205     * @return The value of {@link Channels#COLUMN_APP_LINK_POSTER_ART_URI} for the channel.
206     */
207    public Uri getAppLinkPosterArtUri() {
208        String uri = mValues.getAsString(Channels.COLUMN_APP_LINK_POSTER_ART_URI);
209        return uri == null ? null : Uri.parse(uri);
210    }
211
212    /**
213     * @return The value of {@link Channels#COLUMN_APP_LINK_INTENT_URI} for the channel.
214     */
215    public Uri getAppLinkIntentUri() {
216        String uri = mValues.getAsString(Channels.COLUMN_APP_LINK_INTENT_URI);
217        return uri == null ? null : Uri.parse(uri);
218    }
219
220    /**
221     * @return The value of {@link Channels#COLUMN_APP_LINK_INTENT_URI} for the program.
222     */
223    public Intent getAppLinkIntent() throws URISyntaxException {
224        String uri = mValues.getAsString(Channels.COLUMN_APP_LINK_INTENT_URI);
225        return uri == null ? null : Intent.parseUri(uri.toString(), Intent.URI_INTENT_SCHEME);
226    }
227
228    /**
229     * @return The value of {@link Channels#COLUMN_NETWORK_AFFILIATION} for the channel.
230     */
231    public String getNetworkAffiliation() {
232        return mValues.getAsString(Channels.COLUMN_NETWORK_AFFILIATION);
233    }
234
235    /**
236     * @return The value of {@link Channels#COLUMN_SEARCHABLE} for the channel.
237     */
238    public boolean isSearchable() {
239        Integer i = mValues.getAsInteger(Channels.COLUMN_SEARCHABLE);
240        return i == null || i == IS_SEARCHABLE;
241    }
242
243    /**
244     * @return The value of {@link Channels#COLUMN_INTERNAL_PROVIDER_DATA} for the channel.
245     */
246    public byte[] getInternalProviderDataByteArray() {
247        return mValues.getAsByteArray(Channels.COLUMN_INTERNAL_PROVIDER_DATA);
248    }
249
250    /**
251     * @return The value of {@link Channels#COLUMN_SERVICE_TYPE} for the channel.
252     *
253     * <p>Returns {@link Channels#SERVICE_TYPE_AUDIO}, {@link Channels#SERVICE_TYPE_AUDIO_VIDEO}, or
254     * {@link Channels#SERVICE_TYPE_OTHER}.
255     */
256    public @ServiceType String getServiceType() {
257        return mValues.getAsString(Channels.COLUMN_SERVICE_TYPE);
258    }
259
260    /**
261     * @return The value of {@link Channels#COLUMN_INTERNAL_PROVIDER_FLAG1} for the channel.
262     */
263    public Long getInternalProviderFlag1() {
264        return mValues.getAsLong(Channels.COLUMN_INTERNAL_PROVIDER_FLAG1);
265    }
266
267    /**
268     * @return The value of {@link Channels#COLUMN_INTERNAL_PROVIDER_FLAG2} for the channel.
269     */
270    public Long getInternalProviderFlag2() {
271        return mValues.getAsLong(Channels.COLUMN_INTERNAL_PROVIDER_FLAG2);
272    }
273
274    /**
275     * @return The value of {@link Channels#COLUMN_INTERNAL_PROVIDER_FLAG3} for the channel.
276     */
277    public Long getInternalProviderFlag3() {
278        return mValues.getAsLong(Channels.COLUMN_INTERNAL_PROVIDER_FLAG3);
279    }
280
281    /**
282     * @return The value of {@link Channels#COLUMN_INTERNAL_PROVIDER_FLAG4} for the channel.
283     */
284    public Long getInternalProviderFlag4() {
285        return mValues.getAsLong(Channels.COLUMN_INTERNAL_PROVIDER_FLAG4);
286    }
287
288    /**
289     * @return The value of {@link Channels#COLUMN_INTERNAL_PROVIDER_ID} for the channel.
290     */
291    public String getInternalProviderId() {
292        return mValues.getAsString(Channels.COLUMN_INTERNAL_PROVIDER_ID);
293    }
294
295    /**
296     * @return The value of {@link Channels#COLUMN_TRANSIENT} for the channel.
297     */
298    public boolean isTransient() {
299        Integer i = mValues.getAsInteger(Channels.COLUMN_TRANSIENT);
300        return i != null && i == IS_TRANSIENT;
301    }
302
303    /**
304     * @return The value of {@link Channels#COLUMN_BROWSABLE} for the channel.
305     */
306    public boolean isBrowsable() {
307        Integer i = mValues.getAsInteger(Channels.COLUMN_BROWSABLE);
308        return i != null && i == IS_BROWSABLE;
309    }
310
311    /**
312     * @return The value of {@link Channels#COLUMN_SYSTEM_APPROVED} for the channel.
313     * @hide
314     */
315    @RestrictTo(LIBRARY_GROUP)
316    public boolean isSystemApproved() {
317        Integer i = mValues.getAsInteger(Channels.COLUMN_SYSTEM_APPROVED);
318        return i != null && i == IS_SYSTEM_APPROVED;
319    }
320
321    /**
322     * @return The value of {@link Channels#COLUMN_CONFIGURATION_DISPLAY_ORDER} for the channel.
323     */
324    public int getConfigurationDisplayOrder() {
325        return mValues.getAsInteger(Channels.COLUMN_CONFIGURATION_DISPLAY_ORDER);
326    }
327
328    /**
329     * @return The value of {@link Channels#COLUMN_SYSTEM_CHANNEL_KEY} for the channel.
330     */
331    public String getSystemChannelKey() {
332        return mValues.getAsString(Channels.COLUMN_SYSTEM_CHANNEL_KEY);
333    }
334
335    /**
336     * @return The value of {@link Channels#COLUMN_LOCKED} for the channel.
337     */
338    public boolean isLocked() {
339        Integer i = mValues.getAsInteger(Channels.COLUMN_LOCKED);
340        return i != null && i == IS_LOCKED;
341    }
342
343    @Override
344    public int hashCode() {
345        return mValues.hashCode();
346    }
347
348    @Override
349    public boolean equals(Object other) {
350        if (!(other instanceof Channel)) {
351            return false;
352        }
353        return mValues.equals(((Channel) other).mValues);
354    }
355    @Override
356    public String toString() {
357        return "Channel{" + mValues.toString() + "}";
358    }
359
360    /**
361     * @return The fields of the Channel in the ContentValues format to be easily inserted into the
362     * TV Input Framework database.
363     */
364    public ContentValues toContentValues() {
365        return toContentValues(false);
366    }
367
368    /**
369     * Returns fields of the Channel in the ContentValues format to be easily inserted into the
370     * TV Input Framework database.
371     *
372     * @param includeProtectedFields Whether the fields protected by system is included or not.
373     * @hide
374     */
375    @RestrictTo(LIBRARY_GROUP)
376    public ContentValues toContentValues(boolean includeProtectedFields) {
377        ContentValues values = new ContentValues(mValues);
378        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
379            values.remove(Channels.COLUMN_APP_LINK_COLOR);
380            values.remove(Channels.COLUMN_APP_LINK_TEXT);
381            values.remove(Channels.COLUMN_APP_LINK_ICON_URI);
382            values.remove(Channels.COLUMN_APP_LINK_POSTER_ART_URI);
383            values.remove(Channels.COLUMN_APP_LINK_INTENT_URI);
384            values.remove(Channels.COLUMN_INTERNAL_PROVIDER_FLAG1);
385            values.remove(Channels.COLUMN_INTERNAL_PROVIDER_FLAG2);
386            values.remove(Channels.COLUMN_INTERNAL_PROVIDER_FLAG3);
387            values.remove(Channels.COLUMN_INTERNAL_PROVIDER_FLAG4);
388        }
389        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
390            values.remove(Channels.COLUMN_INTERNAL_PROVIDER_ID);
391            values.remove(Channels.COLUMN_TRANSIENT);
392            values.remove(Channels.COLUMN_CONFIGURATION_DISPLAY_ORDER);
393            values.remove(Channels.COLUMN_SYSTEM_CHANNEL_KEY);
394        }
395
396        if (!includeProtectedFields) {
397            values.remove(Channels.COLUMN_BROWSABLE);
398            values.remove(Channels.COLUMN_LOCKED);
399        }
400        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O || !includeProtectedFields) {
401            values.remove(Channels.COLUMN_SYSTEM_APPROVED);
402        }
403        return values;
404    }
405
406    /**
407     * Creates a Channel object from a cursor including the fields defined in {@link Channels}.
408     *
409     * @param cursor A row from the TV Input Framework database.
410     * @return A channel with the values taken from the cursor.
411     */
412    public static Channel fromCursor(Cursor cursor) {
413        // TODO: Add additional API which does not use costly getColumnIndex().
414        Builder builder = new Builder();
415        int index;
416        if ((index = cursor.getColumnIndex(Channels._ID)) >= 0 && !cursor.isNull(index)) {
417            builder.setId(cursor.getLong(index));
418        }
419        if ((index = cursor.getColumnIndex(Channels.COLUMN_DESCRIPTION)) >= 0
420                && !cursor.isNull(index)) {
421            builder.setDescription(cursor.getString(index));
422        }
423        if ((index = cursor.getColumnIndex(Channels.COLUMN_DISPLAY_NAME)) >= 0
424                && !cursor.isNull(index)) {
425            builder.setDisplayName(cursor.getString(index));
426        }
427        if ((index = cursor.getColumnIndex(Channels.COLUMN_DISPLAY_NUMBER)) >= 0
428                && !cursor.isNull(index)) {
429            builder.setDisplayNumber(cursor.getString(index));
430        }
431        if ((index = cursor.getColumnIndex(Channels.COLUMN_INPUT_ID)) >= 0
432                && !cursor.isNull(index)) {
433            builder.setInputId(cursor.getString(index));
434        }
435        if ((index = cursor.getColumnIndex(Channels.COLUMN_INTERNAL_PROVIDER_DATA)) >= 0
436                && !cursor.isNull(index)) {
437            builder.setInternalProviderData(cursor.getBlob(index));
438        }
439        if ((index = cursor.getColumnIndex(Channels.COLUMN_NETWORK_AFFILIATION)) >= 0
440                && !cursor.isNull(index)) {
441            builder.setNetworkAffiliation(cursor.getString(index));
442        }
443        if ((index = cursor.getColumnIndex(Channels.COLUMN_ORIGINAL_NETWORK_ID)) >= 0
444                && !cursor.isNull(index)) {
445            builder.setOriginalNetworkId(cursor.getInt(index));
446        }
447        if ((index = cursor.getColumnIndex(Channels.COLUMN_PACKAGE_NAME)) >= 0
448                && !cursor.isNull(index)) {
449            builder.setPackageName(cursor.getString(index));
450        }
451        if ((index = cursor.getColumnIndex(Channels.COLUMN_SEARCHABLE)) >= 0
452                && !cursor.isNull(index)) {
453            builder.setSearchable(cursor.getInt(index) == IS_SEARCHABLE);
454        }
455        if ((index = cursor.getColumnIndex(Channels.COLUMN_SERVICE_ID)) >= 0
456                && !cursor.isNull(index)) {
457            builder.setServiceId(cursor.getInt(index));
458        }
459        if ((index = cursor.getColumnIndex(Channels.COLUMN_SERVICE_TYPE)) >= 0
460                && !cursor.isNull(index)) {
461            builder.setServiceType(cursor.getString(index));
462        }
463        if ((index = cursor.getColumnIndex(Channels.COLUMN_TRANSPORT_STREAM_ID)) >= 0
464                && !cursor.isNull(index)) {
465            builder.setTransportStreamId(cursor.getInt(index));
466        }
467        if ((index = cursor.getColumnIndex(Channels.COLUMN_TYPE)) >= 0 && !cursor.isNull(index)) {
468            builder.setType(cursor.getString(index));
469        }
470        if ((index = cursor.getColumnIndex(Channels.COLUMN_VIDEO_FORMAT)) >= 0
471                && !cursor.isNull(index)) {
472            builder.setVideoFormat(cursor.getString(index));
473        }
474        if ((index = cursor.getColumnIndex(Channels.COLUMN_BROWSABLE)) >= 0
475                && !cursor.isNull(index)) {
476            builder.setBrowsable(cursor.getInt(index) == IS_BROWSABLE);
477        }
478        if ((index = cursor.getColumnIndex(Channels.COLUMN_LOCKED)) >= 0
479                && !cursor.isNull(index)) {
480            builder.setLocked(cursor.getInt(index) == IS_LOCKED);
481        }
482        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
483            if ((index = cursor.getColumnIndex(Channels.COLUMN_APP_LINK_COLOR)) >= 0
484                    && !cursor.isNull(index)) {
485                builder.setAppLinkColor(cursor.getInt(index));
486            }
487            if ((index = cursor.getColumnIndex(Channels.COLUMN_APP_LINK_ICON_URI)) >= 0
488                    && !cursor.isNull(index)) {
489                builder.setAppLinkIconUri(Uri.parse(cursor.getString(index)));
490            }
491            if ((index = cursor.getColumnIndex(Channels.COLUMN_APP_LINK_INTENT_URI)) >= 0
492                    && !cursor.isNull(index)) {
493                builder.setAppLinkIntentUri(Uri.parse(cursor.getString(index)));
494            }
495            if ((index = cursor.getColumnIndex(Channels.COLUMN_APP_LINK_POSTER_ART_URI)) >= 0
496                    && !cursor.isNull(index)) {
497                builder.setAppLinkPosterArtUri(Uri.parse(cursor.getString(index)));
498            }
499            if ((index = cursor.getColumnIndex(Channels.COLUMN_APP_LINK_TEXT)) >= 0
500                    && !cursor.isNull(index)) {
501                builder.setAppLinkText(cursor.getString(index));
502            }
503            if ((index = cursor.getColumnIndex(Channels.COLUMN_INTERNAL_PROVIDER_FLAG1)) >= 0
504                    && !cursor.isNull(index)) {
505                builder.setInternalProviderFlag1(cursor.getLong(index));
506            }
507            if ((index = cursor.getColumnIndex(Channels.COLUMN_INTERNAL_PROVIDER_FLAG2)) >= 0
508                    && !cursor.isNull(index)) {
509                builder.setInternalProviderFlag2(cursor.getLong(index));
510            }
511            if ((index = cursor.getColumnIndex(Channels.COLUMN_INTERNAL_PROVIDER_FLAG3)) >= 0
512                    && !cursor.isNull(index)) {
513                builder.setInternalProviderFlag3(cursor.getLong(index));
514            }
515            if ((index = cursor.getColumnIndex(Channels.COLUMN_INTERNAL_PROVIDER_FLAG4)) >= 0
516                    && !cursor.isNull(index)) {
517                builder.setInternalProviderFlag4(cursor.getLong(index));
518            }
519        }
520        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
521            if ((index = cursor.getColumnIndex(Channels.COLUMN_INTERNAL_PROVIDER_ID)) >= 0
522                    && !cursor.isNull(index)) {
523                builder.setInternalProviderId(cursor.getString(index));
524            }
525            if ((index = cursor.getColumnIndex(Channels.COLUMN_TRANSIENT)) >= 0
526                    && !cursor.isNull(index)) {
527                builder.setTransient(cursor.getInt(index) == IS_TRANSIENT);
528            }
529            if ((index = cursor.getColumnIndex(Channels.COLUMN_SYSTEM_APPROVED)) >= 0
530                    && !cursor.isNull(index)) {
531                builder.setSystemApproved(cursor.getInt(index) == IS_SYSTEM_APPROVED);
532            }
533            if ((index = cursor.getColumnIndex(Channels.COLUMN_CONFIGURATION_DISPLAY_ORDER)) >= 0
534                    && !cursor.isNull(index)) {
535                builder.setConfigurationDisplayOrder(cursor.getInt(index));
536            }
537            if ((index = cursor.getColumnIndex(Channels.COLUMN_SYSTEM_CHANNEL_KEY)) >= 0
538                    && !cursor.isNull(index)) {
539                builder.setSystemChannelKey(cursor.getString(index));
540            }
541        }
542        return builder.build();
543    }
544
545    private static String[] getProjection() {
546        String[] baseColumns = new String[] {
547                Channels._ID,
548                Channels.COLUMN_DESCRIPTION,
549                Channels.COLUMN_DISPLAY_NAME,
550                Channels.COLUMN_DISPLAY_NUMBER,
551                Channels.COLUMN_INPUT_ID,
552                Channels.COLUMN_INTERNAL_PROVIDER_DATA,
553                Channels.COLUMN_NETWORK_AFFILIATION,
554                Channels.COLUMN_ORIGINAL_NETWORK_ID,
555                Channels.COLUMN_PACKAGE_NAME,
556                Channels.COLUMN_SEARCHABLE,
557                Channels.COLUMN_SERVICE_ID,
558                Channels.COLUMN_SERVICE_TYPE,
559                Channels.COLUMN_TRANSPORT_STREAM_ID,
560                Channels.COLUMN_TYPE,
561                Channels.COLUMN_VIDEO_FORMAT,
562                Channels.COLUMN_BROWSABLE,
563                Channels.COLUMN_LOCKED,
564        };
565        String[] marshmallowColumns = new String[] {
566                Channels.COLUMN_APP_LINK_COLOR,
567                Channels.COLUMN_APP_LINK_ICON_URI,
568                Channels.COLUMN_APP_LINK_INTENT_URI,
569                Channels.COLUMN_APP_LINK_POSTER_ART_URI,
570                Channels.COLUMN_APP_LINK_TEXT,
571                Channels.COLUMN_INTERNAL_PROVIDER_FLAG1,
572                Channels.COLUMN_INTERNAL_PROVIDER_FLAG2,
573                Channels.COLUMN_INTERNAL_PROVIDER_FLAG3,
574                Channels.COLUMN_INTERNAL_PROVIDER_FLAG4,
575        };
576        String[] oReleaseColumns = new String[] {
577                Channels.COLUMN_INTERNAL_PROVIDER_ID,
578                Channels.COLUMN_TRANSIENT,
579                Channels.COLUMN_SYSTEM_APPROVED,
580                Channels.COLUMN_CONFIGURATION_DISPLAY_ORDER,
581                Channels.COLUMN_SYSTEM_CHANNEL_KEY
582        };
583        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
584            return CollectionUtils.concatAll(baseColumns, marshmallowColumns, oReleaseColumns);
585        }
586        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
587            return CollectionUtils.concatAll(baseColumns, marshmallowColumns);
588        }
589        return baseColumns;
590    }
591
592    /**
593     * The builder class that makes it easy to chain setters to create a {@link Channel} object.
594     */
595    public static final class Builder {
596        private ContentValues mValues;
597
598        public Builder() {
599            mValues = new ContentValues();
600        }
601
602        public Builder(Channel other) {
603            mValues = new ContentValues(other.mValues);
604        }
605
606        /**
607         * Sets the ID of the Channel.
608         *
609         * @param id The value of {@link Channels#_ID} for the channel.
610         * @return This Builder object to allow for chaining of calls to builder methods.
611         */
612        private Builder setId(long id) {
613            mValues.put(Channels._ID, id);
614            return this;
615        }
616
617        /**
618         * Sets the package name of the Channel.
619         *
620         * @param packageName The value of {@link Channels#COLUMN_PACKAGE_NAME} for the channel.
621         * @return This Builder object to allow for chaining of calls to builder methods.
622         * @hide
623         */
624        @RestrictTo(LIBRARY_GROUP)
625        Builder setPackageName(String packageName) {
626            mValues.put(Channels.COLUMN_PACKAGE_NAME, packageName);
627            return this;
628        }
629
630        /**
631         * Sets the input id of the Channel.
632         *
633         * @param inputId The value of {@link Channels#COLUMN_INPUT_ID} for the channel.
634         * @return This Builder object to allow for chaining of calls to builder methods.
635         */
636        public Builder setInputId(String inputId) {
637            mValues.put(Channels.COLUMN_INPUT_ID, inputId);
638            return this;
639        }
640
641        /**
642         * Sets the broadcast standard of the Channel.
643         *
644         * @param type The value of {@link Channels#COLUMN_TYPE} for the channel.
645         * @return This Builder object to allow for chaining of calls to builder methods.
646         */
647        public Builder setType(@Type String type) {
648            mValues.put(Channels.COLUMN_TYPE, type);
649            return this;
650        }
651
652        /**
653         * Sets the display number of the Channel.
654         *
655         * @param displayNumber The value of {@link Channels#COLUMN_DISPLAY_NUMBER} for the channel.
656         * @return This Builder object to allow for chaining of calls to builder methods.
657         */
658        public Builder setDisplayNumber(String displayNumber) {
659            mValues.put(Channels.COLUMN_DISPLAY_NUMBER, displayNumber);
660            return this;
661        }
662
663        /**
664         * Sets the name to be displayed for the Channel.
665         *
666         * @param displayName The value of {@link Channels#COLUMN_DISPLAY_NAME} for the channel.
667         * @return This Builder object to allow for chaining of calls to builder methods.
668         */
669        public Builder setDisplayName(String displayName) {
670            mValues.put(Channels.COLUMN_DISPLAY_NAME, displayName);
671            return this;
672        }
673
674        /**
675         * Sets the description of the Channel.
676         *
677         * @param description The value of {@link Channels#COLUMN_DESCRIPTION} for the channel.
678         * @return This Builder object to allow for chaining of calls to builder methods.
679         */
680        public Builder setDescription(String description) {
681            mValues.put(Channels.COLUMN_DESCRIPTION, description);
682            return this;
683        }
684
685        /**
686         * Sets the video format of the Channel.
687         *
688         * @param videoFormat The value of {@link Channels#COLUMN_VIDEO_FORMAT} for the channel.
689         * @return This Builder object to allow for chaining of calls to builder methods.
690         */
691        public Builder setVideoFormat(@VideoFormat String videoFormat) {
692            mValues.put(Channels.COLUMN_VIDEO_FORMAT, videoFormat);
693            return this;
694        }
695
696        /**
697         * Sets the original network id of the Channel.
698         *
699         * @param originalNetworkId The value of {@link Channels#COLUMN_ORIGINAL_NETWORK_ID} for the
700         *                          channel.
701         * @return This Builder object to allow for chaining of calls to builder methods.
702         */
703        public Builder setOriginalNetworkId(int originalNetworkId) {
704            mValues.put(Channels.COLUMN_ORIGINAL_NETWORK_ID, originalNetworkId);
705            return this;
706        }
707
708        /**
709         * Sets the transport stream id of the Channel.
710         *
711         * @param transportStreamId The value of {@link Channels#COLUMN_TRANSPORT_STREAM_ID} for the
712         *                          channel.
713         * @return This Builder object to allow for chaining of calls to builder methods.
714         */
715        public Builder setTransportStreamId(int transportStreamId) {
716            mValues.put(Channels.COLUMN_TRANSPORT_STREAM_ID, transportStreamId);
717            return this;
718        }
719
720        /**
721         * Sets the service id of the Channel.
722         *
723         * @param serviceId The value of {@link Channels#COLUMN_SERVICE_ID} for the channel.
724         * @return This Builder object to allow for chaining of calls to builder methods.
725         */
726        public Builder setServiceId(int serviceId) {
727            mValues.put(Channels.COLUMN_SERVICE_ID, serviceId);
728            return this;
729        }
730
731        /**
732         * Sets the internal provider data of the channel.
733         *
734         * @param internalProviderData The value of {@link Channels#COLUMN_INTERNAL_PROVIDER_DATA}
735         *                             for the channel.
736         * @return This Builder object to allow for chaining of calls to builder methods.
737         */
738        public Builder setInternalProviderData(byte[] internalProviderData) {
739            mValues.put(Channels.COLUMN_INTERNAL_PROVIDER_DATA, internalProviderData);
740            return this;
741        }
742
743        /**
744         * Sets the internal provider data of the channel.
745         *
746         * @param internalProviderData The value of {@link Channels#COLUMN_INTERNAL_PROVIDER_DATA}
747         *                             for the channel.
748         * @return This Builder object to allow for chaining of calls to builder methods.
749         */
750        public Builder setInternalProviderData(String internalProviderData) {
751            mValues.put(Channels.COLUMN_INTERNAL_PROVIDER_DATA,
752                    internalProviderData.getBytes(Charset.defaultCharset()));
753            return this;
754        }
755
756        /**
757         * Sets the text to be displayed in the App Linking card.
758         *
759         * @param appLinkText The value of {@link Channels#COLUMN_APP_LINK_TEXT} for the channel.
760         * @return This Builder object to allow for chaining of calls to builder methods.
761         */
762        public Builder setAppLinkText(String appLinkText) {
763            mValues.put(Channels.COLUMN_APP_LINK_TEXT, appLinkText);
764            return this;
765        }
766
767        /**
768         * Sets the background color of the App Linking card.
769         *
770         * @param appLinkColor The value of {@link Channels#COLUMN_APP_LINK_COLOR} for the channel.
771         * @return This Builder object to allow for chaining of calls to builder methods.
772         */
773        public Builder setAppLinkColor(int appLinkColor) {
774            mValues.put(Channels.COLUMN_APP_LINK_COLOR, appLinkColor);
775            return this;
776        }
777
778        /**
779         * Sets the icon to be displayed next to the text of the App Linking card.
780         *
781         * @param appLinkIconUri The value of {@link Channels#COLUMN_APP_LINK_ICON_URI} for the
782         *                       channel.
783         * @return This Builder object to allow for chaining of calls to builder methods.
784         */
785        public Builder setAppLinkIconUri(Uri appLinkIconUri) {
786            mValues.put(Channels.COLUMN_APP_LINK_ICON_URI,
787                    appLinkIconUri == null ? null : appLinkIconUri.toString());
788            return this;
789        }
790
791        /**
792         * Sets the background image of the App Linking card.
793         *
794         * @param appLinkPosterArtUri The value of {@link Channels#COLUMN_APP_LINK_POSTER_ART_URI}
795         *                            for the channel.
796         * @return This Builder object to allow for chaining of calls to builder methods.
797         */
798        public Builder setAppLinkPosterArtUri(Uri appLinkPosterArtUri) {
799            mValues.put(Channels.COLUMN_APP_LINK_POSTER_ART_URI,
800                    appLinkPosterArtUri == null ? null : appLinkPosterArtUri.toString());
801            return this;
802        }
803
804        /**
805         * Sets the App Linking Intent.
806         *
807         * @param appLinkIntent The Intent to be executed when the App Linking card is selected
808         * @return This Builder object to allow for chaining of calls to builder methods.
809         */
810        public Builder setAppLinkIntent(Intent appLinkIntent) {
811            return setAppLinkIntentUri(Uri.parse(appLinkIntent.toUri(Intent.URI_INTENT_SCHEME)));
812        }
813
814        /**
815         * Sets the App Linking Intent.
816         *
817         * @param appLinkIntentUri The Intent that should be executed when the App Linking card is
818         *                         selected. Use the method toUri(Intent.URI_INTENT_SCHEME) on your
819         *                         Intent to turn it into a String. See
820         *                         {@link Channels#COLUMN_APP_LINK_INTENT_URI}.
821         * @return This Builder object to allow for chaining of calls to builder methods.
822         */
823        public Builder setAppLinkIntentUri(Uri appLinkIntentUri) {
824            mValues.put(Channels.COLUMN_APP_LINK_INTENT_URI,
825                    appLinkIntentUri == null ? null : appLinkIntentUri.toString());
826            return this;
827        }
828
829        /**
830         * Sets the network name for the channel, which may be different from its display name.
831         *
832         * @param networkAffiliation The value of
833         * {@link Channels#COLUMN_NETWORK_AFFILIATION} for the channel.
834         * @return This Builder object to allow for chaining of calls to builder methods.
835         */
836        public Builder setNetworkAffiliation(String networkAffiliation) {
837            mValues.put(Channels.COLUMN_NETWORK_AFFILIATION, networkAffiliation);
838            return this;
839        }
840
841        /**
842         * Sets whether this channel can be searched for in other applications.
843         *
844         * @param searchable The value of {@link Channels#COLUMN_SEARCHABLE} for the channel.
845         * @return This Builder object to allow for chaining of calls to builder methods.
846         */
847        public Builder setSearchable(boolean searchable) {
848            mValues.put(Channels.COLUMN_SEARCHABLE, searchable ? IS_SEARCHABLE : 0);
849            return this;
850        }
851
852        /**
853         * Sets the type of content that will appear on this channel. This could refer to the
854         * underlying broadcast standard or refer to {@link Channels#SERVICE_TYPE_AUDIO},
855         * {@link Channels#SERVICE_TYPE_AUDIO_VIDEO}, or {@link Channels#SERVICE_TYPE_OTHER}.
856         *
857         * @param serviceType The value of {@link Channels#COLUMN_SERVICE_TYPE} for the channel.
858         * @return This Builder object to allow for chaining of calls to builder methods.
859         */
860        public Builder setServiceType(@ServiceType String serviceType) {
861            mValues.put(Channels.COLUMN_SERVICE_TYPE, serviceType);
862            return this;
863        }
864
865        /**
866         * Sets the internal provider flag1 for the channel.
867         *
868         * @param flag The value of {@link Channels#COLUMN_INTERNAL_PROVIDER_FLAG1} for the program.
869         * @return This Builder object to allow for chaining of calls to builder methods.
870         */
871        public Builder setInternalProviderFlag1(long flag) {
872            mValues.put(Channels.COLUMN_INTERNAL_PROVIDER_FLAG1, flag);
873            return this;
874        }
875
876        /**
877         * Sets the internal provider flag2 for the channel.
878         *
879         * @param flag The value of {@link Channels#COLUMN_INTERNAL_PROVIDER_FLAG2} for the program.
880         * @return This Builder object to allow for chaining of calls to builder methods.
881         */
882        public Builder setInternalProviderFlag2(long flag) {
883            mValues.put(Channels.COLUMN_INTERNAL_PROVIDER_FLAG2, flag);
884            return this;
885        }
886
887        /**
888         * Sets the internal provider flag3 for the channel.
889         *
890         * @param flag The value of {@link Channels#COLUMN_INTERNAL_PROVIDER_FLAG3} for the program.
891         * @return This Builder object to allow for chaining of calls to builder methods.
892         */
893        public Builder setInternalProviderFlag3(long flag) {
894            mValues.put(Channels.COLUMN_INTERNAL_PROVIDER_FLAG3, flag);
895            return this;
896        }
897
898        /**
899         * Sets the internal provider flag4 for the channel.
900         *
901         * @param flag The value of {@link Channels#COLUMN_INTERNAL_PROVIDER_FLAG4} for the program.
902         * @return This Builder object to allow for chaining of calls to builder methods.
903         */
904        public Builder setInternalProviderFlag4(long flag) {
905            mValues.put(Channels.COLUMN_INTERNAL_PROVIDER_FLAG4, flag);
906            return this;
907        }
908
909        /**
910         * Sets the internal provider ID for the channel.
911         *
912         * @param internalProviderId The value of {@link Channels#COLUMN_INTERNAL_PROVIDER_ID}
913         *                           for the program.
914         * @return This Builder object to allow for chaining of calls to builder methods.
915         */
916        public Builder setInternalProviderId(String internalProviderId) {
917            mValues.put(Channels.COLUMN_INTERNAL_PROVIDER_ID, internalProviderId);
918            return this;
919        }
920
921        /**
922         * Sets whether this channel is transient or not.
923         *
924         * @param value The value of {@link Channels#COLUMN_TRANSIENT} for the channel.
925         * @return This Builder object to allow for chaining of calls to builder methods.
926         */
927        public Builder setTransient(boolean value) {
928            mValues.put(Channels.COLUMN_TRANSIENT, value ? IS_TRANSIENT : 0);
929            return this;
930        }
931
932        /**
933         * Sets whether this channel is browsable or not.
934         *
935         * @param value The value of {@link Channels#COLUMN_BROWSABLE} for the channel.
936         * @return This Builder object to allow for chaining of calls to builder methods.
937         * @hide
938         */
939        @RestrictTo(LIBRARY_GROUP)
940        public Builder setBrowsable(boolean value) {
941            mValues.put(Channels.COLUMN_BROWSABLE, value ? IS_BROWSABLE : 0);
942            return this;
943        }
944
945        /**
946         * Sets whether system approved this channel or not.
947         *
948         * @param value The value of {@link Channels#COLUMN_SYSTEM_APPROVED} for the channel.
949         * @return This Builder object to allow for chaining of calls to builder methods.
950         * @hide
951         */
952        @RestrictTo(LIBRARY_GROUP)
953        public Builder setSystemApproved(boolean value) {
954            mValues.put(Channels.COLUMN_SYSTEM_APPROVED, value ? IS_SYSTEM_APPROVED : 0);
955            return this;
956        }
957
958        /**
959         * Sets the configuration display order for this channel. This value will be used to
960         * order channels within the configure channels menu.
961         *
962         * @param value The value of {@link Channels#COLUMN_CONFIGURATION_DISPLAY_ORDER} for the
963         *              channel
964         * @return This Builder object to allow for chaining of calls to builder methods.
965         */
966        public Builder setConfigurationDisplayOrder(int value) {
967            mValues.put(Channels.COLUMN_CONFIGURATION_DISPLAY_ORDER, value);
968            return this;
969        }
970
971        /**
972         * Sets the system channel key for this channel. This identifier helps OEM differentiate
973         * among the app's channels. This identifier should be unique per channel for each app, and
974         * should be agreed between the app and the OEM. It is up to the OEM on how they use this
975         * identifier for customization purposes.
976         *
977         * @param value The value of {@link Channels#COLUMN_SYSTEM_CHANNEL_KEY} for the channel.
978         * @return This Builder object to allow for chaining of calls to builder methods.
979         */
980        public Builder setSystemChannelKey(String value) {
981            mValues.put(Channels.COLUMN_SYSTEM_CHANNEL_KEY, value);
982            return this;
983        }
984
985        /**
986         * Sets whether this channel is locked or not.
987         *
988         * @param value The value of {@link Channels#COLUMN_LOCKED} for the channel.
989         * @return This Builder object to allow for chaining of calls to builder methods.
990         * @hide
991         */
992        @RestrictTo(LIBRARY_GROUP)
993        public Builder setLocked(boolean value) {
994            mValues.put(Channels.COLUMN_LOCKED, value ? IS_LOCKED : 0);
995            return this;
996        }
997
998        /**
999         * Takes the values of the Builder object and creates a Channel object.
1000         * @return Channel object with values from the Builder.
1001         */
1002        public Channel build() {
1003            return new Channel(this);
1004        }
1005    }
1006}
1007