Question

Is it possible to get the calendar's entries from the phone offline? It seem the only way is to use gdata-java-client.

Was it helpful?

Solution

These answers are good, but they all involve hard-coding the Calendar URI (which I've seen in three different incarnations across different Android devices).

A better way to get that URI (which hard-codes the name of a class and a field instead) would be something like this:

Class<?> calendarProviderClass = Class.forName("android.provider.Calendar");
Field uriField = calendarProviderClass.getField("CONTENT_URI");
Uri calendarUri = (Uri) uriField.get(null);

This isn't perfect (it will break if they ever remove the android.provider.Calendar class or the CONTENT_URI field) but it works on more platforms than any single URI hard-code.

Note that these reflection methods will throw exceptions which will need to be caught or re-thrown by the calling method.

OTHER TIPS

Josef and Isaac's solutions for accessing the calendar only work in Android 2.1 and earlier. Google have changed the base content URI in 2.2 from "content://calendar" to "content://com.android.calendar". This change means the best approach is to attempt to obtain a cursor using the old base URI, and if the returned cursor is null, then try the new base URI.

Please note that I got this approach from the open source test code that Shane Conder and Lauren Darcey provide with their Working With The Android Calendar article.

private final static String BASE_CALENDAR_URI_PRE_2_2 = "content://calendar";
private final static String BASE_CALENDAR_URI_2_2 = "content://com.android.calendar";
/*
 * Determines if we need to use a pre 2.2 calendar Uri, or a 2.2 calendar Uri, and returns the base Uri
 */
private String getCalendarUriBase() {
    Uri calendars = Uri.parse(BASE_CALENDAR_URI_PRE_2_2 + "/calendars");
    try {
        Cursor managedCursor = managedQuery(calendars, null, null, null, null);
        if (managedCursor != null) {
            return BASE_CALENDAR_URI_PRE_2_2;
        }
        else {
            calendars = Uri.parse(BASE_CALENDAR_URI_2_2 + "/calendars");
            managedCursor = managedQuery(calendars, null, null, null, null);

            if (managedCursor != null) {
                return BASE_CALENDAR_URI_2_2;
            }
        }
    } catch (Exception e) { /* eat any exceptions */ }

    return null; // No working calendar URI found
}

Currently, this is not possible without using private APIs (see Josef's post.) There is a Calendar provider, but it is not public yet. It could change anytime and break your app.
Though, it probably will not change (I don't think they will change it from "calendar"), so you might be able to use it. But my recommendation is to use a separate class like this:

public class CalendarProvider {
     public static final Uri CONTENT_URI = Uri.parse("content://calendar");
     public static final String TITLE = "title";
     public static final String ....

And use those instead of the strings directly. This will let you change it very easily if/when the API changes or it is made public.

You can use the calendar content provider (com.android.providers.calendar.CalendarProvider). Example:


ContentResolver contentResolver = context.getContentResolver();
Cursor cursor = contentResolver.query(Uri.parse("content://calendar/events"), null, null, null, null);

while(cursor.moveToNext()) {
    String eventTitle = cursor.getString(cursor.getColumnIndex("title"));
    Date eventStart = new Date(cursor.getLong(cursor.getColumnIndex("dtstart")));
    // etc.
}

edit: you might want to put this in a wrapper (see Isaac's post) as it's currently a private API.

You can use the CalendarContract from here: https://github.com/dschuermann/android-calendar-compatibility

It is the same API class as available on Android 4, but made to work with Android >= 2.2.

About the API that can change... The whole ContentProvider approach won't change that quickly so can already overcome a lot of problems by only updating the strings. Therefor create constants you reuse over the whole project.

public static final String URI_CONTENT_CALENDAR_EVENTS = "content://calendar/events";

ContentResolver contentResolver = context.getContentResolver();
Cursor cursor = contentResolver.query(Uri.parse(URI_CONTENT_CALENDAR_EVENTS), null, null, null, null);
    //etc

If you want a proper private API you'll have to create a pojo and some services like this:

public class CalendarEvent {
    private long id;
    private long date;
    //etc...
}

public interface CalendarService {

    public Set<CalendarEvent> getAllCalendarEvents();

    public CalendarEvent findCalendarEventById(long id);

    public CalendarEvent findCalendarEventByDate(long date);

}

and so on. This way you'll only have to update the CalendarEvent object and this service in case the API changes.

Nick's solution involves managedQuery, which is not defined in the Context class. Many times when you are running things in the background you would want to use a context object. Here's a modified version:

public String getCalendarUriBase() {

return (android.os.Build.VERSION.SDK_INT>=8)? "content://com.android.calendar": "content://calendar"; }

The catch for null should not be carried out here since there might be more exceptions even if the managedQuery succeeded earlier.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top