Smartphones

Programming with the Android 4.0 Calendar API: the good, the bad, and the ugly

The Android 4.0 CalendarContact class is a welcome and long overdue addition to your programming arsenal. This tutorial is a brief introduction to the class.

If you've been developing for the Android operating system for a while, you know it has been plagued with a lack of support for the platform's integrated calendar. With Android being open source, plenty of clever developers have managed to circumvent these shortcomings, but they did so without Google's blessing and even received a stern warning that future releases of Android might break apps in the Market that were taking advantage of unofficial APIs.

With the release of Android 4.0 (codenamed Ice Cream Sandwich), the Google framework folks have finally released an official and supported mechanism for interacting with the core calendar app. The class is called CalendarContract, and with it developers can read and write to the underlying databases that maintain a list of calendars, events, attendees, reminders, etc. If I had to sum it up in a word, I'd choose: powerful. If I had to sum it up in three words, I'd say: it's about time.

While I was pleased that Google finally got around to including the calendar API, much to my disappointment the documentation is thin. What I really hoped to find was a sample calendar application included in the Android 4.0 SDK; if it's there, I haven't had any luck locating it. In the end, I did what I do best: wrote some code, watched that code blow up, and then poked at the carnage until I figured out what went wrong.

What follows is a brief introduction to the CalendarContract class; it only tackles reading events, not inserting new ones. If this post warrants sufficient interest, a tutorial for adding events to the calendar will be forthcoming. The source code for this project can be downloaded here. However, besides requiring at least API level 14, this project won't operate properly on the emulator. Why? Because the calendar on the emulator isn't really functional -- meaning that you can't sync it with a Google account. It's another way in which the current level of support to developers for writing calendar apps is not quite there yet.

1. Using Eclipse, create a new Android project. Remember, you must target devices running 4.0 or higher for the calendar API to work.

2. In order to access the user calendar, you must add the proper permissions to the AndroidManifest.xml file. Since at this time we are only reading from the calendar, we only need a single permission:

<uses-permission android:name="android.permission.READ_CALENDAR"/>

3. The /res/layout file for our project will consist of a text view and a couple of buttons.

main.xml

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android=http://schemas.android.com/apk/res/android

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:orientation="vertical" >

<TextView

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:text="Introducing ICS Calendar"

android:gravity="center"

android:padding="10dip"/>

<TextView

android:id="@+id/data"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:gravity="center"

android:padding="10dip"/>

<LinearLayout

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:orientation="horizontal"

android:gravity="center" >

<Button

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:id="@+id/previous"

android:text="Prev"

android:padding="10dip"/>

<Button

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:id="@+id/next"

android:text="Next"

android:padding="10dip"/>

</LinearLayout>

</LinearLayout>

4. Moving on to our /src/Main.java file we will start by extending the Activity class and defining the columns we are interested in getting from the underlying calendar database.

Main.java
package com.authorwjf.calsample;
import java.text.Format;
import android.app.Activity;
import android.database.Cursor;
import android.os.Bundle;
import android.provider.CalendarContract;
import android.text.format.DateFormat;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
public class Main extends Activity implements OnClickListener{
private Cursor mCursor = null;
private static final String[] COLS = new String[]

{ CalendarContract.Events.TITLE, CalendarContract.Events.DTSTART};

}

5. Now we need to override the on create method. Pay special attention to how we populate the database cursor. This is where we need our previously defined COLS constant. You'll note also that after the database cursor is initialized and the click handler callbacks are set, we go ahead and manually invoke the on click handler. This shortcut allows us to initially fill out our UI without having to repeat code.

Main.java

@Override

public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);

setContentView(R.layout.main);

mCursor = getContentResolver().query(

CalendarContract.Events.CONTENT_URI, COLS, null, null, null);

mCursor.moveToFirst();

Button b = (Button)findViewById(R.id.next);

b.setOnClickListener(this);

b = (Button)findViewById(R.id.previous);

b.setOnClickListener(this);

onClick(findViewById(R.id.previous));

}

6. In our callback, we will manipulate the cursor to the correct entry in the database and update the UI.

@Override
public void onClick(View v) {

TextView tv = (TextView)findViewById(R.id.data);

String title = "N/A";

Long start = 0L;

switch(v.getId()) { case R.id.next: if(!mCursor.isLast()) mCursor.moveToNext(); break; case R.id.previous: if(!mCursor.isFirst()) mCursor.moveToPrevious(); break;

}

Format df = DateFormat.getDateFormat(this); Format tf = DateFormat.getTimeFormat(this); try {

title = mCursor.getString(0);

start = mCursor.getLong(1);

} catch (Exception e) {

//ignore

}

tv.setText(title+" on "+df.format(start)+" at "+tf.format(start));

}

That does it. Loading the code to an actual device will allow you to navigate through all the events in the calendar one at a time.

Figure A

As you continue to feel your way around with the API, I think you'll agree that despite its shortcomings, the CalendarContact class is a welcome and long overdue addition to your programming arsenal.

About

William J Francis began programming computers at age eleven. Specializing in embedded and mobile platforms, he has more than 20 years of professional software engineering under his belt, including a four year stint in the US Army's Military Intellige...

8 comments
ranjitp43
ranjitp43

Awesome tutorial. but i have a little doubt .here we get all events in a reverse order i.e from last of the calendar. Is there any way to get all holidays and current events from Google calendar ?

ankitrk
ankitrk

I have a program which inserts multiple events into the calendar and what i have noticed is that this  works properly only some times. Some of the entries do not get added to the calendar and i have to run the code again. Has anyone else experienced similar problems?

blabla2009
blabla2009

 Thanks for this very nice tutorial! I didn't know where to start because the Google stuff is really sketchy... But I have got a question. On this line:

start = mCursor.getLong(1);

Where did you get the columnIndex from?

leomat1988
leomat1988

Thanks! Very Interesting! It would be great to also have an example of writing in the calendar.

authorwjf
authorwjf

Thanks for the updates. I wasn't aware that earlier builds of the Android 4.0 SDK might give users problems so I appreciate you sharing that experience.

btmaster1
btmaster1

While I agree it doesn't work properly on an emulator, I did find it difficult to even compile & build at first. What you might want to mention is to make sure all your Android SDKs (especially the 4.0 and above versions) are up to date. I had an early version of the 4.0 build, and as such the compiler wouldn't recognize R.layout.main , R.id.next , and R.id.previous as valid values. When I installed the updates to 4.0 and 4.0.3. it would recognized them. Note the current update for 4.0 is missing a kernal file, but 4.0.3 works (API 15). You have your class as Main, yet when I try to run this way, the program just crashes with Class Not Found: com.authorwjf.calsample.CalendarContractActivity This can be easily fixed simply my renaming the class as CalendarContractActivity (as well as the java file name). Again this just makes it runnable. The Prev and Next button will do nothing and the date will stay in January of 1969, probably due to reasons you mentioned in your article. You also have the @Override annotation for the onClick(View v) routine. Eclipse says not to as you only add it when you are overriding a superclass method. So just remove the annotation for the routine. Also running it the first time may result in failure. If this happens, just close the emulator and try running it again. Something about initial setup I think is causing the install to fail.

authorwjf
authorwjf

Good catch! Will update it ASAP.

bart001fr
bart001fr

Your splash screen should read "...Calendar" in the first line below the blue separation line. I located the error in the third paragraph of code listed above; in "main.xml" locate the line< android:text="Introducing ICS Calender" > which is the offending line, as you can see. Please correct it. Thank you. Little things like that are important and say a lot about a programmer. Like how much he cares about the accuracy of his programs.

Editor's Picks