newAds = new ArrayList<>();
for (Advertisement oldAd : oldAds) {
newAds.add(new Advertisement.Builder(oldAd)
.setStartTimeUtcMillis(oldAd.getStartTimeUtcMillis() + timeShift)
.setStopTimeUtcMillis(oldAd.getStopTimeUtcMillis() + timeShift)
.build());
}
internalProviderData.setAds(newAds);
}
/**
* Updates the system database, TvProvider, with the given programs.
*
* If there is any overlap between the given and existing programs, the existing ones
* will be updated with the given ones if they have the same title or replaced.
*
* @param channelUri The channel where the program info will be added.
* @param newPrograms A list of {@link Program} instances which includes program
* information.
*/
private void updatePrograms(Uri channelUri, List newPrograms) {
final int fetchedProgramsCount = newPrograms.size();
if (fetchedProgramsCount == 0) {
broadcastError(ERROR_NO_PROGRAMS);
return;
}
List oldPrograms = TvContractUtils.getPrograms(mContext.getContentResolver(),
channelUri);
Program firstNewProgram = newPrograms.get(0);
int oldProgramsIndex = 0;
int newProgramsIndex = 0;
// Skip the past programs. They will be automatically removed by the system.
for (Program program : oldPrograms) {
if (program.getEndTimeUtcMillis() < System.currentTimeMillis() ||
program.getEndTimeUtcMillis() < firstNewProgram.getStartTimeUtcMillis()) {
oldProgramsIndex++;
} else {
break;
}
}
// Compare the new programs with old programs one by one and update/delete the old one
// or insert new program if there is no matching program in the database.
ArrayList ops = new ArrayList<>();
if (isCancelled()) {
return;
}
while (newProgramsIndex < fetchedProgramsCount) {
Program oldProgram = oldProgramsIndex < oldPrograms.size()
? oldPrograms.get(oldProgramsIndex) : null;
Program newProgram = newPrograms.get(newProgramsIndex);
boolean addNewProgram = false;
if (oldProgram != null) {
if (oldProgram.equals(newProgram)) {
// Exact match. No need to update. Move on to the next programs.
oldProgramsIndex++;
newProgramsIndex++;
} else if (shouldUpdateProgramMetadata(oldProgram, newProgram)) {
// Partial match. Update the old program with the new one.
// NOTE: Use 'update' in this case instead of 'insert' and 'delete'. There
// could be application specific settings which belong to the old program.
ops.add(ContentProviderOperation.newUpdate(
TvContract.buildProgramUri(oldProgram.getId()))
.withValues(newProgram.toContentValues())
.build());
oldProgramsIndex++;
newProgramsIndex++;
} else if (oldProgram.getEndTimeUtcMillis()
< newProgram.getEndTimeUtcMillis()) {
// No match. Remove the old program first to see if the next program in
// {@code oldPrograms} partially matches the new program.
ops.add(ContentProviderOperation.newDelete(
TvContract.buildProgramUri(oldProgram.getId()))
.build());
oldProgramsIndex++;
} else {
// No match. The new program does not match any of the old programs. Insert
// it as a new program.
addNewProgram = true;
newProgramsIndex++;
}
} else {
// No old programs. Just insert new programs.
addNewProgram = true;
newProgramsIndex++;
}
if (addNewProgram) {
ops.add(ContentProviderOperation
.newInsert(TvContract.Programs.CONTENT_URI)
.withValues(newProgram.toContentValues())
.build());
}
// Throttle the batch operation not to cause TransactionTooLargeException.
if (ops.size() > BATCH_OPERATION_COUNT
|| newProgramsIndex >= fetchedProgramsCount) {
try {
mContext.getContentResolver().applyBatch(TvContract.AUTHORITY, ops);
} catch (RemoteException | OperationApplicationException e) {
Log.e(TAG, "Failed to insert programs.", e);
broadcastError(ERROR_DATABASE_INSERT);
return;
}
ops.clear();
}
}
}
}
}