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.
Related
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.
I am accessing Android MMS database to get the date of MMS message:
Uri mmsUri = Uri.parse("content://mms/");
ContentResolver contentResolver = getContentResolver();
String[] projection = {"_id", "date"};
Cursor cursor = contentResolver.query(mmsUri, projection, null, null, null);
long dateVal = cursor.getLong(cursor.getColumnIndex("date"));
//This date is always 1970
Date mmsDate = new Date(dateVal);
But the date I get is always 1970. Then, I found an answer for this. I need to set the projection to null (to return all columns) and then use the following code to get date value:
//A mystery column of index 2
long timestamp = cursor.getLong(2) * 1000;
//It works !
Date mmsDate = new Date(timestamp);
Everything until here is fine. But, now instead of geting all rows from MMS database, I need to select those rows which were sent after a certain date, which means I need to use selection & selection argument. Something like:
String selection = NAME_OF_MYSTERY_COLUMN_IDX_2 > minDate
Cursor cursor = contentResolver.query(mmsUri, projection, selection, null, null);
But I have no idea what is the name of the column with index 2, how could I achieve what I need ? Is there a workaround?
Your first code block is correct, except for the Date instantiation. That constructor expects the time in milliseconds, but the MMS table keeps dates in seconds. To correct this, simply multiply the value returned from the query by 1000.
Date mmsDate = new Date(dateVal * 1000);
For future reference, the Cursor#getColumnName() method will give you the String name for a given column index.
You can try this.
String selection = "date_sent" > minDate
https://developer.android.com/reference/android/provider/Telephony.BaseMmsColumns.html#DATE_SENT
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);
}
});
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.
I am querying events table in android to get events.
String[] projection = new String[] { "calendar_id", "title", "description",
"dtstart", "dtend", "eventLocation" };
Cursor cursor = cr.query(Uri.parse("content://com.android.calendar/events"),
projection,
null,
null,
null);
This returns all the events. But now I want events only in specific duration say 4/03/2013 to 7/03/2013. How do I use selection and selectionArgs[] ?
I tried this
String selection = "((dtstart <= ?) AND (dtend >= ?))";
String[] selectionArgs = new String[] {startString, endString};
But its not returning anything. My doubt are
1. Will where clause work ? Because, startDate and endDate are first converted to long (milliseconds) and then to String and then are stored in the table. So how can strings be compared for their long values. There is no toNumber() function in sqlite.
2. If I'm passing selectionArgs then startString and endString should be in which format ?
startString & endString should be passed in millis. Try using:
Calendar c_start= Calendar.getInstance();
c_start.set(2013,2,4,0,0); //Note that months start from 0 (January)
Calendar c_end= Calendar.getInstance();
c_end.set(2013,2,7,0,0); //Note that months start from 0 (January)
Also, I think your logic is reversed, date range should be like this:
String selection = "((dtstart >= "+c_start.getTimeInMillis()+") AND (dtend <= "+c_end.getTimeInMillis()+"))";