Home | History | Annotate | Download | only in data
      1 /*
      2  * Copyright (C) 2015 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 com.android.tv.testing.data;
     17 
     18 import android.content.ContentResolver;
     19 import android.content.ContentValues;
     20 import android.content.Context;
     21 import android.database.Cursor;
     22 import android.media.tv.TvContract;
     23 import android.media.tv.TvContract.Channels;
     24 import android.net.Uri;
     25 import android.os.AsyncTask;
     26 import android.support.annotation.WorkerThread;
     27 import android.text.TextUtils;
     28 import android.util.Log;
     29 import android.util.SparseArray;
     30 import java.io.IOException;
     31 import java.io.InputStream;
     32 import java.io.OutputStream;
     33 import java.util.ArrayList;
     34 import java.util.HashMap;
     35 import java.util.List;
     36 import java.util.Map;
     37 
     38 /** Static helper methods for working with {@link android.media.tv.TvContract}. */
     39 public class ChannelUtils {
     40     private static final String TAG = "ChannelUtils";
     41     private static final boolean DEBUG = false;
     42 
     43     /**
     44      * Query and return the map of (channel_id, ChannelInfo). See: {@link
     45      * com.android.tv.testing.data.ChannelInfo#fromCursor(Cursor)}.
     46      */
     47     @WorkerThread
     48     public static Map<Long, ChannelInfo> queryChannelInfoMapForTvInput(
     49             Context context, String inputId) {
     50         Uri uri = TvContract.buildChannelsUriForInput(inputId);
     51         Map<Long, ChannelInfo> map = new HashMap<>();
     52 
     53         String[] projections = new String[ChannelInfo.PROJECTION.length + 1];
     54         projections[0] = Channels._ID;
     55         System.arraycopy(ChannelInfo.PROJECTION, 0, projections, 1, ChannelInfo.PROJECTION.length);
     56         try (Cursor cursor =
     57                 context.getContentResolver().query(uri, projections, null, null, null)) {
     58             if (cursor != null) {
     59                 while (cursor.moveToNext()) {
     60                     map.put(cursor.getLong(0), ChannelInfo.fromCursor(cursor));
     61                 }
     62             }
     63             return map;
     64         }
     65     }
     66 
     67     @WorkerThread
     68     public static void updateChannels(Context context, String inputId, List<ChannelInfo> channels) {
     69         // Create a map from original network ID to channel row ID for existing channels.
     70         SparseArray<Long> existingChannelsMap = new SparseArray<>();
     71         Uri channelsUri = TvContract.buildChannelsUriForInput(inputId);
     72         String[] projection = {Channels._ID, Channels.COLUMN_ORIGINAL_NETWORK_ID};
     73         ContentResolver resolver = context.getContentResolver();
     74         try (Cursor cursor = resolver.query(channelsUri, projection, null, null, null)) {
     75             while (cursor != null && cursor.moveToNext()) {
     76                 long rowId = cursor.getLong(0);
     77                 int originalNetworkId = cursor.getInt(1);
     78                 existingChannelsMap.put(originalNetworkId, rowId);
     79             }
     80         }
     81 
     82         Map<Uri, String> logos = new HashMap<>();
     83         for (ChannelInfo channel : channels) {
     84             // If a channel exists, update it. If not, insert a new one.
     85             ContentValues values = new ContentValues();
     86             values.put(Channels.COLUMN_INPUT_ID, inputId);
     87             values.put(Channels.COLUMN_DISPLAY_NUMBER, channel.number);
     88             values.put(Channels.COLUMN_DISPLAY_NAME, channel.name);
     89             values.put(Channels.COLUMN_ORIGINAL_NETWORK_ID, channel.originalNetworkId);
     90             String videoFormat = channel.getVideoFormat();
     91             if (videoFormat != null) {
     92                 values.put(Channels.COLUMN_VIDEO_FORMAT, videoFormat);
     93             } else {
     94                 values.putNull(Channels.COLUMN_VIDEO_FORMAT);
     95             }
     96             if (!TextUtils.isEmpty(channel.appLinkText)) {
     97                 values.put(Channels.COLUMN_APP_LINK_TEXT, channel.appLinkText);
     98             }
     99             if (channel.appLinkColor != 0) {
    100                 values.put(Channels.COLUMN_APP_LINK_COLOR, channel.appLinkColor);
    101             }
    102             if (!TextUtils.isEmpty(channel.appLinkPosterArtUri)) {
    103                 values.put(Channels.COLUMN_APP_LINK_POSTER_ART_URI, channel.appLinkPosterArtUri);
    104             }
    105             if (!TextUtils.isEmpty(channel.appLinkIconUri)) {
    106                 values.put(Channels.COLUMN_APP_LINK_ICON_URI, channel.appLinkIconUri);
    107             }
    108             if (!TextUtils.isEmpty(channel.appLinkIntentUri)) {
    109                 values.put(Channels.COLUMN_APP_LINK_INTENT_URI, channel.appLinkIntentUri);
    110             }
    111             Long rowId = existingChannelsMap.get(channel.originalNetworkId);
    112             Uri uri;
    113             if (rowId == null) {
    114                 if (DEBUG) {
    115                     Log.d(TAG, "Inserting " + channel);
    116                 }
    117                 uri = resolver.insert(TvContract.Channels.CONTENT_URI, values);
    118             } else {
    119                 if (DEBUG) {
    120                     Log.d(TAG, "Updating " + channel);
    121                 }
    122                 uri = TvContract.buildChannelUri(rowId);
    123                 resolver.update(uri, values, null, null);
    124                 existingChannelsMap.remove(channel.originalNetworkId);
    125             }
    126             if (!TextUtils.isEmpty(channel.logoUrl)) {
    127                 logos.put(TvContract.buildChannelLogoUri(uri), channel.logoUrl);
    128             }
    129         }
    130         if (!logos.isEmpty()) {
    131             new InsertLogosTask(context).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, logos);
    132         }
    133 
    134         // Deletes channels which don't exist in the new feed.
    135         int size = existingChannelsMap.size();
    136         for (int i = 0; i < size; ++i) {
    137             Long rowId = existingChannelsMap.valueAt(i);
    138             resolver.delete(TvContract.buildChannelUri(rowId), null, null);
    139         }
    140     }
    141 
    142     public static void copy(InputStream is, OutputStream os) throws IOException {
    143         byte[] buffer = new byte[1024];
    144         int len;
    145         while ((len = is.read(buffer)) != -1) {
    146             os.write(buffer, 0, len);
    147         }
    148     }
    149 
    150     private ChannelUtils() {
    151         // Prevent instantiation.
    152     }
    153 
    154     public static List<ChannelInfo> createChannelInfos(Context context, int channelCount) {
    155         List<ChannelInfo> channels = new ArrayList<>();
    156         for (int i = 1; i <= channelCount; i++) {
    157             channels.add(ChannelInfo.create(context, i));
    158         }
    159         return channels;
    160     }
    161 
    162     public static class InsertLogosTask extends AsyncTask<Map<Uri, String>, Void, Void> {
    163         private final Context mContext;
    164 
    165         InsertLogosTask(Context context) {
    166             mContext = context;
    167         }
    168 
    169         @SafeVarargs
    170         @Override
    171         public final Void doInBackground(Map<Uri, String>... logosList) {
    172             for (Map<Uri, String> logos : logosList) {
    173                 for (Uri uri : logos.keySet()) {
    174                     if (uri == null) {
    175                         continue;
    176                     }
    177                     Uri logoUri = Uri.parse(logos.get(uri));
    178                     try (InputStream is = mContext.getContentResolver().openInputStream(logoUri);
    179                             OutputStream os = mContext.getContentResolver().openOutputStream(uri)) {
    180                         copy(is, os);
    181                     } catch (IOException ioe) {
    182                         Log.e(TAG, "Failed to write " + logoUri + "  to " + uri, ioe);
    183                     }
    184                 }
    185             }
    186             return null;
    187         }
    188     }
    189 }
    190