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)
- ContentResolver contentResolver = context.getContentResolver();
- final Cursor cursor = contentResolver.query(Uri.parse("content://calendar/calendars"),
- (new String[] { "_id", "displayName", "selected" }), null, null, null);
- while (cursor.moveToNext()) {
- final String _id = cursor.getString(0);
- final String displayName = cursor.getString(1);
- final Boolean selected = !cursor.getString(2).equals("0");
- System.out.println("Id: " + _id + " Display Name: " + displayName + " Selected: " + selected);
- }
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.
- Uri.Builder builder = Uri.parse("content://calendar/instances/when").buildUpon();
- long now = new Date().getTime();
- ContentUris.appendId(builder, now - DateUtils.WEEK_IN_MILLIS);
- ContentUris.appendId(builder, now + DateUtils.WEEK_IN_MILLIS);
- Cursor eventCursor = contentResolver.query(builder.build(),
- new String[] { "title", "begin", "end", "allDay"}, "Calendars._id=" + id,
- null, "startDay ASC, startMinute ASC");
- while (eventCursor.moveToNext()) {
- final String title = eventCursor.getString(0);
- final Date begin = new Date(eventCursor.getLong(1));
- final Date end = new Date(eventCursor.getLong(2));
- final Boolean allDay = !eventCursor.getString(3).equals("0");
- System.out.println("Title: " + title + " Begin: " + begin + " End: " + end +
- " All Day: " + allDay);
- }
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
Hello ,
i need you help ,,
i am developing a program for my university . i want to include a calender where i add the impotant event to it ;user can click on the date and look at the event ….
besides that whenever an event is close ,notifcation to the user .
i dont want the user to write anything in the calender
how to do so ?
Time for an update – they finally released the public API, and it’s slightly different. In ICS there is a new CalendarContract class, similar to what they did for ContactContract. See the official docs for the names you should now use instead of hardcoding strings that happen to work… See http://developer.android.com/reference/android/provider/CalendarContract.Calendars.html for the real deal.
I was wondering if you have a comprehensive example to use the new API for ICS along with for 3.x and below without having to recompile for each type of device.
Hi JIm,
How can i edit my reminder’s snooze time. I successfully created events with remainders but later i can’t edit their snoozing interval.
i’m doing with android 2.2.
could you help me..
[…] 获取 Google Calendar 的数据不像获取联系人和短信等数据那样,官方有文档,而这是官方不支持的。于是我就在网上查了很久,最后终于查到一个比较靠谱的,http://jimblackler.net/blog/?p=151,这网页上面写得很清楚,我就不粘贴代码了,唯一注意的是一定要添加android.permission.READ_CALENDAR 在 AndroidManifest.xml 里。不过想起来,有的日程事件不是上课,就没有必要设默认静音。然后我就想到 Google Calendar 在新建每个 event 的时候,都会有两个选择,Busy 和 Available。如果选Busy 的时候,就表示你很忙,需要静音,选 Available 的时候,就表示不需要静音,这样岂不是很好?但是,这个 Busy 和 Available 具体又对应 Google Calendar 的数据库的哪个字段呢?然后我看了看 android.provider 里面的 Calendar.java 源码,源码里面有一个字段的注释让我觉得有点靠谱,“transparency”,注释是:Transparency for the event– does the event consume time on the calendar?,嗯,就是它了。后来我有点不确定,自己实际试了试,新建两个 event,一个选 Busy,一个选Available,然后用 Root Explorer 实际看这两个 event 的 transparency 字段值,发现就是这个字段。OK! […]
Hi Jim,
My application has a feature to create, edit and delete the calendar events. I am successfully able to do all the functionalities for the Android 4+ versions.
But I am not able to EDIT the calendar events in 2.x versions.
Is this is possible ?
Any help regarding the same is requested and appreciated.
Thanks in advance.