Accessing the internal calendar database inside Google Android applications

Posted by jimblackler on Oct 23, 2009

The Android phone operating system provides close integration with three excellent services from Google; email, address book and calendar. In addition it has a thriving third party app scene with a great variety of free and commercial applications on the Market. In theory this should mean that apps can provide new services to the user based on the Google service data they have already synced on the phone.


However in the case of calendar data this has proven a bit tricky. Google provide an API to access calendar data via the internet, and this can be used directly from Android apps. Unfortunately this also requires a working data connection, time and battery life to get at the data. In addition it will need the app to prompt for the user’s credentials (user name and password).


All this seems unnecessary when the data you want is right there on the phone already. Since there is an API that any application can use to access the user’s contact (address book) data directly on the phone without going to the internet. It’s called android.provider.Contacts and is documented here. So why doesn’t android.provider.Calender exist?


The answer


The answer is that is does exist – although it is not documented, and it is not included with the standard Google Android SDK. It is a little-known fact that it is possible to download the Android source, force the provider classes to be included by removing the @hide annotation that protects it, and make your own version of the SDK. This can be compiled against apps from which calendar queries can then be made.


This is how I originally built my app Quick Calendar – a ‘lite’ read only version alternative to the built-in calendar application released early 2009. This uses the same data used by the built-in calendar application, already synced onto users’ Android phones. Judging by my inbox it’s been the most popular hobby application I’ve ever put out. I’ve since had a number of inquires from other developers as to how I managed get at the internal calendar data on Android.


Firstly I would say that getting at the data by a custom-build SDK is possible but awkward. For one thing it is a pain keeping up to date with the latest changes and staying in sync with the Android Eclipse plug in.


The good news is you don’t actually need to use the calendar provider SDK classes to get at the data. Android exposes data across applications by using a feed system similar to web-based feeds, and query syntax similar to SQL. As a result, provided your app has the correct user permission (android.permission.READ_CALENDAR) you can query the database from the names, and continue to use standard SDKs such as 1.5 or 1.6. In the absence of specific SDK support for calendars, it’s only slightly less convenient to directly use the provider’s internal URIs (e.g. “content://calendar/calendars”) directly.


Warning


At this point I should point out that there’s a reason why Google haven’t officially exposed the internal calendar APIs. This is probably because they anticipate future changes to the calendar format. Once they’ve published an SDK the formats are to some extent set in stone and they’d like to avoid that. In my experience the formats have been stable since I first discovered the feed. However, don’t be surprised if future firmware changes break any apps that use the feed. In this event you’ll have to update your app and re-upload it to the Market. It’s not that big a worry, certainly not enough to avoid using this great feature – the ability to present users’ calendar data in new ways to them.


Example


I’ve uploaded an example project to http://svn.jimblackler.com/jimblackler/trunk/workspace/AndroidReadCalendarExample. All the calendar reading code is in http://svn.jimblackler.com/jimblackler/trunk/workspace/AndroidReadCalendarExample/src/net/jimblackler/readcalendar/Example.java.


To read the calendar data in your own app firstly add android.permission.READ_CALENDAR under <manifest> in your application’s AndroidManifest.xml.


Please note that you will probably have to use a real phone to test your app in development, because the emulator included with the SDK does not include the Google services (there will simply be no calendar database present to query).


Then get an android.content.ContentResolver and make a query to content://calendar/calendars to enumerate all the available calendars
on the phone. The ‘_id’ column will give you the calendar’s ID (referenced in events). You can get rough documentation of all the columns supported by looking at core/java/android/provider/Calendar.java. (As mentioned previously, this file is part of the public open source Android project but is not part of the official Android SDK from Google)

  1. ContentResolver contentResolver = context.getContentResolver();
  2.  
  3. final Cursor cursor = contentResolver.query(Uri.parse("content://calendar/calendars"),
  4.         (new String[] { "_id", "displayName", "selected" }), null, null, null);
  5.  
  6. while (cursor.moveToNext()) {
  7.  
  8.     final String _id = cursor.getString(0);
  9.     final String displayName = cursor.getString(1);
  10.     final Boolean selected = !cursor.getString(2).equals("0");
  11.    
  12.     System.out.println("Id: " + _id + " Display Name: " + displayName + " Selected: " + selected);
  13. }

Once you have a calendar ID you can use your ContentResolver object to obtain the events from that calendar. Note how a ‘where’ clause of ‘Calendars._id=??’ is employed to fetch only events of that particular calendar. This clause could query whichever columns your app required, using an SQL-like syntax. Note also how the Uri.Builder was used to build up a URL including date bounds for the query.

  1. Uri.Builder builder = Uri.parse("content://calendar/instances/when").buildUpon();
  2. long now = new Date().getTime();
  3. ContentUris.appendId(builder, now - DateUtils.WEEK_IN_MILLIS);
  4. ContentUris.appendId(builder, now + DateUtils.WEEK_IN_MILLIS);
  5.  
  6. Cursor eventCursor = contentResolver.query(builder.build(),
  7.         new String[] { "title", "begin", "end", "allDay"}, "Calendars._id=" + id,
  8.         null, "startDay ASC, startMinute ASC");
  9.  
  10. while (eventCursor.moveToNext()) {
  11.     final String title = eventCursor.getString(0);
  12.     final Date begin = new Date(eventCursor.getLong(1));
  13.     final Date end = new Date(eventCursor.getLong(2));
  14.     final Boolean allDay = !eventCursor.getString(3).equals("0");
  15.    
  16.     System.out.println("Title: " + title + " Begin: " + begin + " End: " + end +
  17.             " All Day: " + allDay);
  18. }

Note that all dates in the calendar format are stored as UTC (the number of milliseconds that have elapsed between midnight 1st January 1970 and the event in Greenwich UK).


This is the basics of calendar access on Android. I haven’t covered all the columns (these can be seen in the SDK source linked above) and I haven’t covered modification. Notheless I hope this information allows other developers to build great calendar-aware apps for Android. Please do leave queries in the comments here, and if you use this information to make an app please do tell us about it in the comments here.


Update


To access the Corporate Calendar on Motorola devices, use “content://calendarEx” in place of “content://calendar”.


Update 2


For Android devices using 2.2 (Froyo) or greater, where previously you had content://calendar you should write content://com.android.calendar