Adding Events to Calendar without user confirmation - android

I'm trying to write a class to add events to a user's calendar in the background (not using intents). I need to ultimately be able to add as many as 7 events in a loop, so I can't do this with the approach where we turn control over to the calendar for the user to confirm.
Unfortunately, I'm not getting anywhere. The only feedback that I get from the logcat is a warning: Cursor finalized without prior close()
#TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public class AddToCalendar {
Context context;
// Projection array. Creating indices for this array instead of doing
// dynamic lookups improves performance.
public static final String[] EVENT_PROJECTION = new String[] {
Calendars._ID, // 0
Calendars.ACCOUNT_NAME, // 1
Calendars.CALENDAR_DISPLAY_NAME, // 2
Calendars.OWNER_ACCOUNT // 3
};
// The indices for the projection array above.
private static final int PROJECTION_ID_INDEX = 0;
private static final int PROJECTION_ACCOUNT_NAME_INDEX = 1;
private static final int PROJECTION_DISPLAY_NAME_INDEX = 2;
private static final int PROJECTION_OWNER_ACCOUNT_INDEX = 3;
public AddToCalendar (Context context) {
this.context = context;
}
public Cursor getCalendars() {
Cursor cursor = null;
ContentResolver cr = context.getContentResolver();
Uri uri = Calendars.CONTENT_URI;
String selection = "((" + Calendars.ACCOUNT_NAME + " = ?) AND ("
+ Calendars.ACCOUNT_TYPE + " = ?) AND ("
+ Calendars.OWNER_ACCOUNT + " = ?))";
String[] selectionArgs = new String[] {"sampleuser#gmail.com", "com.google",
"sampleuser#gmail.com"};
// Submit the query and get a Cursor object back.
cursor = cr.query(uri, EVENT_PROJECTION, selection, selectionArgs, null);
return cursor;
}
public void addEvent(Cursor cursor, String type, String location, String description, Date workout_date) {
System.err.println("adding event");
while (cursor.moveToNext()) {
String displayName = null;
String accountName = null;
String ownerName = null;
// Get the field values
long calID = 0;
calID = cursor.getLong(PROJECTION_ID_INDEX);
displayName = cursor.getString(PROJECTION_DISPLAY_NAME_INDEX);
accountName = cursor.getString(PROJECTION_ACCOUNT_NAME_INDEX);
ownerName = cursor.getString(PROJECTION_OWNER_ACCOUNT_INDEX);
System.err.printf("Calendar: %s\n", displayName);
long startMillis = 0;
long endMillis = 0;
Calendar beginTime = Calendar.getInstance();
beginTime.set(2014, 1, 26, 7, 30);
startMillis = beginTime.getTimeInMillis();
Calendar endTime = Calendar.getInstance();
endTime.set(2014, 1, 26, 8, 45);
endMillis = endTime.getTimeInMillis();
ContentResolver cr = context.getContentResolver();
ContentValues values = new ContentValues();
values.put(Events.DTSTART, startMillis);
values.put(Events.DTEND, endMillis);
values.put(Events.TITLE, type);
values.put(Events.EVENT_LOCATION, location);
values.put(Events.DESCRIPTION, description);
values.put(Events.CALENDAR_ID, calID);
values.put(Events.EVENT_TIMEZONE, "America/Los_Angeles");
cr.insert(Events.CONTENT_URI, values);
}
}
}
Most of this is pulled together from http://developer.android.com/guide/topics/providers/calendar-provider.html but it's just not working. Other than the warning that I mentioned, it all "appears" to work, except for the minor detail that nothing shows up on the calendar.
How do I write the getCalendars() query in a general way to get all of the calendars that might be on a user's device? Right now, you can see that I still have the language from the android site.
Any help is appreciated. Thank you.
EDIT: Andrew T pointed out that I have an off-by-one error since the months are zero-based, so I changed my hard coded date to be month 0. I also added a cursor.close() after my while loop. However, I still didn't have anything on my calendar.
One thing that I noticed was that I wasn't entering the while loop. So I took out the loop. When I hard-code the calID variable to 1, I finally get a calendar entry, but this concerns me. I thought that the point of getting the calendars was that a user might use multiple calendars on their device, and the cursor was a way of iterating over each of these calendars. Am I wrong about that?
If hardcoding calID to 1 will solve my problem, I'm fine with that, but I want to make sure that I understand this and am not relying on an ad hoc solution. Thanks again!

The warning is caused by not closing the Calendar Cursor after inserting the events. Call cursor.close() to close it.
Also, have you checked the events on February? I'm afraid you have off-by-1 issue with how month works in Java's Calendar. (Month starts with 0, which is Calendar.JANUARY)
EDIT :
It seems that there is a problem with getCalendars(), particularly on String selection = ... which returns empty Cursor. Also, with this approach, there is a possibility that the event will be added to multiple calendars, which might not be desired.
My idea is to create a ListActivity to list all calendars to let user chooses which calendar he wants to use. With this, you can also save the calendar ID to SharedPreference and use it when creating event conveniently.
I think you can use cursor = cr.query(uri, EVENT_PROJECTION, null, null, null); to list all calendars on the device.

Related

Why duration is NULL for recurrent event in android?

I try to implement one way synchronization with android calendar events (I need original events - not event instances). So, I have the following query:
String[] projection = new String[]{
CalendarContract.Events.DTSTART,
CalendarContract.Events.EVENT_TIMEZONE,
CalendarContract.Events.DTEND,
CalendarContract.Events.EVENT_END_TIMEZONE,
CalendarContract.Events.DURATION
};
String selection = null;
String[] args = new String[0];
String sort = CalendarContract.Events.DTSTART + " ASC";
Cursor cursor = getContentResolver().query(CalendarContract.Events.CONTENT_URI, projection, selection, args, sort);
According to developer docs for recurrent events dtstart and duration are required but when I create event through Google calendar and receive it later in my code I have dtend = 0 and duration = null.
Integer dtend = cursor.getLong(2);
String duration = cursor.getString(4);
Why it may happen?
I have an issue with indexes in my original application. CalendarContract.Events.DURATION column solves the problem. It has single event duration in RFC2445 format. I just need to parse it.

Get and filter events from Google Calendar

In my android app, I have the code to write, update and delete events from/to calendars.
Now I want to get some events from a calendar but I don't know the event IDs. I want to filter events by title, description, or any other value.
How can I do that?
My code:
public static void getEvents(Activity mContext) {
Uri.Builder builder = Uri.parse("content://com.android.calendar/instances/when").buildUpon();
long now = new Date().getTime();
ContentUris.appendId(builder, now - DateUtils.DAY_IN_MILLIS * 10000);
ContentUris.appendId(builder, now + DateUtils.DAY_IN_MILLIS * 10000);
String selection = "((calendar_id = ?) AND (description LIKE ?))";
String[] selectionArgs = new String[] {""+id, "'%abc%'"};
Cursor eventCursor = contentResolver.query(builder.build(),
new String[] { "title", "begin", "end", "allDay", "description"},
selection, selectionArgs,
"startDay ASC, startMinute ASC");
while (eventCursor.moveToNext()) {
String eventTitle = eventCursor.getString(0);
String description = eventCursor.getString(4);
}
}
When I filter by calendar id only - it works and I get all the events in this calendar. When I try to add the "AND description = ? " - I get 0 events although I know there are events with the string in their description.
Calendar API has Freebusy query where you conduct a filter between dates using timeMin and timeMax.
Check this SO thread for actual code sample.
If you wanted to do a query based on your preferences (title,etc), you'd have to make your own custom implementation. You can try to fetch all the events first then make your own custom query on the response.

Calendar query sorted by date and time

I am trying to return a query of Calendar events sorted by date and time so that I can iterate through them. I have queried it like so:
private Cursor mCursor = null;
private static final String[] COLS = new String[]{CalendarContract.Events.TITLE, CalendarContract.Events.DTSTART, CalendarContract.Events.AVAILABILITY};
mCursor = getContentResolver().query(CalendarContract.Events.CONTENT_URI, COLS, null, null, null);
mCursor.moveToLast();
This works great except it seems to return the calendar events based on when it was created and not by date and time. For example, if I create a new event for December 25, 2012, it will show up as the last result.
My end goal is that I would like to find the first event that starts before the current time. For example, if it is currently 8:00am, I want to find the first event that starts before 8:00am. I want to then check the duration of that event to see if it is a currently ongoing event.
Is there an easy way to return a query sorted by date and time or will I have to implement a sort after the results are queried? Better yet, is there a simple way to find the first event before the current time?
maybe this?
private Cursor mCursor = null;
private static final String[] COLS = new String[]{CalendarContract.Events.TITLE, CalendarContract.Events.DTSTART, CalendarContract.Events.AVAILABILITY};
mCursor = getContentResolver().query(CalendarContract.Events.CONTENT_URI, COLS, null, null, CalendarContract.Events.DTSTART + " ASC");
mCursor.moveToLast();
Another way is like this.
Collections.sort(dateList, new Comparator<Date>(){
public int compare(Date date1, Date date2){
return date1.after(date2);
}
});

Get events from all accounts of the device and only events for today

I need to get the number of events on the calendars for today. Only the number of events of all calendars, do not need anything more.
I found several threads about it, but I can not get the number of events for today.
using this code, the variable "events" gives me the number of all events in the first calendar.
int events=0;
public void read() {
Context context = getApplicationContext();
ContentResolver contentResolver = context.getContentResolver();
long ntime = System.currentTimeMillis();
Cursor cursr = contentResolver.query(Uri.parse("content://com.android.calendar/events"), new String[]{ "calendar_id", "title", "description", "dtstart", "dtend", "eventLocation" }, "calendar_id=1", null, null);
cursr.moveToFirst();
String[] CalNames = new String[cursr.getCount()];
int[] CalIds = new int[cursr.getCount()];
for (int i = 0; i < CalNames.length; i++) {
CalIds[i] = cursr.getInt(0);
CalNames[i] = "Event"+cursr.getInt(0)+": \nTitle: "+ cursr.getString(1)+"\nDescription: "+cursr.getString(2)+"\nStart Date: "+cursr.getLong(3)+"\nEnd Date : "+cursr.getLong(4)+"\nLocation : "+cursr.getString(5);
long StartTime = cursr.getLong(3);
long EndTime = cursr.getLong(4);
if ((StartTime<ntime)&&(ntime<EndTime)) {
System.out.println("In the middle of something");
break;
}
cursr.moveToNext();
events++;
}
cursr.close();
Log.i("control","vueltas:"+events);
events=0;
}
I need to modify the code to read events from all calendars of the device, and only events for today.
several days looking for how to do it, but the solutions I've found (including this page) do not know how to apply my code...
There is a very similar to my question, but has no answer https://stackoverflow.com/questions/18301870/get-todays-calendar-events
I appreciate your help.
What you need to do is query the calendars, and for each of them, query the events instances and just count them.
Something like that
// Projection array. Creating indices for this array instead of doing
// dynamic lookups improves performance.
private static final String[] CALENDAR_PROJECTION = new String[] { Calendars.ACCOUNT_NAME };
private static final String[] INSTANCE_PROJECTION = new String[] { Instances._ID };
// The indices for the projection array above.
private static final int PROJECTION_ACCOUNT_NAME_INDEX = 0;
// this is the method that returns the count of all events for all the calendars
public static int getAllEventsCount(final Context context)
{
int eventsCount = 0;
// queries the calendars
final Cursor calendarCursor = context.getContentResolver().query(Calendars.CONTENT_URI, CALENDAR_PROJECTION, null, null, null);
while (calendarCursor.moveToNext())
{
// gets the calendar name - you need that to query the instances for each calendar
final String accountName = calendarCursor.getString(PROJECTION_ACCOUNT_NAME_INDEX);
eventsCount = eventsCount + getCalendarEventsCount(context, accountName);
}
calendarCursor.close();
return eventsCount;
}
// this method gets a count of the events in a given calendar
private static int getCalendarEventsCount(final Context context, final String accountName)
{
final Calendar beginTime = Calendar.getInstance();
final Calendar endTime = Calendar.getInstance();
// get events from the start of the day until the same evening.
beginTime.set(beginTime.get(Calendar.YEAR), beginTime.get(Calendar.MONTH), beginTime.get(Calendar.DAY_OF_MONTH), 0, 0, 0);
endTime.set(beginTime.get(Calendar.YEAR), beginTime.get(Calendar.MONTH), beginTime.get(Calendar.DAY_OF_MONTH), 23, 59, 59);
final long startMillis = beginTime.getTimeInMillis();
final long endMillis = endTime.getTimeInMillis();
// Construct the query with the desired date range.
final Uri.Builder builder = Instances.CONTENT_URI.buildUpon();
ContentUris.appendId(builder, startMillis);
ContentUris.appendId(builder, endMillis);
final String selection = "(" + Calendars.ACCOUNT_NAME + " = ?)";
final String[] selectionArgs = new String[] { accountName };
final Cursor cursor = context.getContentResolver().query(builder.build(), INSTANCE_PROJECTION, selection, selectionArgs, Instances.BEGIN + " ASC");
int count = cursor.getCount();
cursor.close();
return count;
}
This has been tested and it works.
Hope it helps.
Did you look at Reading all of today's events using CalendarContract - Android 4.0+ ? But you would still need to query each calendar separately. You code is querying all events and post-processing whereas you should filter the start and end times in the query.
A content resolver is designed to return content, and is not a general purpose SQL that can return only a count. To make it more efficient, you would want to query just the id column and get the cursor row count.
Cursor.getCount()
Use Instances.CONTENT_BY_DAY_URI to get events of specific day. You can find an example of such query here - https://github.com/CyanogenMod/android_packages_apps_Calendar/blob/cm-12.0/src/com/android/calendar/Event.java#L307

Android Event contentResolver query synchronize problem

I am using following code to pull the data from the core event calendar application to show in my app.
ContentResolver contentResolver = this.getContentResolver();
final Cursor cursor = contentResolver.query(Uri.parse("content://calendar/calendars"),(new String[] { "_id", "displayName", "selected" }),null, null, null);
HashSet<String> calendarIds = new HashSet<String>();
while (cursor.moveToNext()) {
String _id = cursor.getString(0);
String displayName = cursor.getString(1);
Boolean selected = !cursor.getString(2).equals("0");
calendarIds.add(_id);
}
for (String id : calendarIds) {
Uri.Builder builder = Uri.parse(
"content://calendar/instances/when").buildUpon();
Calendar calendar = Calendar.getInstance();
long now = calendar.getTimeInMillis();
ContentUris.appendId(builder, now
- ((DateUtils.DAY_IN_MILLIS) * 24));
ContentUris.appendId(builder, now
+ ((DateUtils.DAY_IN_MILLIS) * 24));
Cursor eventCursor = contentResolver.query(builder.build(),
new String[] { "title", "begin", "end", "allDay",
"description", "eventLocation", "dtstart",
"dtend", "eventStatus", "visibility",
"transparency", "hasAlarm" },
"Calendars._id=" + id, null,
"startDay ASC, startMinute ASC");
while (eventCursor.moveToNext()) {
if (eventCursor != null) {
String title = eventCursor.getString(0);
}
}
The code is working fine. But sometime if I add a event in the calendar and come back to app, it is not showing the new event. If i exit the app and again come back or change the tab, the new event is added to the list. What have to do solve the synchronize?
When you come back to your app after the calendar add occurs, onResume() gets called. Re-query the cursor there to cause your UI to update.
Also, you should look at the ContentObserver interface, which sets up a callback interface for being notified of changes (Say they show up on the device after a network sync while you haven't left your app at all.)
Finally, using a CursorAdapter to drive a ListView from your Cursor will set up a ContentObserver automatically and take care of a lot of the glue code you had to write to get this far. It's a much more automatic way of doing it.

Categories

Resources