Android : Deleting an occurrence of event deletes all of its occurrence - android

I know, I am not the first one who ask this. I found many questions in stack itself, like
Delete only one instance of a recurring event from my Android calendar
Android Calendar Provider exception on recurring events
Android Calendar Specific Event Deletion
but none of above solved the issue. Now to my code.
I am using calendar contract provider api for all operations (dont need support for older android versions). and its NOT A SYNC ADAPTER. We are successful in deleting all events (By deleting the events from event table itself).But when I try to delete an occurrence of event using the Events.CONTENT_EXCEPTION_URI (by inserting) All events are getting disappeared. Following is my code
ContentValues args = new ContentValues();
args.put(Events.TITLE, eNote.getEventTitle());
args.put(Events.DTSTART, eNote.getStartTimeMill());
args.put(CalendarContract.Events.ORIGINAL_INSTANCE_TIME, eNote.getStartTimeMill());
args.put(Events.ORIGINAL_SYNC_ID, 1);
args.put(Events.HAS_ALARM, "0");
args.put(Events.HAS_ATTENDEE_DATA,"0");
args.put(CalendarContract.Events.EVENT_TIMEZONE, eNote.getTimeZone());
args.put(CalendarContract.Events.STATUS, CalendarContract.Events.STATUS_CANCELED);
Uri.Builder eventUriBuilder = CalendarContract.Events.CONTENT_EXCEPTION_URI.buildUpon();
ContentUris.appendId(eventUriBuilder, eventID);
try {
final Uri resultUri = activity.getContentResolver().insert(eventUriBuilder.build(), args);
int eventIDNew = Integer.parseInt(resultUri.getLastPathSegment());
Log.i(Global.DEBUG_TAG,"eventIDNew : " +eventIDNew);
} catch (Exception e) {
e.printStackTrace();
Log.i(Global.DEBUG_TAG,
"Eroor : " +e.getMessage() );
}
eNote is an object stores the details of an event from the instance table,
given the value args.put(Events.ORIGINAL_SYNC_ID, 1); directly, As I am not setting the sync id while creating the event and we don't need any sync operations
Insertion to the exception uri returns a new ID but this makes all events get disappeared.
what is wrong with the code..
please helps us, we welcomes all suggestions and advance thanks to all...

Here are the basics (as I've answered here)
Find the instance you want to delete. (using Instances.query())
Create the exception URI with the event ID appended.
Create ContentValues. Put your instance's BEGIN value as ...Events.ORIGINAL_INSTANCE_TIME. Put STATUS_CANCELED as ...Events.STATUS
Now only insert(yourURI, yourValues) and that's it!

Related

Adding birthday to a contact creates 2 entries after sync

I have a very weired problem with my app while I try to add a birthday event on a picked contact.
My code for this is:
int mret = np2.getValue()+1;
ContentResolver cr = v.getContext().getContentResolver();
ContentValues values = new ContentValues();
values.put(ContactsContract.RawContacts.Data.MIMETYPE, ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE);
values.put(ContactsContract.RawContacts.Data.RAW_CONTACT_ID, rawContactId);
values.put(ContactsContract.CommonDataKinds.Event.TYPE, ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY);
if (boolyear == true) {
values.put(ContactsContract.CommonDataKinds.Event.START_DATE, np3.getValue() + "-" + mret + "-" + np1.getValue());
} else {
values.put(ContactsContract.CommonDataKinds.Event.START_DATE, "0000-" + mret + "-" + np1.getValue());
}
Uri bduri= null;
try {
bduri= cr.insert(ContactsContract.Data.CONTENT_URI,values);
} catch (Exception e) {}
The above code works fine as it creates the birthday event but a few seconds later appears a second birthday entry which possibly is a result of sync as it happens only if there is an Internet connection.
This is not a problem of the device because other apps downloaded from Play Store work as expected. It's only my app that creates double birthday entries.
Why is this happening and how to fix that?
Thank you in advance.
UPDATE: I managed to fix it. The problem was the date format. All values (day and month) must be in a two-digit format. E.g. "1980-07-01", not "1980-7-1".
I managed to fix it. The problem was the date format. All values (day and month) must be in a two-digit format. E.g. "1980-07-01", not "1980-7-1".
I am assuming that you are using a Sync Adapter to interface with the Calendar.
The Sync Adapter has two important methods:
onPerformSync(...), and onSyncCanceled(...)
onPerformSync is called whenever you make a ContentResolver.requestSync call or if automatic sync is set to true.
While executing, onPerformSync can be interrupted by Android. This can happen if the device is running low on resources of if your app is not in the foreground anymore.
When interupted, the Sync Adapter will stop execution where it's at and will call onSyncCanceled. The default behavior of the Sync Adapter is to retry the failed sync messages the first chance it gets. So if your app manages to grab onto OS resources again, it will replay the interrupted sync message.
It is possible that some of your messages are being fully processed and, just before onPerformSync is about to complete, the Sync Adapter is interrupted. At this point you managed to save the event, however, the Sync Adapter believes that the sync failed, and therefore will replay the same message again the next time it tries to sync.
I am not sure if you are syncing one event at a time so I can't offer a definitive solution. However, what you can do is change the default behaviour of the Sync Adapter to stop it from replaying "failed" messages.
Bundle extras = new Bundle();
...
extras.putBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, true);
CalendarConstants.AUTHORITY, true);
ContentResolver.requestSync(mAccount, CalendarConstants.AUTHORITY, extras);
In OnSyncCanceled, you can do some light checks to make sure the message succeeded, i.e. is present in the calendar's events table. If not, then manually replay it. If it is present, the SyncAdapter will simply move on to the next message, and you won't get any duplicates.
Alternatively, you could make sure all operations within onPerformSync are atomic.

Firebase last action time in specified location

I keep a local copy of a simple database in user's phone. This way the app can be used offline. I want to check with firebase server from time to time and update the local database if there is any change. So I need to know last action (insert, update, delete, etc.) time in a specified location in Firebase database. Is that possible? Or should I implement my own mechanism?
The Firebase Database does not store informations like the timestamp for CRUD operations that are performed. Because of that, you need to store this kind of data yourself with your own mechanism. So, you need to create a new field for each child you want to trace and change the value of the TIMESTAMP every time a action is performed. The best practice is to save your data as a TIMESTAMP like this: ServerValue.TIMESTAMP. Note, that when you are saving the TIMESTAMP, you are saving as a Map and when you are retrieving, you are retrieving it as a long. To set the TIMESTAMP, i recomand you using the following code:
DatabaseReference ref = FirebaseDatabase.getInstance().getReference();
Map map = new HashMap();
map.put("time", ServerValue.TIMESTAMP);
ref.child("yourNode").updateChildren(map);
To get you data back, i recomand you using the following method:
public static String getTimeDate(long timeStamp){
try{
DateFormat dateFormat = getDateTimeInstance();
Date netDate = (new Date(timeStamp));
return dateFormat.format(netDate);
} catch(Exception e) {
return "date";
}
}
This is possible but you shouldn't
Firebase already has offline capabilities, is very simple to use. Is a 2 steps process:
Set offline capabilities on
Set what you want to keep track ass offline
This is the official documentation
In code this is done something like this:
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
DatabaseReference scoresRef = FirebaseDatabase.getInstance().getReference("scores");
scoresRef.keepSynced(true);
There is big temptation to use:
FirebaseDatabase.getInstance().getReference().keepSynced(true);
Trying to keep everything sync by syncing the root reference doesn't work, you have to be specific, at least at 1 level parent.
The syncing when the connection is resumed is automatic, the user has to open the app though, flawless.
Now, if you still insist on doing this, then what you have to do is set ServeValue.TimeStamp
In the same link provided above, you can find how to set the server timestamp, here is the direct reference.
Later you can sort by that timestamp, here is a more detailed answer

Make calendar entry readonly

I inserted a calender contract entry like this:
ContentValues values = new ContentValues();
values.put(CalendarContract.Calendars.ACCOUNT_NAME, account.name);
values.put(CalendarContract.Calendars.ACCOUNT_TYPE, account.type);
values.put(CalendarContract.Calendars.NAME, name);
values.put(CalendarContract.Calendars.CALENDAR_DISPLAY_NAME, getDisplayName(account));
values.put(CalendarContract.Calendars.CALENDAR_COLOR, 0xffff0000);
values.put(CalendarContract.Calendars.CALENDAR_ACCESS_LEVEL, CalendarContract.Calendars.CAL_ACCESS_READ);
values.put(CalendarContract.Calendars.OWNER_ACCOUNT, getMailAddressOf(account));
values.put(CalendarContract.Calendars.CALENDAR_TIME_ZONE, TimeZone.getTimeZone("GMT").getDisplayName());
values.put(CalendarContract.Calendars.SYNC_EVENTS, 1);
Uri.Builder builder = CalendarContract.Calendars.CONTENT_URI.buildUpon();
builder.appendQueryParameter(CalendarContract.Calendars.ACCOUNT_NAME, account.name);
builder.appendQueryParameter(CalendarContract.Calendars.ACCOUNT_TYPE, account.type);
builder.appendQueryParameter(CalendarContract.CALLER_IS_SYNCADAPTER, "true");
Uri result = getContext().getContentResolver().insert(builder.build(), values);
This works great, I can enter calender entries etc. But I need to make it now read only so that the user cannot edit the calendar entry.
I thought that when I set CALENDAR_ACCESS_LEVEL to CAL_ACCESS_READ it should be enough to make the calendar read only.
Any idea how to achieve that? By the way I'm testing on a Pixel with Android O.
Don't make the user to be the organizer. When adding the event, the default organizer will be the user. To change it, add this line to the ContentValues when adding the event, assign an organizer email address like this
values.put(CalendarContract.Events.ORGANIZER,"an_organizer_name#gmail.com";
Since the user is not the organizer, the user can still delete the event but not able to edit the event content such as time, title, description, etc.
Well that took some time. In the end I found out that I have to change the mail address of the CalendarContract.Events.ORGANIZER value to prevent that I can edit the calender entry. Just as a hint for others how I sloved the problem I tried a lot of stupid things but then I had the idea to check how it is done in AOSP. So I just searched for "calendar aosp code" and found the code mirrowed on GitHub. After using the code search within the repo I found the method canModifyEvent(...). Now it became oblivious:
public static boolean canModifyEvent(CalendarEventModel model) {
return canModifyCalendar(model)
&& (model.mIsOrganizer || model.mGuestsCanModify);
}
Make sure that the event cannot been edited by guests and by don't be the organizer.
Happy Coding!

How do I remove a reminder from an event so it updates the event in the provider not just the sqlite db?

I tried deleting a reminder for an event by using this code:
int result = getContentResolver().delete(
Reminders.CONTENT_URI,
Reminders.EVENT_ID + " = ?",
new String[] { "44" }
);
This shows it deleted 1 row. However, when I view the event in the calendar app, the reminder is still there. Even if I updated the "hasAlarm" field, I don't think it will update the event in Exchange. How do I properly (using Ice Cream Sandwich - API 14 - or later) remove a reminder from a calendar event?
Instead of deleting , try with update query , it works . IF it doesn't work lemme know i'll try to help with code snippet.

What is the correct URI to pass to a ContentObserver for sent SMSes in Jelly Bean?

I'm trying to tally the number of outgoing SMSes sent by the user. Currently, my code goes like this:
class SentSMSObserver extends ContentObserver {
....
}
SentSMSObserver sso = new SentSMSObserver(new Handler());
ContentResolver cr = this.getContentResolver();
cr.registerContentObserver(Uri.parse("content://sms"), true, sso);
I tried to run the app in an emulator sporting Jelly Bean and for some reason, when I send a text message via the native SMS app, it increments my tally by three.
Now, I decided to replace content://sms with content://sms/sent as the related StackOverflow q&a's would suggest, but when I run the app with that setting, it never even executes my content observer's onChange(). What do I do?
the callback on registerContentObserver will be called when the db updater calls the exact uri for which you have registered. But in your case the MMS app does not use content://sms/sent but uses something like content://sms/10 and keeps moving it from type QUEUED to OUTBOX and then to SENT . So i guess thats why you are recieving 3 updates when you register for content://sms/
MMS App changes the TYPE column each time after it is queued.
One way to solve this problem is each time you get the content change callback query for the uri and then get "type" column and check if its value is Telephony.Sms.MESSAGE_TYPE_SENT .
From the answer suggested by Lalit Poptani, you need to get the value of the "type" of record the cursor is currently pointing at.
// cr is the content resolver
Cursor cursor = cr.query(Uri.parse("content://sms"), null, null, null, null);
int columnIndex = cursor.getColumnIndex("type");
while (cursor.moveToNext()) {
int type = cursor.getInt(columnIndex);
if (type == 2) {
// do your stuff here
}
}
As mentioned in my discussion with nandeesh, the message types are not part of the public API, but they can be found here.
Currently, I'm fixing a bug where Android makes inaccurate counts of a single sent SMS message when it is directed to more than one recipient (sending to two people counts three, sending to three counts 5). It seems that even when you filter out the message types, the content observer gets invoked more than once even for just a single message. I will post updates to this answer when I've fixed it.

Categories

Resources