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.IntDef;
     25 import androidx.annotation.RestrictTo;
     26 import androidx.tvprovider.media.tv.TvContractCompat.WatchNextPrograms;
     27 
     28 import java.lang.annotation.Retention;
     29 import java.lang.annotation.RetentionPolicy;
     30 import java.util.Objects;
     31 import java.util.Set;
     32 
     33 /**
     34  * A convenience class to access {@link WatchNextPrograms} entries in the system content
     35  * provider.
     36  *
     37  * <p>This class makes it easy to insert or retrieve a program from the system content provider,
     38  * which is defined in {@link TvContractCompat}.
     39  *
     40  * <p>Usage example when inserting a "watch next" program:
     41  * <pre>
     42  * WatchNextProgram watchNextProgram = new WatchNextProgram.Builder()
     43  *         .setWatchNextType(WatchNextPrograms.WATCH_NEXT_TYPE_CONTINUE)
     44  *         .setType(PreviewPrograms.TYPE_MOVIE)
     45  *         .setTitle("Program Title")
     46  *         .setDescription("Program Description")
     47  *         .setPosterArtUri(Uri.parse("http://example.com/poster_art.png"))
     48  *         // Set more attributes...
     49  *         .build();
     50  * Uri watchNextProgramUri = getContentResolver().insert(WatchNextPrograms.CONTENT_URI,
     51  *         watchNextProgram.toContentValues());
     52  * </pre>
     53  *
     54  * <p>Usage example when retrieving a "watch next" program:
     55  * <pre>
     56  * WatchNextProgram watchNextProgram;
     57  * try (Cursor cursor = resolver.query(watchNextProgramUri, null, null, null, null)) {
     58  *     if (cursor != null && cursor.getCount() != 0) {
     59  *         cursor.moveToNext();
     60  *         watchNextProgram = WatchNextProgram.fromCursor(cursor);
     61  *     }
     62  * }
     63  * </pre>
     64  *
     65  * <p>Usage example when updating an existing "watch next" program:
     66  * <pre>
     67  * WatchNextProgram updatedProgram = new WatchNextProgram.Builder(watchNextProgram)
     68  *         .setLastEngagementTimeUtcMillis(System.currentTimeMillis())
     69  *         .build();
     70  * getContentResolver().update(TvContractCompat.buildWatchNextProgramUri(updatedProgram.getId()),
     71  *         updatedProgram.toContentValues(), null, null);
     72  * </pre>
     73  *
     74  * <p>Usage example when deleting a "watch next" program:
     75  * <pre>
     76  * getContentResolver().delete(TvContractCompat.buildWatchNextProgramUri(existingProgram.getId()),
     77  *         null, null);
     78  * </pre>
     79  */
     80 public final class WatchNextProgram extends BasePreviewProgram {
     81     /**
     82      * @hide
     83      */
     84     @RestrictTo(LIBRARY_GROUP)
     85     public static final String[] PROJECTION = getProjection();
     86 
     87     private static final long INVALID_LONG_VALUE = -1;
     88     private static final int INVALID_INT_VALUE = -1;
     89 
     90     /** @hide */
     91     @IntDef({
     92             WATCH_NEXT_TYPE_UNKNOWN,
     93             WatchNextPrograms.WATCH_NEXT_TYPE_CONTINUE,
     94             WatchNextPrograms.WATCH_NEXT_TYPE_NEXT,
     95             WatchNextPrograms.WATCH_NEXT_TYPE_NEW,
     96             WatchNextPrograms.WATCH_NEXT_TYPE_WATCHLIST,
     97     })
     98     @Retention(RetentionPolicy.SOURCE)
     99     @RestrictTo(LIBRARY_GROUP)
    100     public @interface WatchNextType {
    101     }
    102 
    103     /**
    104      * The unknown watch next type. Use this type when the actual type is not known.
    105      */
    106     public static final int WATCH_NEXT_TYPE_UNKNOWN = -1;
    107 
    108     private WatchNextProgram(Builder builder) {
    109         super(builder);
    110     }
    111 
    112     /**
    113      * @return The value of {@link WatchNextPrograms#COLUMN_WATCH_NEXT_TYPE} for the program,
    114      * or {@link #WATCH_NEXT_TYPE_UNKNOWN} if it's unknown.
    115      */
    116     public @WatchNextType int getWatchNextType() {
    117         Integer i = mValues.getAsInteger(WatchNextPrograms.COLUMN_WATCH_NEXT_TYPE);
    118         return i == null ? WATCH_NEXT_TYPE_UNKNOWN : i;
    119     }
    120 
    121     /**
    122      * @return The value of {@link WatchNextPrograms#COLUMN_LAST_ENGAGEMENT_TIME_UTC_MILLIS} for the
    123      * program.
    124      */
    125     public long getLastEngagementTimeUtcMillis() {
    126         Long l = mValues.getAsLong(WatchNextPrograms.COLUMN_LAST_ENGAGEMENT_TIME_UTC_MILLIS);
    127         return l == null ? INVALID_LONG_VALUE : l;
    128     }
    129 
    130     @Override
    131     public boolean equals(Object other) {
    132         if (!(other instanceof WatchNextProgram)) {
    133             return false;
    134         }
    135         return mValues.equals(((WatchNextProgram) other).mValues);
    136     }
    137 
    138     /**
    139      * Indicates whether some other WatchNextProgram has any set attribute that is different from
    140      * this WatchNextProgram's respective attributes. An attribute is considered "set" if its key
    141      * is present in the ContentValues vector.
    142      */
    143     public boolean hasAnyUpdatedValues(WatchNextProgram update) {
    144         Set<String> updateKeys = update.mValues.keySet();
    145         for (String key : updateKeys) {
    146             Object updateValue = update.mValues.get(key);
    147             Object currValue = mValues.get(key);
    148             if (!Objects.deepEquals(updateValue, currValue)) {
    149                 return true;
    150             }
    151         }
    152         return false;
    153     }
    154 
    155     @Override
    156     public String toString() {
    157         return "WatchNextProgram{" + mValues.toString() + "}";
    158     }
    159 
    160     /**
    161      * @return The fields of the Program in the ContentValues format to be easily inserted into the
    162      * TV Input Framework database.
    163      */
    164     @Override
    165     public ContentValues toContentValues() {
    166         return toContentValues(false);
    167     }
    168 
    169     /**
    170      * Returns fields of the WatchNextProgram in the ContentValues format to be easily inserted
    171      * into the TV Input Framework database.
    172      *
    173      * @param includeProtectedFields Whether the fields protected by system is included or not.
    174      * @hide
    175      */
    176     @RestrictTo(LIBRARY_GROUP)
    177     @Override
    178     public ContentValues toContentValues(boolean includeProtectedFields) {
    179         ContentValues values = super.toContentValues(includeProtectedFields);
    180         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
    181             values.remove(WatchNextPrograms.COLUMN_WATCH_NEXT_TYPE);
    182             values.remove(WatchNextPrograms.COLUMN_LAST_ENGAGEMENT_TIME_UTC_MILLIS);
    183         }
    184         return values;
    185     }
    186 
    187     /**
    188      * Creates a WatchNextProgram object from a cursor including the fields defined in
    189      * {@link WatchNextPrograms}.
    190      *
    191      * @param cursor A row from the TV Input Framework database.
    192      * @return A Program with the values taken from the cursor.
    193      */
    194     public static WatchNextProgram fromCursor(Cursor cursor) {
    195         // TODO: Add additional API which does not use costly getColumnIndex().
    196         Builder builder = new Builder();
    197         BasePreviewProgram.setFieldsFromCursor(cursor, builder);
    198         int index;
    199         if ((index = cursor.getColumnIndex(WatchNextPrograms.COLUMN_WATCH_NEXT_TYPE)) >= 0
    200                 && !cursor.isNull(index)) {
    201             builder.setWatchNextType(cursor.getInt(index));
    202         }
    203         if ((index = cursor.getColumnIndex(
    204                 WatchNextPrograms.COLUMN_LAST_ENGAGEMENT_TIME_UTC_MILLIS)) >= 0
    205                 && !cursor.isNull(index)) {
    206             builder.setLastEngagementTimeUtcMillis(cursor.getLong(index));
    207         }
    208         return builder.build();
    209     }
    210 
    211     private static String[] getProjection() {
    212         String[] oColumns = new String[]{
    213                 WatchNextPrograms.COLUMN_WATCH_NEXT_TYPE,
    214                 WatchNextPrograms.COLUMN_LAST_ENGAGEMENT_TIME_UTC_MILLIS,
    215         };
    216         return CollectionUtils.concatAll(BasePreviewProgram.PROJECTION, oColumns);
    217     }
    218 
    219     /**
    220      * This Builder class simplifies the creation of a {@link WatchNextProgram} object.
    221      */
    222     public static final class Builder extends BasePreviewProgram.Builder<Builder> {
    223 
    224         /**
    225          * Creates a new Builder object.
    226          */
    227         public Builder() {
    228         }
    229 
    230         /**
    231          * Creates a new Builder object with values copied from another Program.
    232          *
    233          * @param other The Program you're copying from.
    234          */
    235         public Builder(WatchNextProgram other) {
    236             mValues = new ContentValues(other.mValues);
    237         }
    238 
    239         /**
    240          * Sets the "watch next" type of this program content.
    241          *
    242          * <p>The value should match one of the followings:
    243          * {@link WatchNextPrograms#WATCH_NEXT_TYPE_CONTINUE},
    244          * {@link WatchNextPrograms#WATCH_NEXT_TYPE_NEXT}, and
    245          * {@link WatchNextPrograms#WATCH_NEXT_TYPE_NEW}.
    246          *
    247          * @param watchNextType The value of {@link WatchNextPrograms#COLUMN_WATCH_NEXT_TYPE} for
    248          *                      the program.
    249          * @return This Builder object to allow for chaining of calls to builder methods.
    250          */
    251         public Builder setWatchNextType(@WatchNextType int watchNextType) {
    252             mValues.put(WatchNextPrograms.COLUMN_WATCH_NEXT_TYPE, watchNextType);
    253             return this;
    254         }
    255 
    256         /**
    257          * Sets the time when the program is going to begin in milliseconds since the epoch.
    258          *
    259          * @param lastEngagementTimeUtcMillis The value of
    260          *      {@link WatchNextPrograms#COLUMN_LAST_ENGAGEMENT_TIME_UTC_MILLIS}
    261          *      for the program.
    262          * @return This Builder object to allow for chaining of calls to builder methods.
    263          */
    264         public Builder setLastEngagementTimeUtcMillis(long lastEngagementTimeUtcMillis) {
    265             mValues.put(WatchNextPrograms.COLUMN_LAST_ENGAGEMENT_TIME_UTC_MILLIS,
    266                     lastEngagementTimeUtcMillis);
    267             return this;
    268         }
    269 
    270         /**
    271          * @return A new Program with values supplied by the Builder.
    272          */
    273         public WatchNextProgram build() {
    274             return new WatchNextProgram(this);
    275         }
    276     }
    277 }
    278