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.database.Cursor;
     22 import android.os.Build;
     23 
     24 import androidx.annotation.NonNull;
     25 import androidx.annotation.RestrictTo;
     26 import androidx.tvprovider.media.tv.TvContractCompat.Programs;
     27 import androidx.tvprovider.media.tv.TvContractCompat.Programs.Genres.Genre;
     28 
     29 /**
     30  * A convenience class to access {@link TvContractCompat.Programs} entries in the system content
     31  * provider.
     32  *
     33  * <p>This class makes it easy to insert or retrieve a program from the system content provider,
     34  * which is defined in {@link TvContractCompat}.
     35  *
     36  * <p>Usage example when inserting a program:
     37  * <pre>
     38  * Program program = new Program.Builder()
     39  *         .setChannelId(channel.getId())
     40  *         .setTitle("Program Title")
     41  *         .setDescription("Program Description")
     42  *         .setPosterArtUri(Uri.parse("http://example.com/poster_art.png"))
     43  *         // Set more attributes...
     44  *         .build();
     45  * Uri programUri = getContentResolver().insert(Programs.CONTENT_URI, program.toContentValues());
     46  * </pre>
     47  *
     48  * <p>Usage example when retrieving a program:
     49  * <pre>
     50  * Program program;
     51  * try (Cursor cursor = resolver.query(programUri, null, null, null, null)) {
     52  *     if (cursor != null && cursor.getCount() != 0) {
     53  *         cursor.moveToNext();
     54  *         program = Program.fromCursor(cursor);
     55  *     }
     56  * }
     57  * </pre>
     58  *
     59  * <p>Usage example when updating an existing program:
     60  * <pre>
     61  * Program updatedProgram = new Program.Builder(program)
     62  *         .setEndTimeUtcMillis(newProgramEndTime)
     63  *         .build();
     64  * getContentResolver().update(TvContractCompat.buildProgramUri(updatedProgram.getId()),
     65  *         updatedProgram.toContentValues(), null, null);
     66  * </pre>
     67  *
     68  * <p>Usage example when deleting a program:
     69  * <pre>
     70  * getContentResolver().delete(TvContractCompat.buildProgramUri(existingProgram.getId()),
     71  *         null, null);
     72  * </pre>
     73  */
     74 public final class Program extends BaseProgram implements Comparable<Program> {
     75     /**
     76      * @hide
     77      */
     78     @RestrictTo(LIBRARY_GROUP)
     79     public static final String[] PROJECTION = getProjection();
     80 
     81     private static final long INVALID_LONG_VALUE = -1;
     82     private static final int IS_RECORDING_PROHIBITED = 1;
     83 
     84     private Program(Builder builder) {
     85         super(builder);
     86     }
     87 
     88     /**
     89      * @return The value of {@link Programs#COLUMN_CHANNEL_ID} for the program.
     90      */
     91     public long getChannelId() {
     92         Long l = mValues.getAsLong(Programs.COLUMN_CHANNEL_ID);
     93         return l == null ? INVALID_LONG_VALUE : l;
     94     }
     95 
     96     /**
     97      * @return The value of {@link Programs#COLUMN_START_TIME_UTC_MILLIS} for the program.
     98      */
     99     public long getStartTimeUtcMillis() {
    100         Long l = mValues.getAsLong(Programs.COLUMN_START_TIME_UTC_MILLIS);
    101         return l == null ? INVALID_LONG_VALUE : l;
    102     }
    103 
    104     /**
    105      * @return The value of {@link Programs#COLUMN_END_TIME_UTC_MILLIS} for the program.
    106      */
    107     public long getEndTimeUtcMillis() {
    108         Long l = mValues.getAsLong(Programs.COLUMN_END_TIME_UTC_MILLIS);
    109         return l == null ? INVALID_LONG_VALUE : l;
    110     }
    111 
    112     /**
    113      * @return The value of {@link Programs#COLUMN_BROADCAST_GENRE} for the program.
    114      */
    115     public String[] getBroadcastGenres() {
    116         return Programs.Genres.decode(mValues.getAsString(Programs.COLUMN_BROADCAST_GENRE));
    117     }
    118 
    119     /**
    120      * @return The value of {@link Programs#COLUMN_RECORDING_PROHIBITED} for the program.
    121      */
    122     public boolean isRecordingProhibited() {
    123         Integer i = mValues.getAsInteger(Programs.COLUMN_RECORDING_PROHIBITED);
    124         return i != null && i == IS_RECORDING_PROHIBITED;
    125     }
    126 
    127     @Override
    128     public int hashCode() {
    129         return mValues.hashCode();
    130     }
    131 
    132     @Override
    133     public boolean equals(Object other) {
    134         if (!(other instanceof Program)) {
    135             return false;
    136         }
    137         return mValues.equals(((Program) other).mValues);
    138     }
    139 
    140     /**
    141      * @param other The program you're comparing to.
    142      * @return The chronological order of the programs.
    143      */
    144     @Override
    145     public int compareTo(@NonNull Program other) {
    146         return Long.compare(mValues.getAsLong(Programs.COLUMN_START_TIME_UTC_MILLIS),
    147                 other.mValues.getAsLong(Programs.COLUMN_START_TIME_UTC_MILLIS));
    148     }
    149 
    150     @Override
    151     public String toString() {
    152         return "Program{" + mValues.toString() + "}";
    153     }
    154 
    155     /**
    156      * @return The fields of the Program in the ContentValues format to be easily inserted into the
    157      * TV Input Framework database.
    158      */
    159     @Override
    160     public ContentValues toContentValues() {
    161         ContentValues values = super.toContentValues();
    162         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
    163             values.remove(Programs.COLUMN_RECORDING_PROHIBITED);
    164         }
    165         return values;
    166     }
    167 
    168     /**
    169      * Creates a Program object from a cursor including the fields defined in {@link Programs}.
    170      *
    171      * @param cursor A row from the TV Input Framework database.
    172      * @return A Program with the values taken from the cursor.
    173      */
    174     public static Program fromCursor(Cursor cursor) {
    175         // TODO: Add additional API which does not use costly getColumnIndex().
    176         Builder builder = new Builder();
    177         BaseProgram.setFieldsFromCursor(cursor, builder);
    178         int index;
    179         if ((index = cursor.getColumnIndex(Programs.COLUMN_CHANNEL_ID)) >= 0
    180                 && !cursor.isNull(index)) {
    181             builder.setChannelId(cursor.getLong(index));
    182         }
    183         if ((index = cursor.getColumnIndex(Programs.COLUMN_BROADCAST_GENRE)) >= 0
    184                 && !cursor.isNull(index)) {
    185             builder.setBroadcastGenres(Programs.Genres.decode(
    186                     cursor.getString(index)));
    187         }
    188         if ((index = cursor.getColumnIndex(Programs.COLUMN_START_TIME_UTC_MILLIS)) >= 0
    189                 && !cursor.isNull(index)) {
    190             builder.setStartTimeUtcMillis(cursor.getLong(index));
    191         }
    192         if ((index = cursor.getColumnIndex(Programs.COLUMN_END_TIME_UTC_MILLIS)) >= 0
    193                 && !cursor.isNull(index)) {
    194             builder.setEndTimeUtcMillis(cursor.getLong(index));
    195         }
    196         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    197             if ((index = cursor.getColumnIndex(Programs.COLUMN_RECORDING_PROHIBITED)) >= 0
    198                     && !cursor.isNull(index)) {
    199                 builder.setRecordingProhibited(cursor.getInt(index) == IS_RECORDING_PROHIBITED);
    200             }
    201         }
    202         return builder.build();
    203     }
    204 
    205     private static String[] getProjection() {
    206         String[] baseColumns = new String[] {
    207                 Programs.COLUMN_CHANNEL_ID,
    208                 Programs.COLUMN_BROADCAST_GENRE,
    209                 Programs.COLUMN_START_TIME_UTC_MILLIS,
    210                 Programs.COLUMN_END_TIME_UTC_MILLIS,
    211         };
    212         String[] nougatColumns = new String[] {
    213                 Programs.COLUMN_RECORDING_PROHIBITED
    214         };
    215         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    216             return CollectionUtils.concatAll(BaseProgram.PROJECTION, baseColumns, nougatColumns);
    217         } else {
    218             return CollectionUtils.concatAll(BaseProgram.PROJECTION, baseColumns);
    219         }
    220     }
    221 
    222     /**
    223      * This Builder class simplifies the creation of a {@link Program} object.
    224      */
    225     public static class Builder extends BaseProgram.Builder<Builder> {
    226 
    227         /**
    228          * Creates a new Builder object.
    229          */
    230         public Builder() {
    231         }
    232 
    233         /**
    234          * Creates a new Builder object with values copied from another Program.
    235          * @param other The Program you're copying from.
    236          */
    237         public Builder(Program other) {
    238             mValues = new ContentValues(other.mValues);
    239         }
    240 
    241         /**
    242          * Sets the ID of the {@link Channel} that contains this program.
    243          *
    244          * @param channelId The value of {@link Programs#COLUMN_CHANNEL_ID for the program.
    245          * @return This Builder object to allow for chaining of calls to builder methods.
    246          */
    247         public Builder setChannelId(long channelId) {
    248             mValues.put(Programs.COLUMN_CHANNEL_ID, channelId);
    249             return this;
    250         }
    251 
    252         /**
    253          * Sets the time when the program is going to begin in milliseconds since the epoch.
    254          *
    255          * @param startTimeUtcMillis The value of {@link Programs#COLUMN_START_TIME_UTC_MILLIS} for
    256          *                           the program.
    257          * @return This Builder object to allow for chaining of calls to builder methods.
    258          */
    259         public Builder setStartTimeUtcMillis(long startTimeUtcMillis) {
    260             mValues.put(Programs.COLUMN_START_TIME_UTC_MILLIS, startTimeUtcMillis);
    261             return this;
    262         }
    263 
    264         /**
    265          * Sets the time when this program is going to end in milliseconds since the epoch.
    266          *
    267          * @param endTimeUtcMillis The value of {@link Programs#COLUMN_END_TIME_UTC_MILLIS} for the
    268          *                         program.
    269          * @return This Builder object to allow for chaining of calls to builder methods.
    270          */
    271         public Builder setEndTimeUtcMillis(long endTimeUtcMillis) {
    272             mValues.put(Programs.COLUMN_END_TIME_UTC_MILLIS, endTimeUtcMillis);
    273             return this;
    274         }
    275 
    276         /**
    277          * Sets the broadcast-specified genres of the program.
    278          *
    279          * @param genres Array of genres that apply to the program based on the broadcast standard
    280          *               which will be flattened to a String to store in a database.
    281          * @return This Builder object to allow for chaining of calls to builder methods.
    282          * @see Programs#COLUMN_BROADCAST_GENRE
    283          */
    284         public Builder setBroadcastGenres(@Genre String[] genres) {
    285             mValues.put(Programs.COLUMN_BROADCAST_GENRE, Programs.Genres.encode(genres));
    286             return this;
    287         }
    288 
    289         /**
    290          * Sets whether this program cannot be recorded.
    291          *
    292          * @param prohibited The value of {@link Programs#COLUMN_RECORDING_PROHIBITED} for the
    293          *                   program.
    294          * @return This Builder object to allow for chaining of calls to builder methods.
    295          */
    296         public Builder setRecordingProhibited(boolean prohibited) {
    297             mValues.put(Programs.COLUMN_RECORDING_PROHIBITED,
    298                     prohibited ? IS_RECORDING_PROHIBITED : 0);
    299             return this;
    300         }
    301 
    302         /**
    303          * @return A new Program with values supplied by the Builder.
    304          */
    305         public Program build() {
    306             return new Program(this);
    307         }
    308     }
    309 }
    310