/** * Starts the manager. */ public void start() { if (mStarted) { return; } mStarted = true; // Should be called directly instead of posting MSG_UPDATE_CURRENT_PROGRAMS message // to the handler. If not, another DB task can be executed before loading current programs. handleUpdateCurrentPrograms(); if (mPrefetchEnabled) { mHandler.sendEmptyMessage(MSG_UPDATE_PREFETCH_PROGRAM); } mContentResolver.registerContentObserver(Programs.CONTENT_URI, true, mProgramObserver); if (mEpgFetcher != null && Experiments.CLOUD_EPG.get()) { mEpgFetcher.start(); } }
public static long getLastProgramEndTimeMillis(ContentResolver resolver, Uri channelUri) { Uri uri = TvContract.buildProgramsUriForChannel(channelUri); String[] projection = {Programs.COLUMN_END_TIME_UTC_MILLIS}; Cursor cursor = null; try { // TvProvider returns programs chronological order by default. cursor = resolver.query(uri, projection, null, null, null); if (cursor == null || cursor.getCount() == 0) { return 0; } cursor.moveToLast(); return cursor.getLong(0); } catch (Exception e) { Log.w(TAG, "Unable to get last program end time for " + channelUri, e); } finally { if (cursor != null) { cursor.close(); } } return 0; }
/** * Starts the DB sync. */ public void start() { if (!mChannelDataManager.isDbLoadFinished()) { mChannelDataManager.addListener(mChannelDataManagerListener); return; } mContext.getContentResolver().registerContentObserver(Programs.CONTENT_URI, true, mContentObserver); mDataManager.addScheduledRecordingListener(mScheduleListener); onChannelsUpdated(); onProgramsUpdated(); }
@Override public void onCreate(Bundle savedInstanceState) { Bundle args = getArguments(); if (args != null) { mSeriesRecording = args.getParcelable(SERIES_SCHEDULES_KEY_SERIES_RECORDING); mPrograms = args.getParcelableArrayList(SERIES_SCHEDULES_KEY_SERIES_PROGRAMS); } super.onCreate(savedInstanceState); ApplicationSingletons singletons = TvApplication.getSingletons(getContext()); singletons.getDvrDataManager().addSeriesRecordingListener(mSeriesRecordingListener); mChannelDataManager = singletons.getChannelDataManager(); mChannelDataManager.addListener(mChannelListener); getContext().getContentResolver().registerContentObserver(Programs.CONTENT_URI, true, mContentObserver); }
private List<Program> queryPrograms(long channelId, long startTimeMs, long endTimeMs) { try (Cursor c = mContext.getContentResolver().query( TvContract.buildProgramsUriForChannel(channelId, startTimeMs, endTimeMs), Program.PROJECTION, null, null, Programs.COLUMN_START_TIME_UTC_MILLIS)) { if (c == null) { return Collections.emptyList(); } ArrayList<Program> programs = new ArrayList<>(); while (c.moveToNext()) { programs.add(Program.fromCursor(c)); } return programs; } }
/** * Search channels, inputs, or programs from TvProvider. * This assumes that parental control settings will not be change while searching. * * @param action One of {@link #ACTION_TYPE_SWITCH_CHANNEL}, {@link #ACTION_TYPE_SWITCH_INPUT}, * or {@link #ACTION_TYPE_AMBIGUOUS}, */ @Override @WorkerThread public List<SearchResult> search(String query, int limit, int action) { List<SearchResult> results = new ArrayList<>(); if (!PermissionUtils.hasAccessAllEpg(mContext)) { // TODO: support this feature for non-system LC app. b/23939816 return results; } Set<Long> channelsFound = new HashSet<>(); if (action == ACTION_TYPE_SWITCH_CHANNEL) { results.addAll(searchChannels(query, channelsFound, limit)); } else if (action == ACTION_TYPE_SWITCH_INPUT) { results.addAll(searchInputs(query, limit)); } else { // Search channels first. results.addAll(searchChannels(query, channelsFound, limit)); if (results.size() >= limit) { return results; } // In case the user wanted to perform the action "switch to XXX", which is indicated by // setting the limit to 1, search inputs. if (limit == 1) { results.addAll(searchInputs(query, limit)); if (!results.isEmpty()) { return results; } } // Lastly, search programs. limit -= results.size(); results.addAll(searchPrograms(query, null, new String[] { Programs.COLUMN_TITLE, Programs.COLUMN_SHORT_DESCRIPTION }, channelsFound, limit)); } return results; }
/** * Replaces the channel information - title, description, channel logo - with the current * program information of the channel if the current program information exists and it is not * blocked. */ @WorkerThread private void fillProgramInfo(SearchResult result) { long now = System.currentTimeMillis(); Uri uri = TvContract.buildProgramsUriForChannel(result.channelId, now, now); String[] projection = new String[] { Programs.COLUMN_TITLE, Programs.COLUMN_POSTER_ART_URI, Programs.COLUMN_CONTENT_RATING, Programs.COLUMN_VIDEO_WIDTH, Programs.COLUMN_VIDEO_HEIGHT, Programs.COLUMN_START_TIME_UTC_MILLIS, Programs.COLUMN_END_TIME_UTC_MILLIS }; try (Cursor c = mContentResolver.query(uri, projection, null, null, null)) { if (c != null && c.moveToNext() && !isRatingBlocked(c.getString(2))) { String channelName = result.title; long startUtcMillis = c.getLong(5); long endUtcMillis = c.getLong(6); result.title = c.getString(0); result.description = buildProgramDescription(result.channelNumber, channelName, startUtcMillis, endUtcMillis); String imageUri = c.getString(1); if (imageUri != null) { result.imageUri = imageUri; } result.videoWidth = c.getInt(3); result.videoHeight = c.getInt(4); result.duration = endUtcMillis - startUtcMillis; result.progressPercentage = getProgressPercentage(startUtcMillis, endUtcMillis); } } }
private static long getLastProgramEndTimeMs( Context context, Uri channelUri, long startTimeMs, long endTimeMs) { Uri uri = TvContract.buildProgramsUriForChannel(channelUri, startTimeMs, endTimeMs); String[] projection = {Programs.COLUMN_END_TIME_UTC_MILLIS}; try (Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null)) { if (cursor != null && cursor.moveToLast()) { return cursor.getLong(0); } } return 0; }
private SqlParams createSqlParams() { SqlParams sqlParams = new SqlParams(); if (PermissionUtils.hasAccessAllEpg(mContext)) { sqlParams.uri = Programs.CONTENT_URI; // Base StringBuilder selection = new StringBuilder(mLoadCurrentProgram ? PROGRAM_PREDICATE_WITH_CURRENT_PROGRAM : PROGRAM_PREDICATE); List<String> args = new ArrayList<>(); args.add(Long.toString(System.currentTimeMillis())); // Channel option if (!mQueryAllChannels) { selection.append(" AND ").append(CHANNEL_ID_PREDICATE); args.add(Long.toString(mSeriesRecordings.get(0).getChannelId())); } // Title if (mSeriesRecordings.size() == 1) { selection.append(" AND ").append(PROGRAM_TITLE_PREDICATE); args.add(mSeriesRecordings.get(0).getTitle()); } sqlParams.selection = selection.toString(); sqlParams.selectionArgs = args.toArray(new String[args.size()]); sqlParams.filter = new SeriesRecordingCursorFilter(mSeriesRecordings); } else { // The query includes the current program. Will be filtered if needed. if (mQueryAllChannels) { sqlParams.uri = Programs.CONTENT_URI.buildUpon() .appendQueryParameter(PARAM_START_TIME, String.valueOf(System.currentTimeMillis())) .appendQueryParameter(PARAM_END_TIME, String.valueOf(Long.MAX_VALUE)) .build(); } else { sqlParams.uri = TvContract.buildProgramsUriForChannel( mSeriesRecordings.get(0).getChannelId(), System.currentTimeMillis(), Long.MAX_VALUE); } sqlParams.selection = null; sqlParams.selectionArgs = null; sqlParams.filter = new SeriesRecordingCursorFilterForNonSystem(mSeriesRecordings); } return sqlParams; }
@SuppressLint("InlinedApi") @SuppressWarnings("deprecation") private static ContentValues toContentValues(Program program) { ContentValues values = new ContentValues(); values.put(TvContract.Programs.COLUMN_CHANNEL_ID, program.getChannelId()); putValue(values, TvContract.Programs.COLUMN_TITLE, program.getTitle()); putValue(values, TvContract.Programs.COLUMN_EPISODE_TITLE, program.getEpisodeTitle()); if (BuildCompat.isAtLeastN()) { putValue(values, TvContract.Programs.COLUMN_SEASON_DISPLAY_NUMBER, program.getSeasonNumber()); putValue(values, TvContract.Programs.COLUMN_EPISODE_DISPLAY_NUMBER, program.getEpisodeNumber()); } else { putValue(values, TvContract.Programs.COLUMN_SEASON_NUMBER, program.getSeasonNumber()); putValue(values, TvContract.Programs.COLUMN_EPISODE_NUMBER, program.getEpisodeNumber()); } putValue(values, TvContract.Programs.COLUMN_SHORT_DESCRIPTION, program.getDescription()); putValue(values, TvContract.Programs.COLUMN_POSTER_ART_URI, program.getPosterArtUri()); putValue(values, TvContract.Programs.COLUMN_THUMBNAIL_URI, program.getThumbnailUri()); String[] canonicalGenres = program.getCanonicalGenres(); if (canonicalGenres != null && canonicalGenres.length > 0) { putValue(values, TvContract.Programs.COLUMN_CANONICAL_GENRE, Genres.encode(canonicalGenres)); } else { putValue(values, TvContract.Programs.COLUMN_CANONICAL_GENRE, ""); } TvContentRating[] ratings = program.getContentRatings(); if (ratings != null && ratings.length > 0) { StringBuilder sb = new StringBuilder(ratings[0].flattenToString()); for (int i = 1; i < ratings.length; ++i) { sb.append(CONTENT_RATING_SEPARATOR); sb.append(ratings[i].flattenToString()); } putValue(values, TvContract.Programs.COLUMN_CONTENT_RATING, sb.toString()); } else { putValue(values, TvContract.Programs.COLUMN_CONTENT_RATING, ""); } values.put(TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS, program.getStartTimeUtcMillis()); values.put(TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS, program.getEndTimeUtcMillis()); putValue(values, TvContract.Programs.COLUMN_INTERNAL_PROVIDER_DATA, InternalDataUtils.serializeInternalProviderData(program)); return values; }
public ProgramsUpdateTask(ContentResolver contentResolver, long time) { super(contentResolver, Programs.CONTENT_URI.buildUpon() .appendQueryParameter(PARAM_START_TIME, String.valueOf(time)) .appendQueryParameter(PARAM_END_TIME, String.valueOf(time)).build(), Program.PROJECTION, null, null, SORT_BY_TIME); }
@WorkerThread private List<SearchResult> searchChannels(String query, String[] columnForExactMatching, String[] columnForPartialMatching, Set<Long> channelsFound, int limit) { Assert.assertTrue( (columnForExactMatching != null && columnForExactMatching.length > 0) || (columnForPartialMatching != null && columnForPartialMatching.length > 0)); String[] projection = { Channels._ID, Channels.COLUMN_DISPLAY_NUMBER, Channels.COLUMN_DISPLAY_NAME, Channels.COLUMN_DESCRIPTION }; StringBuilder sb = new StringBuilder(); sb.append(Channels.COLUMN_BROWSABLE).append("=1 AND ") .append(Channels.COLUMN_SEARCHABLE).append("=1"); if (mTvInputManager.isParentalControlsEnabled()) { sb.append(" AND ").append(Channels.COLUMN_LOCKED).append("=0"); } sb.append(" AND ("); appendSelectionString(sb, columnForExactMatching, columnForPartialMatching); sb.append(")"); String selection = sb.toString(); int len = (columnForExactMatching == null ? 0 : columnForExactMatching.length) + (columnForPartialMatching == null ? 0 : columnForPartialMatching.length); String[] selectionArgs = new String[len]; insertSelectionArgumentStrings(selectionArgs, 0, query, columnForExactMatching, columnForPartialMatching); List<SearchResult> searchResults = new ArrayList<>(); try (Cursor c = mContentResolver.query(Channels.CONTENT_URI, projection, selection, selectionArgs, null)) { if (c != null) { int count = 0; while (c.moveToNext()) { long id = c.getLong(0); // Filter out the channel which has been already searched. if (channelsFound.contains(id)) { continue; } channelsFound.add(id); SearchResult result = new SearchResult(); result.channelId = id; result.channelNumber = c.getString(1); result.title = c.getString(2); result.description = c.getString(3); result.imageUri = TvContract.buildChannelLogoUri(result.channelId).toString(); result.intentAction = Intent.ACTION_VIEW; result.intentData = buildIntentData(result.channelId); result.contentType = Programs.CONTENT_ITEM_TYPE; result.isLive = true; result.progressPercentage = LocalSearchProvider.PROGRESS_PERCENTAGE_HIDE; searchResults.add(result); if (limit != NO_LIMIT && ++count >= limit) { break; } } } } return searchResults; }
/** * If query is matched to channel, {@code program} should be null. */ private void addResult(List<SearchResult> results, Set<Long> channelsFound, Channel channel, Program program) { if (program == null) { program = mProgramDataManager.getCurrentProgram(channel.getId()); if (program != null && isRatingBlocked(program.getContentRatings())) { program = null; } } SearchResult result = new SearchResult(); long channelId = channel.getId(); result.channelId = channelId; result.channelNumber = channel.getDisplayNumber(); if (program == null) { result.title = channel.getDisplayName(); result.description = channel.getDescription(); result.imageUri = TvContract.buildChannelLogoUri(channelId).toString(); result.intentAction = Intent.ACTION_VIEW; result.intentData = buildIntentData(channelId); result.contentType = Programs.CONTENT_ITEM_TYPE; result.isLive = true; result.progressPercentage = LocalSearchProvider.PROGRESS_PERCENTAGE_HIDE; } else { result.title = program.getTitle(); result.description = buildProgramDescription(channel.getDisplayNumber(), channel.getDisplayName(), program.getStartTimeUtcMillis(), program.getEndTimeUtcMillis()); result.imageUri = program.getPosterArtUri(); result.intentAction = Intent.ACTION_VIEW; result.intentData = buildIntentData(channelId); result.contentType = Programs.CONTENT_ITEM_TYPE; result.isLive = true; result.videoWidth = program.getVideoWidth(); result.videoHeight = program.getVideoHeight(); result.duration = program.getDurationMillis(); result.progressPercentage = getProgressPercentage( program.getStartTimeUtcMillis(), program.getEndTimeUtcMillis()); } if (DEBUG) { Log.d(TAG, "Add a result : channel=" + channel + " program=" + program); } results.add(result); channelsFound.add(channel.getId()); }
public AsyncProgramQueryTask(ContentResolver contentResolver) { super(contentResolver, Programs.CONTENT_URI, Program.PROJECTION, null, null, null); }
/** * Populate programs by repeating given program information. * This method will populate programs without any gap nor overlapping * starting from the current time. */ public static void populatePrograms(Context context, Uri channelUri, ProgramInfo program) { ContentValues values = new ContentValues(); long channelId = ContentUris.parseId(channelUri); values.put(Programs.COLUMN_CHANNEL_ID, channelId); values.put(Programs.COLUMN_SHORT_DESCRIPTION, program.description); values.put(Programs.COLUMN_CONTENT_RATING, TvContentRatingCache.contentRatingsToString(program.contentRatings)); long currentTimeMs = System.currentTimeMillis(); long targetEndTimeMs = currentTimeMs + PROGRAM_INSERT_DURATION_MS; long timeMs = getLastProgramEndTimeMs(context, channelUri, currentTimeMs, targetEndTimeMs); if (timeMs <= 0) { timeMs = currentTimeMs; } int index = program.getIndex(timeMs, channelId); timeMs = program.getStartTimeMs(index, channelId); ArrayList<ContentValues> list = new ArrayList<>(); while (timeMs < targetEndTimeMs) { ProgramInfo programAt = program.build(context, index++); values.put(Programs.COLUMN_TITLE, programAt.title); values.put(Programs.COLUMN_EPISODE_TITLE, programAt.episode); if (programAt.seasonNumber != 0) { values.put(Programs.COLUMN_SEASON_NUMBER, programAt.seasonNumber); } if (programAt.episodeNumber != 0) { values.put(Programs.COLUMN_EPISODE_NUMBER, programAt.episodeNumber); } values.put(Programs.COLUMN_POSTER_ART_URI, programAt.posterArtUri); values.put(Programs.COLUMN_START_TIME_UTC_MILLIS, timeMs); values.put(Programs.COLUMN_END_TIME_UTC_MILLIS, timeMs + programAt.durationMs); values.put(Programs.COLUMN_CANONICAL_GENRE, programAt.genre); values.put(Programs.COLUMN_POSTER_ART_URI, programAt.posterArtUri); list.add(new ContentValues(values)); timeMs += programAt.durationMs; if (list.size() >= MAX_DB_INSERT_COUNT_AT_ONCE || timeMs >= targetEndTimeMs) { try { context.getContentResolver().bulkInsert(Programs.CONTENT_URI, list.toArray(new ContentValues[list.size()])); } catch (SQLiteException e) { Log.e(TAG, "Can't insert EPG.", e); return; } if (DEBUG) Log.d(TAG, "Inserted " + list.size() + " programs for " + channelUri); list.clear(); } } }
public static RatingInfo buildRatingInfo(Context context, ChannelDescriptor channel) { if (channel == null) { return null; } Uri uri = TvContract.buildProgramsUriForChannel(channel.getChannelId()); String time = String.valueOf(System.currentTimeMillis()); String selection = Programs.COLUMN_START_TIME_UTC_MILLIS + "<=? AND " + Programs.COLUMN_END_TIME_UTC_MILLIS + ">?"; String[] args = { time, time }; Cursor cursor = null; //Sometimes TIF service will throw exception when trying to use selection on programs table try { cursor = context.getContentResolver().query(uri, null, selection, args, null); } catch (Exception e) { e.printStackTrace(); } long eventEndTime = 0; String eventRating = null; mLog.d("CRR - id=" + channel.getChannelId()); mLog.d("CRR - time=" + time); if (cursor == null) { mLog.d("CRR - no cursor!"); } else { mLog.d("CRR - count=" + cursor.getCount()); if (cursor.moveToFirst()) { eventEndTime = cursor.getLong(cursor.getColumnIndex( Programs.COLUMN_END_TIME_UTC_MILLIS)); eventRating = cursor.getString(cursor.getColumnIndex( Programs.COLUMN_CONTENT_RATING)); mLog.d("CRR - event.title=" + cursor.getString(cursor.getColumnIndex(Programs.COLUMN_TITLE))); mLog.d("CRR - event.rating=" + eventRating); mLog.d("CRR - event.endTime=" + eventEndTime); return new RatingInfo( (eventRating == null) ? null : TvContentRating.unflattenFromString(eventRating), eventEndTime); } cursor.close(); } return null; }