Mobility

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...

Editor's Picks