• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
queryChannelInfoMapForTvInput( Context context, String inputId)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
updateChannels(Context context, String inputId, List<ChannelInfo> channels)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 
copy(InputStream is, OutputStream os)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 
ChannelUtils()150     private ChannelUtils() {
151         // Prevent instantiation.
152     }
153 
createChannelInfos(Context context, int channelCount)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 
InsertLogosTask(Context context)165         InsertLogosTask(Context context) {
166             mContext = context;
167         }
168 
169         @SafeVarargs
170         @Override
doInBackground(Map<Uri, String>.... logosList)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