Home | History | Annotate | Download | only in tv
      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  */
     16 package androidx.tvprovider.media.tv;
     17 
     18 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
     19 
     20 import android.content.ContentValues;
     21 import android.content.Intent;
     22 import android.database.Cursor;
     23 import android.net.Uri;
     24 import android.os.Build;
     25 
     26 import androidx.annotation.RestrictTo;
     27 import androidx.tvprovider.media.tv.TvContractCompat.Channels;
     28 import androidx.tvprovider.media.tv.TvContractCompat.Channels.ServiceType;
     29 import androidx.tvprovider.media.tv.TvContractCompat.Channels.Type;
     30 import androidx.tvprovider.media.tv.TvContractCompat.Channels.VideoFormat;
     31 
     32 import java.net.URISyntaxException;
     33 import 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  */
     79 public 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