use of android.content.ContentProviderOperation in project Etar-Calendar by Etar-Group.
the class EditEventHelper method saveRemindersWithBackRef.
/**
* Saves the reminders, if they changed. Returns true if operations to
* update the database were added. Uses a reference id since an id isn't
* created until the row is added.
*
* @param ops the array of ContentProviderOperations
* @param eventId the id of the event whose reminders are being updated
* @param reminderMinutes the array of reminders set by the user
* @param originalMinutes the original array of reminders
* @param forceSave if true, then save the reminders even if they didn't change
* @return true if operations to update the database were added
*/
public static boolean saveRemindersWithBackRef(ArrayList<ContentProviderOperation> ops, int eventIdIndex, ArrayList<ReminderEntry> reminders, ArrayList<ReminderEntry> originalReminders, boolean forceSave) {
// If the reminders have not changed, then don't update the database
if (reminders.equals(originalReminders) && !forceSave) {
return false;
}
// Delete all the existing reminders for this event
ContentProviderOperation.Builder b = ContentProviderOperation.newDelete(Reminders.CONTENT_URI);
b.withSelection(Reminders.EVENT_ID + "=?", new String[1]);
b.withSelectionBackReference(0, eventIdIndex);
ops.add(b.build());
ContentValues values = new ContentValues();
int len = reminders.size();
// Insert the new reminders, if any
for (int i = 0; i < len; i++) {
ReminderEntry re = reminders.get(i);
values.clear();
values.put(Reminders.MINUTES, re.getMinutes());
values.put(Reminders.METHOD, re.getMethod());
b = ContentProviderOperation.newInsert(Reminders.CONTENT_URI).withValues(values);
b.withValueBackReference(Reminders.EVENT_ID, eventIdIndex);
ops.add(b.build());
}
return true;
}
use of android.content.ContentProviderOperation in project Etar-Calendar by Etar-Group.
the class EditEventHelper method saveEvent.
/**
* Saves the event. Returns true if the event was successfully saved, false
* otherwise.
*
* @param model The event model to save
* @param originalModel A model of the original event if it exists
* @param modifyWhich For recurring events which type of series modification to use
* @return true if the event was successfully queued for saving
*/
public boolean saveEvent(CalendarEventModel model, CalendarEventModel originalModel, int modifyWhich) {
boolean forceSaveReminders = false;
if (DEBUG) {
Log.d(TAG, "Saving event model: " + model);
}
if (!mEventOk) {
if (DEBUG) {
Log.w(TAG, "Event no longer exists. Event was not saved.");
}
return false;
}
// modifying an existing event and we have the wrong original model
if (model == null) {
Log.e(TAG, "Attempted to save null model.");
return false;
}
if (!model.isValid()) {
Log.e(TAG, "Attempted to save invalid model.");
return false;
}
if (originalModel != null && !isSameEvent(model, originalModel)) {
Log.e(TAG, "Attempted to update existing event but models didn't refer to the same " + "event.");
return false;
}
if (originalModel != null && model.isUnchanged(originalModel)) {
return false;
}
ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
int eventIdIndex = -1;
ContentValues values = getContentValuesFromModel(model);
if (model.mUri != null && originalModel == null) {
Log.e(TAG, "Existing event but no originalModel provided. Aborting save.");
return false;
}
Uri uri = null;
if (model.mUri != null) {
uri = Uri.parse(model.mUri);
}
// Update the "hasAlarm" field for the event
ArrayList<ReminderEntry> reminders = model.mReminders;
int len = reminders.size();
values.put(Events.HAS_ALARM, (len > 0) ? 1 : 0);
if (uri == null) {
// Add hasAttendeeData for a new event
values.put(Events.HAS_ATTENDEE_DATA, 1);
values.put(Events.STATUS, Events.STATUS_CONFIRMED);
eventIdIndex = ops.size();
ContentProviderOperation.Builder b = ContentProviderOperation.newInsert(Events.CONTENT_URI).withValues(values);
ops.add(b.build());
forceSaveReminders = true;
} else if (TextUtils.isEmpty(model.mRrule) && TextUtils.isEmpty(originalModel.mRrule)) {
// Simple update to a non-recurring event
checkTimeDependentFields(originalModel, model, values, modifyWhich);
ops.add(ContentProviderOperation.newUpdate(uri).withValues(values).build());
} else if (TextUtils.isEmpty(originalModel.mRrule)) {
// This event was changed from a non-repeating event to a
// repeating event.
ops.add(ContentProviderOperation.newUpdate(uri).withValues(values).build());
} else if (modifyWhich == MODIFY_SELECTED) {
// Modify contents of the current instance of repeating event
// Create a recurrence exception
long begin = model.mOriginalStart;
values.put(Events.ORIGINAL_SYNC_ID, originalModel.mSyncId);
values.put(Events.ORIGINAL_INSTANCE_TIME, begin);
boolean allDay = originalModel.mAllDay;
values.put(Events.ORIGINAL_ALL_DAY, allDay ? 1 : 0);
values.put(Events.STATUS, originalModel.mEventStatus);
eventIdIndex = ops.size();
ContentProviderOperation.Builder b = ContentProviderOperation.newInsert(Events.CONTENT_URI).withValues(values);
ops.add(b.build());
forceSaveReminders = true;
} else if (modifyWhich == MODIFY_ALL_FOLLOWING) {
if (TextUtils.isEmpty(model.mRrule)) {
// to end at the new start time.
if (isFirstEventInSeries(model, originalModel)) {
ops.add(ContentProviderOperation.newDelete(uri).build());
} else {
// Update the current repeating event to end at the new start time. We
// ignore the RRULE returned because the exception event doesn't want one.
updatePastEvents(ops, originalModel, model.mOriginalStart);
}
eventIdIndex = ops.size();
values.put(Events.STATUS, originalModel.mEventStatus);
ops.add(ContentProviderOperation.newInsert(Events.CONTENT_URI).withValues(values).build());
} else {
if (isFirstEventInSeries(model, originalModel)) {
checkTimeDependentFields(originalModel, model, values, modifyWhich);
ContentProviderOperation.Builder b = ContentProviderOperation.newUpdate(uri).withValues(values);
ops.add(b.build());
} else {
// We need to update the existing recurrence to end before the exception
// event starts. If the recurrence rule has a COUNT, we need to adjust
// that in the original and in the exception. This call rewrites the
// original event's recurrence rule (in "ops"), and returns a new rule
// for the exception. If the exception explicitly set a new rule, however,
// we don't want to overwrite it.
String newRrule = updatePastEvents(ops, originalModel, model.mOriginalStart);
if (model.mRrule.equals(originalModel.mRrule)) {
values.put(Events.RRULE, newRrule);
}
// Create a new event with the user-modified fields
eventIdIndex = ops.size();
values.put(Events.STATUS, originalModel.mEventStatus);
ops.add(ContentProviderOperation.newInsert(Events.CONTENT_URI).withValues(values).build());
}
}
forceSaveReminders = true;
} else if (modifyWhich == MODIFY_ALL) {
// Modify all instances of repeating event
if (TextUtils.isEmpty(model.mRrule)) {
// We've changed a recurring event to a non-recurring event.
// Delete the whole series and replace it with a new
// non-recurring event.
ops.add(ContentProviderOperation.newDelete(uri).build());
eventIdIndex = ops.size();
ops.add(ContentProviderOperation.newInsert(Events.CONTENT_URI).withValues(values).build());
forceSaveReminders = true;
} else {
checkTimeDependentFields(originalModel, model, values, modifyWhich);
ops.add(ContentProviderOperation.newUpdate(uri).withValues(values).build());
}
}
// New Event or New Exception to an existing event
boolean newEvent = (eventIdIndex != -1);
ArrayList<ReminderEntry> originalReminders;
if (originalModel != null) {
originalReminders = originalModel.mReminders;
} else {
originalReminders = new ArrayList<ReminderEntry>();
}
if (newEvent) {
saveRemindersWithBackRef(ops, eventIdIndex, reminders, originalReminders, forceSaveReminders);
} else if (uri != null) {
long eventId = ContentUris.parseId(uri);
saveReminders(ops, eventId, reminders, originalReminders, forceSaveReminders);
}
ContentProviderOperation.Builder b;
boolean hasAttendeeData = model.mHasAttendeeData;
if (hasAttendeeData && model.mOwnerAttendeeId == -1) {
// Organizer is not an attendee
String ownerEmail = model.mOwnerAccount;
if (model.mAttendeesList.size() != 0 && Utils.isValidEmail(ownerEmail)) {
// Add organizer as attendee since we got some attendees
values.clear();
values.put(Attendees.ATTENDEE_EMAIL, ownerEmail);
values.put(Attendees.ATTENDEE_RELATIONSHIP, Attendees.RELATIONSHIP_ORGANIZER);
values.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_REQUIRED);
values.put(Attendees.ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_ACCEPTED);
if (newEvent) {
b = ContentProviderOperation.newInsert(Attendees.CONTENT_URI).withValues(values);
b.withValueBackReference(Attendees.EVENT_ID, eventIdIndex);
} else {
values.put(Attendees.EVENT_ID, model.mId);
b = ContentProviderOperation.newInsert(Attendees.CONTENT_URI).withValues(values);
}
ops.add(b.build());
}
} else if (hasAttendeeData && model.mSelfAttendeeStatus != originalModel.mSelfAttendeeStatus && model.mOwnerAttendeeId != -1) {
if (DEBUG) {
Log.d(TAG, "Setting attendee status to " + model.mSelfAttendeeStatus);
}
Uri attUri = ContentUris.withAppendedId(Attendees.CONTENT_URI, model.mOwnerAttendeeId);
values.clear();
values.put(Attendees.ATTENDEE_STATUS, model.mSelfAttendeeStatus);
values.put(Attendees.EVENT_ID, model.mId);
b = ContentProviderOperation.newUpdate(attUri).withValues(values);
ops.add(b.build());
}
// a new event or an existing event. or is this a paranoia check?
if (hasAttendeeData && (newEvent || uri != null)) {
String attendees = model.getAttendeesString();
String originalAttendeesString;
if (originalModel != null) {
originalAttendeesString = originalModel.getAttendeesString();
} else {
originalAttendeesString = "";
}
// has changed it
if (newEvent || !TextUtils.equals(originalAttendeesString, attendees)) {
// figure out which attendees need to be added and which ones
// need to be deleted. use a linked hash set, so we maintain
// order (but also remove duplicates).
HashMap<String, Attendee> newAttendees = model.mAttendeesList;
LinkedList<String> removedAttendees = new LinkedList<String>();
// the eventId is only used if eventIdIndex is -1.
// TODO: clean up this code.
long eventId = uri != null ? ContentUris.parseId(uri) : -1;
// have any existing attendees.
if (!newEvent) {
removedAttendees.clear();
HashMap<String, Attendee> originalAttendees = originalModel.mAttendeesList;
for (String originalEmail : originalAttendees.keySet()) {
if (newAttendees.containsKey(originalEmail)) {
// existing attendee. remove from new attendees set.
newAttendees.remove(originalEmail);
} else {
// no longer in attendees. mark as removed.
removedAttendees.add(originalEmail);
}
}
// delete removed attendees if necessary
if (removedAttendees.size() > 0) {
b = ContentProviderOperation.newDelete(Attendees.CONTENT_URI);
String[] args = new String[removedAttendees.size() + 1];
args[0] = Long.toString(eventId);
int i = 1;
StringBuilder deleteWhere = new StringBuilder(ATTENDEES_DELETE_PREFIX);
for (String removedAttendee : removedAttendees) {
if (i > 1) {
deleteWhere.append(",");
}
deleteWhere.append("?");
args[i++] = removedAttendee;
}
deleteWhere.append(")");
b.withSelection(deleteWhere.toString(), args);
ops.add(b.build());
}
}
if (newAttendees.size() > 0) {
// Insert the new attendees
for (Attendee attendee : newAttendees.values()) {
values.clear();
values.put(Attendees.ATTENDEE_NAME, attendee.mName);
values.put(Attendees.ATTENDEE_EMAIL, attendee.mEmail);
values.put(Attendees.ATTENDEE_RELATIONSHIP, Attendees.RELATIONSHIP_ATTENDEE);
values.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_REQUIRED);
values.put(Attendees.ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_NONE);
if (newEvent) {
b = ContentProviderOperation.newInsert(Attendees.CONTENT_URI).withValues(values);
b.withValueBackReference(Attendees.EVENT_ID, eventIdIndex);
} else {
values.put(Attendees.EVENT_ID, eventId);
b = ContentProviderOperation.newInsert(Attendees.CONTENT_URI).withValues(values);
}
ops.add(b.build());
}
}
}
}
mService.startBatch(mService.getNextToken(), null, android.provider.CalendarContract.AUTHORITY, ops, Utils.UNDO_DELAY);
return true;
}
use of android.content.ContentProviderOperation in project Etar-Calendar by Etar-Group.
the class EditEventHelper method updatePastEvents.
/**
* Prepares an update to the original event so it stops where the new series
* begins. When we update 'this and all following' events we need to change
* the original event to end before a new series starts. This creates an
* update to the old event's rrule to do that.
*<p>
* If the event's recurrence rule has a COUNT, we also need to reduce the count in the
* RRULE for the exception event.
*
* @param ops The list of operations to add the update to
* @param originalModel The original event that we're updating
* @param endTimeMillis The time before which the event must end (i.e. the start time of the
* exception event instance).
* @return A replacement exception recurrence rule.
*/
public String updatePastEvents(ArrayList<ContentProviderOperation> ops, CalendarEventModel originalModel, long endTimeMillis) {
boolean origAllDay = originalModel.mAllDay;
String origRrule = originalModel.mRrule;
String newRrule = origRrule;
EventRecurrence origRecurrence = new EventRecurrence();
origRecurrence.parse(origRrule);
// Get the start time of the first instance in the original recurrence.
long startTimeMillis = originalModel.mStart;
Time dtstart = new Time();
dtstart.timezone = originalModel.mTimezone;
dtstart.set(startTimeMillis);
ContentValues updateValues = new ContentValues();
if (origRecurrence.count > 0) {
/*
* Generate the full set of instances for this recurrence, from the first to the
* one just before endTimeMillis. The list should never be empty, because this method
* should not be called for the first instance. All we're really interested in is
* the *number* of instances found.
*
* TODO: the model assumes RRULE and ignores RDATE, EXRULE, and EXDATE. For the
* current environment this is reasonable, but that may not hold in the future.
*
* TODO: if COUNT is 1, should we convert the event to non-recurring? e.g. we
* do an "edit this and all future events" on the 2nd instances.
*/
RecurrenceSet recurSet = new RecurrenceSet(originalModel.mRrule, null, null, null);
RecurrenceProcessor recurProc = new RecurrenceProcessor();
long[] recurrences;
try {
recurrences = recurProc.expand(dtstart, recurSet, startTimeMillis, endTimeMillis);
} catch (DateException de) {
throw new RuntimeException(de);
}
if (recurrences.length == 0) {
throw new RuntimeException("can't use this method on first instance");
}
EventRecurrence excepRecurrence = new EventRecurrence();
// TODO: add+use a copy constructor instead
excepRecurrence.parse(origRrule);
excepRecurrence.count -= recurrences.length;
newRrule = excepRecurrence.toString();
origRecurrence.count = recurrences.length;
} else {
// The "until" time must be in UTC time in order for Google calendar
// to display it properly. For all-day events, the "until" time string
// must include just the date field, and not the time field. The
// repeating events repeat up to and including the "until" time.
Time untilTime = new Time();
untilTime.timezone = Time.TIMEZONE_UTC;
// Subtract one second from the old begin time to get the new
// "until" time.
// subtract one second (1000 millis)
untilTime.set(endTimeMillis - 1000);
if (origAllDay) {
untilTime.hour = 0;
untilTime.minute = 0;
untilTime.second = 0;
untilTime.allDay = true;
untilTime.normalize(false);
// This should no longer be necessary -- DTSTART should already be in the correct
// format for an all-day event.
dtstart.hour = 0;
dtstart.minute = 0;
dtstart.second = 0;
dtstart.allDay = true;
dtstart.timezone = Time.TIMEZONE_UTC;
}
origRecurrence.until = untilTime.format2445();
}
updateValues.put(Events.RRULE, origRecurrence.toString());
updateValues.put(Events.DTSTART, dtstart.normalize(true));
ContentProviderOperation.Builder b = ContentProviderOperation.newUpdate(Uri.parse(originalModel.mUri)).withValues(updateValues);
ops.add(b.build());
return newRrule;
}
use of android.content.ContentProviderOperation in project Etar-Calendar by Etar-Group.
the class AsyncQueryServiceTest method testBatch.
@SmallTest
public void testBatch() throws Exception {
int index = 0;
final OperationInfo[] work = new OperationInfo[1];
work[index] = new OperationInfo();
work[index].op = Operation.EVENT_ARG_BATCH;
work[index].token = ++mId;
work[index].cookie = ++mId;
work[index].authority = AUTHORITY;
work[index].cpo = new ArrayList<ContentProviderOperation>();
work[index].cpo.add(ContentProviderOperation.newInsert(Uri.parse(AUTHORITY_URI + ++mId)).build());
work[index].delayMillis = 0;
ContentProviderResult[] resultArray = new ContentProviderResult[1];
resultArray[0] = new ContentProviderResult(++mId);
work[index].result = resultArray;
TestAsyncQueryService aqs = new TestAsyncQueryService(buildTestContext(work), work);
aqs.startBatch(work[index].token, work[index].cookie, work[index].authority, work[index].cpo, work[index].delayMillis);
Log.d(TAG, "testBatch Waiting >>>>>>>>>>>");
assertEquals("Not all operations were executed.", work.length, aqs.waitForCompletion(BASE_TEST_WAIT_TIME));
Log.d(TAG, "testBatch Done <<<<<<<<<<<<<<");
}
use of android.content.ContentProviderOperation in project YourAppIdea by Michenux.
the class TutorialSyncAdapter method updateDatabase.
private void updateDatabase(List<WPJsonPost> posts, ContentProviderClient provider) throws RemoteException, OperationApplicationException, ParseException {
ArrayList<ContentProviderOperation> ops = new ArrayList<>();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.FRENCH);
sdf.setTimeZone(TimeZone.getTimeZone("Europe/Paris"));
for (WPJsonPost post : posts) {
String thumbnail = "";
if (post.getThumbnailImages() != null && post.getThumbnailImages().getFoundationFeaturedImage() != null) {
thumbnail = post.getThumbnailImages().getFoundationFeaturedImage().getUrl();
}
Date postCreationDate = sdf.parse(post.getDate());
Date postModifDate = sdf.parse(post.getDate());
Cursor cursor = getContext().getContentResolver().query(// The content URI of the words table
TutorialContentProvider.CONTENT_URI, // The columns to return for each row
new String[] { TutorialContentProvider.DATEMODIFICATION_COLUMN }, // Selection criteria
TutorialContentProvider.POSTID_COLUMN + "= ?", // Selection criteria
new String[] { Integer.toString(post.getId()) }, null);
boolean insertOrModified = false;
if (cursor != null) {
if (cursor.moveToFirst()) {
long modificationDate = CursorUtils.getLong(TutorialContentProvider.DATEMODIFICATION_COLUMN, cursor);
if (modificationDate != (postModifDate.getTime() / 1000)) {
if (BuildConfig.DEBUG) {
Log.d(YourApplication.LOG_TAG, "updated post: " + post.getId());
}
// delete the old one if modified
ops.add(ContentProviderOperation.newDelete(TutorialContentProvider.CONTENT_URI).withSelection(TutorialContentProvider.POSTID_COLUMN + " = ?", new String[] { Integer.toString(post.getId()) }).build());
insertOrModified = true;
} else {
if (BuildConfig.DEBUG) {
Log.d(YourApplication.LOG_TAG, "unchanged post: " + post.getId() + ", not inserting.");
}
}
} else {
if (BuildConfig.DEBUG) {
Log.d(YourApplication.LOG_TAG, "new post: " + post.getId());
}
insertOrModified = true;
}
cursor.close();
}
if (insertOrModified) {
// insert if new or modified
ops.add(ContentProviderOperation.newInsert(TutorialContentProvider.CONTENT_URI).withValue(TutorialContentProvider.POSTID_COLUMN, post.getId()).withValue(TutorialContentProvider.TITLE_COLUMN, post.getTitle()).withValue(TutorialContentProvider.DESCRIPTION_COLUMN, post.getExcerpt()).withValue(TutorialContentProvider.THUMBNAIL_COLMUN, thumbnail).withValue(TutorialContentProvider.URL_COLUMN, post.getUrl()).withValue(TutorialContentProvider.CONTENT_COLUMN, post.getContent()).withValue(TutorialContentProvider.AUTHOR_COLUMN, post.getAuthor().getName()).withValue(TutorialContentProvider.DATECREATION_COLUMN, postCreationDate.getTime() / 1000).withValue(TutorialContentProvider.DATEMODIFICATION_COLUMN, postModifDate.getTime() / 1000).withYieldAllowed(false).build());
}
}
// Keep last 50 posts
String deleteSelection = TutorialContentProvider.ID_COLUMN + " not in ( select " + TutorialContentProvider.ID_COLUMN + " from " + TutorialContentProvider.TABLE_NAME + " order by " + TutorialContentProvider.DATECREATION_COLUMN + " desc limit 50 )";
String[] deleteParams = new String[] {};
int actuToDelete = ContentProviderUtils.count(TutorialContentProvider.CONTENT_URI, deleteSelection, deleteParams, this.getContext().getContentResolver());
if (BuildConfig.DEBUG) {
Log.d(YourApplication.LOG_TAG, actuToDelete + " old posts to delete");
}
if (actuToDelete > 0) {
ops.add(ContentProviderOperation.newDelete(TutorialContentProvider.CONTENT_URI).withSelection(deleteSelection, deleteParams).build());
}
if (!ops.isEmpty()) {
if (BuildConfig.DEBUG) {
Log.d(YourApplication.LOG_TAG, "execute batch");
}
provider.applyBatch(ops);
} else {
if (BuildConfig.DEBUG) {
Log.d(YourApplication.LOG_TAG, "ignore batch, empty");
}
}
}
Aggregations