Update calendar instances.end via events.dtend - android

When I update the CalendarContract.Events DTEND column, why doesn't the change show up in the CalendarContract.Instances END column?
My app allows the user to view and change calendar events using the CalendarContract.Events APIs. The code performs an update to the Events table and then reads it back (later) using the Instances table. Changes to TITLE, for example, work fine (that is, I update Events and can read back the change in Instances). Changes to Events.DTEND do show up in Instances.DTEND, but how can I get that update to also show up in Instances.END?
This is important since, evidently, the Android calendar app (and my app, too) uses Instances.BEGIN and Instances.END to determine what to display in the calendar.
Here is my update code:
ContentResolver cr = getContentResolver();
ContentValues values = new ContentValues();
values.put (Events.CALENDAR_ID, calendarId);
values.put (Events.TITLE, title);
values.put (Events.DTEND, eventEnd.getTimeInMillis());
String where = "_id =" + eventId +
" and " + CALENDAR_ID + "=" + calendarId;
int count = cr.update (Events.CONTENT_URI, values, where, null);
if (count != 1)
throw new IllegalStateException ("more than one row updated");
Thanks.

The solution turns out to be adding the start date:
ContentResolver cr = getContentResolver();
ContentValues values = new ContentValues();
values.put (Events.CALENDAR_ID, calendarId);
values.put (Events.TITLE, title);
values.put (Events.DTSTART, eventStart.getTimeInMillis());
values.put (Events.DTEND, eventEnd.getTimeInMillis());
String where = "_id =" + eventId +
" and " + CALENDAR_ID + "=" + calendarId;
int count = cr.update (Events.CONTENT_URI, values, where, null);
if (count != 1)
throw new IllegalStateException ("more than one row updated");
A word of caution: this case only shows how to update nonrecurring events. Nonrecurring events have a null RRULE.
I suspect what the provider code is doing is using only the values you provide instead of refetching the start date itself (obviously if the user changes the start date you would have to provide it anyway). This makes sense from the perspective of reducing db accesses. Too bad Google didn't document any of this.

Related

Update Android calendar events

I'm trying to update calendar events programmatically but I have some issues.
I'm using updating code from Google Android Documentation: https://developer.android.com/guide/topics/providers/calendar-provider.html#update-event
So here's my code :
ContentValues values = new ContentValues();
Uri updateUri = null;
// New end for event
values.put(CalendarContract.Events.DTEND, endMillis);
updateUri = ContentUris.withAppendedId(CalendarContract.Events.CONTENT_URI, eventID);
int rows = c.getContentResolver().update(updateUri, values, null, null);
Log.i("DEBUG_TAG", "Rows updated: " + rows);
But rows variable always returns 0 except when I go into apps settings, remove calendar storage, add a new calendar and a new event and then when I try to update it, it works only once. If I try to add another event and update it afterward, rows return 0 again.
Any idea?
Thanks
Please note that my app is min API level 17 (on which it doesn't work) but I tried on API level 25 and it works, so do you have any idea how to add support to my code for previous Android versions? I've also found that after some tests, it works from API level 21. Under, it doesn't.
EDIT :
I've found a solution, check my answer below.
Here's the solution that worked for me :
The eventID that I used was the result of the query with column CalendarContract.Events._ID but I had to used the ID provided by CalendarContract.Instances.EVENT_ID.
Indeed if I create a new event (the first one) it has _id = 1 and event_id = 1, and if I delete it from Android Calendar App and I create a new one with my app, it has also _id = 1 but event_id = 2.
So the update query failed if I use _id instead of event_id with withAppendedID().
Here's the code that worked for me. startMillis and endMillis are respectively your meeting start time and end time in milliseconds.
Uri.Builder eventsUriBuilder = CalendarContract.Instances.CONTENT_URI
.buildUpon();
ContentUris.appendId(eventsUriBuilder, startMillis);
ContentUris.appendId(eventsUriBuilder, endMillis);
Uri eventsUri = eventsUriBuilder.build();
String[] column = {CalendarContract.Instances.EVENT_ID, CalendarContract.Events.DTSTART, CalendarContract.Events.DTEND, CalendarContract.Events.TITLE};
Cursor cursor;
cursor = c.getContentResolver().query(eventsUri, column, CalendarContract.Events.CALENDAR_ID+"="+Constant.ID_CALENDAR, null, CalendarContract.Instances.DTSTART + " ASC");
if (cursor.moveToFirst()) {
do {
uri = ContentUris.withAppendedId(CalendarContract.Events.CONTENT_URI,cursor.getLong(0));
} while (cursor.moveToNext());
}
cursor.close();
c.getContentResolver().delete(uri, null, null);

How to bulk delete events from the Device calendar by app id, and not by an array of event ids

This is how I create calendar events from within my app:
for(CalendarEventDescriptor calendarEventDescriptor : calendarEventDescriptors.values()) {
if(calendarEventDescriptor.startMilliseconds>now){
values = new ContentValues();
values.put(CalendarContract.Events.DTSTART, calendarEventDescriptor.startMilliseconds);
values.put(CalendarContract.Events.DTEND, calendarEventDescriptor.endMilliseconds);
values.put(CalendarContract.Events.TITLE, calendarEventDescriptor.title);
values.put(CalendarContract.Events.DESCRIPTION, calendarEventDescriptor.description);
values.put(CalendarContract.Events.CALENDAR_ID, 1);
values.put(CalendarContract.Events.EVENT_TIMEZONE, timeZone);
uri = cr.insert(CalendarContract.Events.CONTENT_URI, values);
calendarEventDescriptor.eventId = Long.parseLong(uri.getLastPathSegment());
}
}
At the time or writing, I store an array of all the event Ids that I've created, so that when the user flicks a switch, I loop through them and delete them from the Calendar.
for(long eventId : eventIds) {
if(eventId>0){
Uri deleteUri = ContentUris.withAppendedId(CalendarContract.Events.CONTENT_URI, eventId);
rowsDeleted += application.getContentResolver().delete(deleteUri, null, null);
}
}
It occurred to me that it might be possible to put a custom value for one of the CalendarContract.Events. columns so that I can do the deletion for all of the events at once, and that I don't have to remember their ids (I always delete them all, never delete certain ones)
Is that possible and which CalendarContract.Events. column should I use and how do I do the deletion then?
Nice question! I agree, that extra-property in ContentValues is the way to go in this case.
I've done it by re-using CalendarContract.Events.CUSTOM_APP_PACKAGE, as something invisible for the user and, so far, no side effects found:
So I'm creating Events, like you do:
ContentResolver cr = getContentResolver();
ContentValues values = new ContentValues();
values.put(CalendarContract.Events.CALENDAR_ID, 1);
values.put(CalendarContract.Events.DTSTART, ...);
values.put(CalendarContract.Events.DTEND, ...);
values.put(CalendarContract.Events.TITLE, ...);
values.put(CalendarContract.Events.DESCRIPTION, ....);
values.put(CalendarContract.Events.CUSTOM_APP_PACKAGE, getApplicationContext().getPackageName());
values.put(CalendarContract.Events.EVENT_TIMEZONE, TimeZone.getDefault().getID());
cr.insert(CalendarContract.Events.CONTENT_URI, values);
And then once I need to delete all of them, I call:
Cursor cursor = cr
.query(CalendarContract.Events.CONTENT_URI,
new String[] { CalendarContract.Events._ID, CalendarContract.Events.CUSTOM_APP_PACKAGE },
null, null, null);
cursor.moveToFirst();
String idsToDelete = "";
for (int i = 0; i < cursor.getCount(); i++) {
// it might be also smart to check CALENDAR_ID here
if (getApplicationContext().getPackageName().equals(cursor.getString(1))) {
idsToDelete += String.format("_ID = %s OR ", cursor.getString(0));
}
cursor.moveToNext();
}
if (idsToDelete.endsWith(" OR ")) {
idsToDelete = idsToDelete.substring(0, idsToDelete.length()-4);
}
cr.delete(CalendarContract.Events.CONTENT_URI, idsToDelete, null);
I hope, it helps
You can use ContentProviderOperation with applyBatch to perform multiple deletes/inserts in a single transaction.
Check this SO answer for code sample.
after a long long research, I've got a sort of new doc of google calendar
you can add multiple args to a selection allowing you to find all desired rows and delete them all at once
val operationList = ArrayList<ContentProviderOperation>()
var contentProviderOperation: ContentProviderOperation
appointments.forEach {
contentProviderOperation = ContentProviderOperation
.newDelete(CalendarContract.Events.CONTENT_URI)
.withSelection(CalendarContract.Events.ORIGINAL_SYNC_ID + " =?", arrayOf(it.id))
.build()
operationList.add(contentProviderOperation)
}
contentResolver.applyBatch(CalendarContract.AUTHORITY, operationList)

CalendarContract Events : bad id inserted, Calendar corrupted in Android

I made a mistake on testing insert Events using CalendarContract.
I set my own _ID in a Events insert.
values.put(Events._ID, "156498713465");
Now, all my new events are created with a bad id (for exemple -535191590).
When I click to the event in the Google Calendar Application, it crash.
I have the same error as this thread :
Calendar corrupted in Android
I tried to delete all bad events :
activity.getContentResolver().delete(Events.CONTENT_URI, Events._ID + " > ? ",
new String[] { "10000" });
But when a new events are inserted, a bad id is generated.
My question is :
Where can I reset the Events Id sequence ?
Thanks,
Regards
Don't set the id; the following will do what you want:
ContentResolver cr = getContentResolver();
ContentValues values = new ContentValues();
values.put (Events.CALENDAR_ID, Long.toString(newCalendarId));
values.put (Events.DTSTART, dtStart);
values.put (Events.DTEND, dtEnd);
values.put (Events.EVENT_TIMEZONE, TimeZone.getDefault().getID());
values.put (Events.TITLE, title);
Uri uri = cr.insert (Events.CONTENT_URI, values);
// The returned uri will contain the eventId assigned by Events.

Android contacts tables update but do not get saved to SIM

I am trying to update the phone number associated with a contact on the SIM. I am able to update the Data table's row ok, but the change does not get propagated to the SIM. I dump out the Data row after it has been updated; the _ID is still the same, the phone (data1) col has the new phone number. When I powercycle the phone, the entry has the old value. The raw contact number of the contact has been incremented by 1. Any ideas on how to make the change persistent in the SIM? (Yes, I know some doc says access to SIM is not supported. But the Contacts app can do it - there must be a way for an app to update the SIM.) There may be typos - I had to (re)type the code by hand here.
Here is the code I use to update the row.
String rawId = "87"; // provided by caller; hardcoded here for brevity
String num = "212-222-3333"; // new phone #
ContentResolver cr = ...get actvity's resolver...
Uri uri = ContactsContract.Data.CONTENT_URI;
ContentValues cv = new ContentValues();
cv.put(ContactsContract.CommonDataKinds.Phone.Number, num);
String select = ContactsContract.Data.RAW_CONTACT_ID " + " = " + rawId + " and " +
ContactsContract.Data.MIMETYPE + " = '" +
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_ITEM + "'";
String[] selectArgs = {};
cr.update(uri, cv, select, selectArgs);
Catch exception and other details omitted.
Thanks for any clues!

Android - How to populate Content Provider only once on App install?

I have a database in my application that is used as a ContentProvider.
It holds settings values for the application, and when I install the application I
want it to add a hardcoded set of values just once.
This is how I am trying to do it at the minute.
if(settings.size()<= 0){
Settings s = new Settings("voipusernameTa", "xxxxxxxxx", "xxxxx",
"displayNameTa", "sip.networks.com", "sip.networks.com", "siprealmTa", 120);
addNewSettings(s);
}
And this is the addNewSettings method:
private void addNewSettings(Settings _settings) {
ContentResolver cr = getContentResolver();
String w = CiceroSettings._ID + " = " + _settings.get_Id();
Log.d("ADDNEW SETTINGS", "new setting id =" + _settings.get_Id());
Cursor c = cr.query(CiceroSettings.CONTENT_URI, null, w, null, null);
Log.d("CURSOR", "cursor created");
int dbCount = c.getCount();
c.close();
if (dbCount == 0){
ContentValues values = new ContentValues();
values.put(Settings._ID, _settings.get_Id());
values.put(Settings.VOIPUSERNAME, _settings.getVoipUserName());
values.put(Settings.VOIPAUTHID, _settings.getVoipAuthId());
values.put(Settings.PASSWORD, _settings.getPassword());
values.put(Settings.VOIPDISPLAYNAME, _settings.getVoipDisplayName());
values.put(Settings.SIPPROXYSERVER, _settings.getSipProxyServer());
values.put(Settings.SIPREGISTRAR, _settings.getSipRegistrar());
values.put(Settings.SIPREALM, _settings.getSipRealm());
values.put(Settings.EXPIRESTIME, Integer.toString(_settings.getExpiresTime()));
Log.d("CURSOR", "Values assigned");
cr.insert(CiceroSettings.CONTENT_URI, values);
Log.d("CURSOR", "Values inserted");
}
}
}
This works however each time the app starts it adds a new settings object and I only want it to app ONE object at the install of the application and thats it no more.
Has anyone a better solution for this? Or a reason why my code is flawed?
If you want to add default values to your SQL table, I think the best way would be to set them right after creating your table.
So if you are using SQLiteOpenHelper, in the onCreate method, you can call db.insert() right after creating your table so that those values will be created just once

Categories

Resources