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 17 package com.android.tv.testing.data; 18 19 import android.content.ContentUris; 20 import android.content.ContentValues; 21 import android.content.Context; 22 import android.database.Cursor; 23 import android.database.sqlite.SQLiteException; 24 import android.media.tv.TvContract; 25 import android.media.tv.TvContract.Programs; 26 import android.net.Uri; 27 import android.util.Log; 28 import com.android.tv.common.TvContentRatingCache; 29 import com.android.tv.common.util.Clock; 30 import java.util.ArrayList; 31 import java.util.Map; 32 import java.util.concurrent.TimeUnit; 33 34 /** Static utilities for using Programs in tests */ 35 public final class ProgramUtils { 36 private static final String TAG = "ProgramUtils"; 37 private static final boolean DEBUG = false; 38 39 /** Populate program data for a week */ 40 public static final long PROGRAM_INSERT_DURATION_MS = TimeUnit.DAYS.toMillis(7); 41 42 private static final int MAX_DB_INSERT_COUNT_AT_ONCE = 500; 43 44 /** 45 * Populate programs by repeating given program information. This method will populate programs 46 * without any gap nor overlapping starting from the current time. 47 */ populatePrograms( Context context, Uri channelUri, ProgramInfo program, Clock clock)48 public static void populatePrograms( 49 Context context, Uri channelUri, ProgramInfo program, Clock clock) { 50 populatePrograms(context, channelUri, program, clock, PROGRAM_INSERT_DURATION_MS); 51 } 52 populatePrograms( Context context, Uri channelUri, ProgramInfo program, Clock clock, long programInsertDurationMs)53 public static void populatePrograms( 54 Context context, 55 Uri channelUri, 56 ProgramInfo program, 57 Clock clock, 58 long programInsertDurationMs) { 59 long currentTimeMs = clock.currentTimeMillis(); 60 long targetEndTimeMs = currentTimeMs + programInsertDurationMs; 61 populatePrograms(context, channelUri, program, currentTimeMs, targetEndTimeMs); 62 } 63 populatePrograms( Context context, Uri channelUri, ProgramInfo program, long currentTimeMs, long targetEndTimeMs)64 public static void populatePrograms( 65 Context context, 66 Uri channelUri, 67 ProgramInfo program, 68 long currentTimeMs, 69 long targetEndTimeMs) { 70 ContentValues values = new ContentValues(); 71 long channelId = ContentUris.parseId(channelUri); 72 73 values.put(Programs.COLUMN_CHANNEL_ID, channelId); 74 values.put(Programs.COLUMN_SHORT_DESCRIPTION, program.description); 75 values.put( 76 Programs.COLUMN_CONTENT_RATING, 77 TvContentRatingCache.contentRatingsToString(program.contentRatings)); 78 79 long timeMs = getLastProgramEndTimeMs(context, channelUri, currentTimeMs, targetEndTimeMs); 80 if (timeMs <= 0) { 81 timeMs = currentTimeMs; 82 } 83 int index = program.getIndex(timeMs, channelId); 84 timeMs = program.getStartTimeMs(index, channelId); 85 86 ArrayList<ContentValues> list = new ArrayList<>(); 87 while (timeMs < targetEndTimeMs) { 88 ProgramInfo programAt = program.build(context, index++); 89 values.put(Programs.COLUMN_TITLE, programAt.title); 90 values.put(Programs.COLUMN_EPISODE_TITLE, programAt.episode); 91 if (programAt.seasonNumber != 0) { 92 values.put(Programs.COLUMN_SEASON_NUMBER, programAt.seasonNumber); 93 } 94 if (programAt.episodeNumber != 0) { 95 values.put(Programs.COLUMN_EPISODE_NUMBER, programAt.episodeNumber); 96 } 97 values.put(Programs.COLUMN_POSTER_ART_URI, programAt.posterArtUri); 98 values.put(Programs.COLUMN_START_TIME_UTC_MILLIS, timeMs); 99 values.put(Programs.COLUMN_END_TIME_UTC_MILLIS, timeMs + programAt.durationMs); 100 values.put(Programs.COLUMN_CANONICAL_GENRE, programAt.genre); 101 values.put(Programs.COLUMN_POSTER_ART_URI, programAt.posterArtUri); 102 list.add(new ContentValues(values)); 103 timeMs += programAt.durationMs; 104 105 if (list.size() >= MAX_DB_INSERT_COUNT_AT_ONCE || timeMs >= targetEndTimeMs) { 106 try { 107 context.getContentResolver() 108 .bulkInsert( 109 Programs.CONTENT_URI, 110 list.toArray(new ContentValues[list.size()])); 111 } catch (SQLiteException e) { 112 Log.e(TAG, "Can't insert EPG.", e); 113 return; 114 } 115 if (DEBUG) Log.d(TAG, "Inserted " + list.size() + " programs for " + channelUri); 116 list.clear(); 117 } 118 } 119 } 120 getLastProgramEndTimeMs( Context context, Uri channelUri, long startTimeMs, long endTimeMs)121 private static long getLastProgramEndTimeMs( 122 Context context, Uri channelUri, long startTimeMs, long endTimeMs) { 123 Uri uri = TvContract.buildProgramsUriForChannel(channelUri, startTimeMs, endTimeMs); 124 String[] projection = {Programs.COLUMN_END_TIME_UTC_MILLIS}; 125 try (Cursor cursor = 126 context.getContentResolver().query(uri, projection, null, null, null)) { 127 if (cursor != null && cursor.moveToLast()) { 128 return cursor.getLong(0); 129 } 130 } 131 return 0; 132 } 133 ProgramUtils()134 private ProgramUtils() {} 135 updateProgramForAllChannelsOf( Context context, String inputId, Clock clock, long durationMs)136 public static void updateProgramForAllChannelsOf( 137 Context context, String inputId, Clock clock, long durationMs) { 138 // Reload channels so we have the ids. 139 Map<Long, ChannelInfo> channelIdToInfoMap = 140 ChannelUtils.queryChannelInfoMapForTvInput(context, inputId); 141 for (Long channelId : channelIdToInfoMap.keySet()) { 142 ProgramInfo programInfo = ProgramInfo.create(); 143 populatePrograms( 144 context, TvContract.buildChannelUri(channelId), programInfo, clock, durationMs); 145 } 146 } 147 } 148