blob: 920c7087fe9d33b54a2f8177988594b217e92eec [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tv.testing.data;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.media.tv.TvContract;
import android.media.tv.TvContract.Channels;
import android.net.Uri;
import android.os.AsyncTask;
import android.support.annotation.WorkerThread;
import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/** Static helper methods for working with {@link android.media.tv.TvContract}. */
public class ChannelUtils {
private static final String TAG = "ChannelUtils";
private static final boolean DEBUG = false;
/**
* Query and return the map of (channel_id, ChannelInfo). See: {@link
* com.android.tv.testing.data.ChannelInfo#fromCursor(Cursor)}.
*/
@WorkerThread
public static Map<Long, ChannelInfo> queryChannelInfoMapForTvInput(
Context context, String inputId) {
Uri uri = TvContract.buildChannelsUriForInput(inputId);
Map<Long, ChannelInfo> map = new HashMap<>();
String[] projections = new String[ChannelInfo.PROJECTION.length + 1];
projections[0] = Channels._ID;
System.arraycopy(ChannelInfo.PROJECTION, 0, projections, 1, ChannelInfo.PROJECTION.length);
try (Cursor cursor =
context.getContentResolver().query(uri, projections, null, null, null)) {
if (cursor != null) {
while (cursor.moveToNext()) {
map.put(cursor.getLong(0), ChannelInfo.fromCursor(cursor));
}
}
return map;
}
}
@WorkerThread
public static void updateChannels(Context context, String inputId, List<ChannelInfo> channels) {
// Create a map from original network ID to channel row ID for existing channels.
SparseArray<Long> existingChannelsMap = new SparseArray<>();
Uri channelsUri = TvContract.buildChannelsUriForInput(inputId);
String[] projection = {Channels._ID, Channels.COLUMN_ORIGINAL_NETWORK_ID};
ContentResolver resolver = context.getContentResolver();
try (Cursor cursor = resolver.query(channelsUri, projection, null, null, null)) {
while (cursor != null && cursor.moveToNext()) {
long rowId = cursor.getLong(0);
int originalNetworkId = cursor.getInt(1);
existingChannelsMap.put(originalNetworkId, rowId);
}
}
Map<Uri, String> logos = new HashMap<>();
for (ChannelInfo channel : channels) {
// If a channel exists, update it. If not, insert a new one.
ContentValues values = new ContentValues();
values.put(Channels.COLUMN_INPUT_ID, inputId);
values.put(Channels.COLUMN_DISPLAY_NUMBER, channel.number);
values.put(Channels.COLUMN_DISPLAY_NAME, channel.name);
values.put(Channels.COLUMN_ORIGINAL_NETWORK_ID, channel.originalNetworkId);
String videoFormat = channel.getVideoFormat();
if (videoFormat != null) {
values.put(Channels.COLUMN_VIDEO_FORMAT, videoFormat);
} else {
values.putNull(Channels.COLUMN_VIDEO_FORMAT);
}
if (!TextUtils.isEmpty(channel.appLinkText)) {
values.put(Channels.COLUMN_APP_LINK_TEXT, channel.appLinkText);
}
if (channel.appLinkColor != 0) {
values.put(Channels.COLUMN_APP_LINK_COLOR, channel.appLinkColor);
}
if (!TextUtils.isEmpty(channel.appLinkPosterArtUri)) {
values.put(Channels.COLUMN_APP_LINK_POSTER_ART_URI, channel.appLinkPosterArtUri);
}
if (!TextUtils.isEmpty(channel.appLinkIconUri)) {
values.put(Channels.COLUMN_APP_LINK_ICON_URI, channel.appLinkIconUri);
}
if (!TextUtils.isEmpty(channel.appLinkIntentUri)) {
values.put(Channels.COLUMN_APP_LINK_INTENT_URI, channel.appLinkIntentUri);
}
Long rowId = existingChannelsMap.get(channel.originalNetworkId);
Uri uri;
if (rowId == null) {
if (DEBUG) {
Log.d(TAG, "Inserting " + channel);
}
uri = resolver.insert(TvContract.Channels.CONTENT_URI, values);
} else {
if (DEBUG) {
Log.d(TAG, "Updating " + channel);
}
uri = TvContract.buildChannelUri(rowId);
resolver.update(uri, values, null, null);
existingChannelsMap.remove(channel.originalNetworkId);
}
if (!TextUtils.isEmpty(channel.logoUrl)) {
logos.put(TvContract.buildChannelLogoUri(uri), channel.logoUrl);
}
}
if (!logos.isEmpty()) {
new InsertLogosTask(context).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, logos);
}
// Deletes channels which don't exist in the new feed.
int size = existingChannelsMap.size();
for (int i = 0; i < size; ++i) {
Long rowId = existingChannelsMap.valueAt(i);
resolver.delete(TvContract.buildChannelUri(rowId), null, null);
}
}
public static void copy(InputStream is, OutputStream os) throws IOException {
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
}
private ChannelUtils() {
// Prevent instantiation.
}
public static List<ChannelInfo> createChannelInfos(Context context, int channelCount) {
List<ChannelInfo> channels = new ArrayList<>();
for (int i = 1; i <= channelCount; i++) {
channels.add(ChannelInfo.create(context, i));
}
return channels;
}
public static class InsertLogosTask extends AsyncTask<Map<Uri, String>, Void, Void> {
private final Context mContext;
InsertLogosTask(Context context) {
mContext = context;
}
@SafeVarargs
@Override
public final Void doInBackground(Map<Uri, String>... logosList) {
for (Map<Uri, String> logos : logosList) {
for (Uri uri : logos.keySet()) {
if (uri == null) {
continue;
}
Uri logoUri = Uri.parse(logos.get(uri));
try (InputStream is = mContext.getContentResolver().openInputStream(logoUri);
OutputStream os = mContext.getContentResolver().openOutputStream(uri)) {
copy(is, os);
} catch (IOException ioe) {
Log.e(TAG, "Failed to write " + logoUri + " to " + uri, ioe);
}
}
}
return null;
}
}
}