When your app needs to access calendar data you have to choose whether to use the CalendarContract content provider or whether to use Intents to start specific Activities of the Calendar app. This post covers the intents offered by the official Calendar app.
While with using Intents your possibilities are limited compared to those of the content provider, using intents has the advantage that the user already knows the Calendar app and that some things get automated for you (e.g. alarm and attendee settings). And if you only use intents, your app doesn't need the permissions you need for the content provider.
As of now you can do exactly four things:
- You can open the Calendar app at a specific date
- You can view events
- You can create new events
- You can edit events
Well, actually you can only do three things, since the last intent doesn't work. More on this later.
From ICS onwards the Calendar is part of Android's standard apps. So it is very unlikely that the Calendar app is missing. Nevertheless you should always test for the availability of Intents. I omit this in the code samples in the next paragraphs since these are examples only - but you should not! You can read more about this in my post about how to test for Intent availability.
Opening the Calendar app
Maybe you want to give the user the possibility to view her calendar. In this case you can use the
CalendarContract.CONTENT_URI as the Uri to start the app.
You have to add the time at which you want to open the calendar. You can either do this by concatenating Strings or by using Android's
Uri.Builder helper class.
In the following code fragment I show how you would open the calendar with a date two month in the future:
Calendar cal = new GregorianCalendar(); cal.setTime(new Date()); cal.add(Calendar.MONTH, 2); long time = cal.getTime().getTime(); Uri.Builder builder = CalendarContract.CONTENT_URI.buildUpon(); builder.appendPath("time"); builder.appendPath(Long.toString(time)); Intent intent = new Intent(Intent.ACTION_VIEW, builder.build()); startActivity(intent);
Creating an event
Probably more interesting is creating an event. In this case the action to use is
ACTION_INSERT and the URI to use is
Events.CONTENT_URI. What makes this option so compelling is that you do not have to add any Intent extras. You can open the event editor without any data - in which case only some default values of the form are prefilled as you can see in the next screen.
If the user has no calendar account added, Android shows a dialog that warns the user that no events can be added as long as no account exists. This is pretty common on the emulator - but should be the exception on a real device. Normally device vendors have a calendar with some sync adapter of their own. But even if this is not the case, your app will not crash. The next screenshot shows the message in this case.
The simplest code to create an event with an intent is this:
Intent intent = new Intent(Intent.ACTION_INSERT, Events.CONTENT_URI); startActivity(intent);
But if you want, you can add data so that the form is prefilled with these values. You can basically set all the data, that you can use to create events with the content provider. For attendees and alarms you have to use other constants, since they are not part of the
Events class. Strangely Google has decided that for the start- end end-time of the event you also need special constants. Why they didn't use the constants of the Events class is beyond me. This makes the code kind of ugly and the API harder to understand than necessary. As you can see in the next code fragment these constants from differing classes do not look that good:
Calendar cal = new GregorianCalendar(); cal.setTime(new Date()); cal.add(Calendar.MONTH, 2); Intent intent = new Intent(Intent.ACTION_INSERT); intent.setData(Events.CONTENT_URI); intent.putExtra(Events.TITLE, "Some Test Event"); intent.putExtra(Events.ALL_DAY, true); intent.putExtra( CalendarContract.EXTRA_EVENT_BEGIN_TIME, cal.getTime().getTime()); intent.putExtra( CalendarContract.EXTRA_EVENT_END_TIME, cal.getTime().getTime() + 600000); intent.putExtra( Intent.EXTRA_EMAIL, "email@example.com, firstname.lastname@example.org"); startActivity(intent);
Viewing an event
In addition to creating a new event you sometimes want users to view an event. Once again an intent is useful here because the presentation of the intent is done in a way that the user is accustomed to. But to do so, you need to have the event id - for which you need the content provider.
For the next example I assume that you already have the id for the event. In this case the code is really simple:
Intent intent = new Intent(Intent.ACTION_VIEW); Uri.Builder uri = Events.CONTENT_URI.buildUpon(); uri.appendPath(Long.toString(yourEventId)); intent.setData(uri.build()); startActivity(intent);
That's all nice and swell. Alas, the default Calendar app manipulates the back stack and pressing back does not jump back to your app but to the calendar view. That's the correct behavior when started from the calendar widget but not when the calendar app has been called from another app.
Your users have to press back twice to get back to the point where they came from. And they will blame you for this even though it's not your fault. Because of this try to avoid the need for viewing events. It might be better to open the calendar at the correct date.
I have filed a bug report for this issue. If you agree, that this is annoying, please star this issue.
And it's not just me, at least one developer at StackOverflow complains about the same bug.
Editing an Event
The final Intent that the calendar app supports is editing an event. Well at least according to the Android documentation.
Now it would have been nice if it worked. But not so. What you actually see if you use the intent for editing is the read-only version of the editing activity. In this case you can only change minor stuff like the reminders or the timezone. Theoretically you can provide additional data - though this unsurprisingly also doesn't work.
Add to this that the back button causes trouble again. When you click "Done" the Calendar-View is shown. The more natural behaviour would have been to save the changes and then return to where you came from. At least clicking "Cancel" or the back button works as expected.
This is pretty annoying since this is a very useful intent - or would be if it worked. If your app needs to edit events there is no other way to do so than to create the necessary form yourself. Not what users have come to expect in Android. And not what we developers love on Android.
I have also filed a bug report for this issue. Please vote for this as well.
For this bug I have also found another fellow StackOverflow user complaining about it.
Choosing between Intents and the CalendarContract provider
When to use which approach depends on the needs of your app. The following bullet points list some pros and cons of each approach:
Advantages of using intents
- You do not need to use permissions
- The user of your app can decide which backend calendar to use for an event
- familiar interface of the calendar app
- common behavior in Android
Disadvantages of intents
- Only very few use cases are supported; an important one doesn't work correctly
- The consistency of the user experience within your app might get lost when opening the Calendar app.
Advantages of the content provider
- Significant changes of nearly all data possible
- The only way for sync adapters to sync calendar related data
- Might happen in the background without the user knowing much about it
Disadvantages of the content provider
- You have to add permissions to your app
- If your app is not acting as a sync adapter you might not know which calendar id to use
You have ssen how to leverage intents to open up the Calendar view at a specific date, how to create new events and how to view existing ones.
You have also learned about the weak spots of the app's intents: The incorrect back behavior for viewing events and the total mess of the edit event intent.
Thus you have to choose carefully which and if intents are the correct way for your app to deal with events.