What You Need to Know About the Intents of Android’s Calendar App

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

Form for entering events

Form for entering events

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.

Warning when no calendar account exists

Warning when no calendar account exists

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, 
      "attendee1@yourtest.com, attendee2@yourtest.com"); 
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

Lessons learned

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.

Half a Year of Blogging About Android Development

Exactly six months ago I published my “Hello World” post. And one week later my first post with serious content followed: Android: Checking Connectivity.

Over the last half year I have continuously blogged and there were only two weeks in which I didn’t publish any posts. According my general feeling my blog progresses nicely. And Google Analytics, G+ reshares and the number of G+ followers seem to confirm this. So, reason enough to celebrate :-)

I will share many stats with you, but if you do not care about numbers, please head directly to the sections about my plan to change the current hosting solution, my current unwillingness to offer a responsive version of this website and for what’s to expect in the coming months.

Published posts

Total number of posts published: 32 (my Hello World not included)

Tutorials published: 11

Quick tips published: 2

Obviously I should expand on the Quick tip type :-)

Usage stats

Visitors per day: Roughly 300 (on weekends less, on posting days about twice as much) – steadily increasing

Page views per day: Above 450

Bounce rate: 78 per cent (I do not know whether this is good or bad – if you blog, please let me know how you do in this regard)

Ratio of new vs. returning visitors: 61 to 39 (I also do not know what to make of this number)

Favorite topics

The two top posts are my tutorial on BroadcastReceivers and on making your app searchable. Both get about 50 hits each daily.

The topic with the most hits so far is the post about the library Crouton. This post caused the biggest traffic spike yet and was really loved on the three social media sites I use (Google plus, Reddit and DZone). But as was to be expected this post is nowhere near the previous two on daily views.

Generally speaking tutorials are liked much more. This also is no big surprise since they tend to get more search traffic.

Traffic sources

Search: 50%
Referrals: 30%
Direct traffic: 16%
Campaigns: 4%

Campaigns is an odd term. In my case this are visits originating from an RSS reader.

Referrals are all visits that come via a link from any other site. This can be social media sites, blog links, newsletters seen on the web or coming via a redirect and so on – unless it is a search result.

Search is obviously a visit via Google (Bing is nearly irrelevant with about half a per cent of all search traffic – yahoo is even worse).

Direct traffic are those people entering the domain directly into the browser. I can only assume that many people copy links before opening them, use Chrome’s incognito mode or hide the referrer in other ways. This number is definitely too high.

Referring blogs

A whopping ten per cent of all referrals (see above) come via Juhani Lehtimäkis blog androiduipatterns.com. That’s the next big traffic source directly after Reddit and Google plus. And these visitors are an especially interested crowd (low bounce rate, long duration of average visit).

So if anyone hasn’t been to his fantastic blog it’s about time to have a look. And since you’re in the right mood, why not buy his book “Smashing Android UI”? Should be in every Android developers and designers bag anyway! And, no, I do not get any affiliate commissions for this link!

Other blogs are also doing well. Readers coming from blogs are on average much more interested than those coming via social networking sites. I think this is not surprising. A reader of another blog found the content of that blog interesting. And if the other blogger recommends yet another blog, chances are good that the reader likes this blog as well.

So if you haven’t done so, please have a look at my blogroll. These blogs are highly recommended by me and all deal with Android development as well.

A big thank you to all those bloggers that have linked to my blog!

Newsletters

Apart from blogs another important source of readers of my blog are the newsletters Android Weekly, #AndroidDev Weekly and the newsletter of the French site FrAndroid.

Bounce rates with newsletter visitors is higher than those of blog referrals but those readers do visit more pages on average than those coming via social media or search.

Also thanks to the newsletter editors for linking to my blog.

Search

As mentioned, for my blog Google is the only search engine that counts. It’s responsible for 50 per cent of all visits. Since people search for a specific topic, the bounce rate is quite high.

The top five queries where people see my site on a results page all have to do with BroadcastReceivers. Unsurprisingly my tutorial about BroadcastReceivers is where most readers end, when coming from Google.

Google Analytics uses two stats for search. The first is the report about the top 1000 daily queries. The second report is about all searches where my blog showed up in the results.

For the 1000 top queries (do not ask me about this report, I still do not get it) my click-through-rate is 5 per cent, for all Search Impressions in total my click-through-rate is 7.5 per cent. From what I have read these are quite decent numbers. But if some fellow bloggers could shed some light on these two reports, I would be thankful.

Technology used by visitors

Since this is a developers blog, you probably are interested in the technology people use. So let me share some stats.

Mobile usage

Nearly 10 per cent of all visitors use mobile devices. Of all devices the Google Nexus is the top device. It accounts for more than three times the number of visitors than the next device – which is the iPad. The Nexus 7 is the third most used device and the fourth is one of the iPhones. So, there you have it. We Android users and devs are an open-minded crowd.

Still, Android usage is eight times that of iOS usage. No surprises here. But since Android offers a better variety of products, individual devices carry less weight.

The most interested readers are those using a Nexus 7 or a Galaxy Tab 10.1. They stay longer, come back more often and visit more pages. Other than for the Nexus 7 and Galaxy Tab 10.1 the bounce rate is quite high for mobile users.

OS share

Windows: 60%
Mac: 17%
Linux: 13%
Android: 8%
iOS: 1%

All others (Windows Phone, Symbian, Chrome OS, unidentifiable) account together for only 1 per cent.

Browser share

Chrome: 63%
Firefox: 21%
Safari: 4%
IE: 4%
Android Browser: 4%
Opera: 3%

I especially like the numbers for the Internet Explorer. I have suffered under this browser way too long!

Top Countries

United States: 15% (California alone 4%)
India: 11% (the city of Bangalore alone 3%)
Germany: 8%
France: 5%
Great Britain: 5%

Even though more French people read my blog than Brits, London is way ahead of Paris. But still, for a blog written in English, this is a surprising result. I guess it has to do with the FrAndroid newsletter mentioning this blog regularly.

Germany’s numbers probably are higher than Germany’s actual developer share simply because I am German myself and have many German G+ followers.

Social media

Now apart from search most people come to my blog via social media. In per cent of all visitors:

Reddit: 8%
Google plus: 6%
DZone: 3%
StackOverflow: 1.4%
Twitter: 0.5%

This is for the last month. The months before Reddit has had a much higher share of about three times that of Google plus. But my experience with Reddit has been quite mixed. Some posts go wild while others are duds or, worse, get downvoted rather quickly (which causes them to vanish from the Reddit site). I simply do not get Reddit.

The numbers for Twitter and Facebook are that low simply because I am no user of these sites. But I plan to use Twitter in the future. Why is that? Well, many interesting developers still do not use Google plus. Mark Murphy is probably the best-known of them, but not the only one. And of course, Twitter users love to follow and reshare links. So this should open my blog to new readers.

I really like DZone. I have found some nice blogs via their site. But it is not the biggest traffic engine. Anyway: If anyone at DZone reads this: Please offer an API. For a social media site having no API is simply unbelievable. Apps that monitor social media coverage cannot deliver anything about DZone at the moment. That’s sad! And of course I would like to see an Android app for DZone. Yours truely would love to help create one ;-)

Why I have to move my blog

Right now I am using shared hosting. This was very easy to set up, but in the end it is nevertheless a pain. The shared hosting I use is way too slow to meet my expectations. Performance is simply not sufficient. Just imagine I had an even bigger reader base. Some of you probably would walk away – not willing to wait any longer for responses of my server.

Furthermore the package I am currently using, doesn’t offer the chance to use gzip compression. Page Speed Score is still 86 (of 100), which is okay. But that’s not how it feels.

All in all I think I have to switch to something better. Probably Dedicated hosting. Maybe a vhost server. I am evaluating options at the moment. But I will change the provider (or the package). So expect to see a significant improvement in performance pretty soon.

No mobile version? No responsive design? Is this gonna change?

To be honest: Mobiles are getting bigger at a fast rate and screen real estate has improved a lot. Thus I have no plans to do a lot in this regard. So while I am a big proponent of responsive design for Android apps, I do not consider it necessary for this blog. Of course, if a lot of you would post in the comments that you miss this, I would reconsider. It’s up to you!

What I will change though, is how a printout looks. I often print blog posts of others if I like the content and expect to refer to the post more than once. Or for posts that look important but are pretty longish. But my blog simply doesn’t look good when printed. Which is not okay and which I plan to correct. You shouldn’t have to wait too long.

What’s to expect in coming months

I already started my series about libraries with my post about Crouton. I intend to make this a regular post style, appearing once a month. The next one will be about EventBus.

Furthermore book reviews are in the pipeline. Probably one review per month. It depends on what books get published and how much time I have to read them. Actually a book review will be on of my next posts. Disclaimer: I will add Amazon affiliate links. Just ignore them if you don’t like this kind of stuff. But if you want to support my caffeine-level, follow these links if you intend to buy the books anyway :-)

I also plan on doing some interviews with fellow developers, may be even some Googlers and – if possible – Android-savvy designers as well. This is not gonna happen this year but hopefully at the beginning of the next year.

I might also occasionally have a look at other blogs. Maybe coupled together with an interview of the blogger. I like this idea, but I am not sure if this adds value. If you would like to see this let me know.

Apart from this, I simply will continue what I did the last months. Complete with occasional bug reports, rants, tutorials, in-depth posts and short quick tips.

Thanks to all of you for your support so far!

iCal’s Recurrence Rule and Duration Formats

If you create repeating events in Android using the CalendarContract content provider, the fields for recurrence rules (RRULE) and durations (DURATION) follow the iCal format (RFC 5545). Here I cover the basics of these formats to create valid field values.

This post is somewhat of an oddity on this blog as most of its content is not Android specific.

Recurrence Rule

The recurrence rule is used in Android whenever you deal with events of the CalendarContract content provider. The event table has the column RRULE that takes the recurrence rule as a String value.

This rule is made up of several components that are separated by semicolons.

As the first element every recurrence rule must define the frequency. It covers all intervals from seconds to years. You use the FREQ qualifier to do so, e.g. FREQ=DAILY.

All other elements are optional. To limit the number of events you can specify the interval, the number of occurrences or the enddate. A FREQ=WEEKLY and a INTERVAL=2 would result in a biweekly event. The number of occurrences is given by the COUNT=x qualifier. And finally an enddate can be given using the element UNTIL=yyyymmdd.

It is also possible for you to select certain elements. E.g. a BYDAY=TU would select Tuesdays, a BYMONTH=5 would select May.

There is another obscure but interesting element: BYSETPOS. This is used to denote one element in a set of elements. A BYSETPOS=1 selects the first element – a BYSETPOS=-1 selects the last element. You have to use it together with another BYxyz element.

Some RRULE samples

As I have already mentioned in my post about the CalendarContract content provider the in and outs of the recurrence rule are pretty tedious and unnecessary complex. Most often you do not need to know all the details. The following samples should help you get to grips with most of the rules.

  • FREQ=DAILY;BYDAY=MO,TU,WE,TH,FR;COUNT=15
    This rule describes an event that takes place on every weekday (BYDAY) for the next 15 weekdays (COUNT).
  • FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3;COUNT=2
    Last Sunday (negative value for BYDAY) in March for every year. That’s the day the summer time begins in the EU.
  • FREQ=YEARLY;BYDAY=2SU;BYMONTH=5;COUNT=3
    Mother’s day. This rule describes a yearly that takes place on the second Sunday (BYDAY) in May (BYMONTH).
  • FREQ=YEARLY;INTERVAL=4;BYMONTH=11;BYDAY=TU;BYMONTHDAY=2,3,4,5,6,7,8
    US election day. Every fourth year (INTERVAL) on the first Tuesday (BYDAY) after a Monday (BYMONTHDAY ensures that) in November (BYMONTH).
  • FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=1;UNTIL=20131231
    All first weekdays (BYDAY in combination with BYSETPOS) of the month until the end of 2013 (UNTIL).

If you are really into this, have a look at this enormous list of recurrence rules samples.

Duration

The format of the duration element is much easier to grasp. It takes only one page of the spec compared to seven for the recurrence rule.

So let’s me show you one duration value and explain the format with this example as a basis:

+P5W3D

The first thing, you will notice is the plus sign. Surprisingly durations can have a positive or negative value. The positive value is the default and thus the “+” sign can be omitted. Negative durations can be used to trigger alarms before the event starts. But Android uses a fixed time for this so negative values aren’t used on Android.

The next element after the sign is the “P” which is a fixed constant.

And lastly follows a non-separated string of time quantifiers that all are added up to form the duration.

So the sample given above is for five weeks and three days. What’s odd with durations in the spec is that the biggest period is weeks. It’s not possible to specify months or years.

Time qualifiers
Qualifier Meaning
W Week
D Day
H Hour
M Minute
S Second

Since Android doesn’t use durations for the length of an event, durations usually consist of weeks and days only.

Summary

I have covered the basics of the RRULE and DURATION elements of CalendarContracts event table. Since these rules are in a somewhat obscure format I have given you some samples of how they are created. This should get you started whenever you need to create recurring events in your code.

Android’s CalendarContract Content Provider

Android developers have been longing for an official Calendar app and content provider since Android has been released. With the release of Ice Cream Sandwich Google has finally added this feature to our tools list. Now we developers can use the Calendar app from within our Activities using Intents or we can access the data by making use of the new CalendarContract content provider.

CalendarContracts entities

Before I explain how to use the calendar I first present the calendar’s data model. It consists of calendars, events, event instances, event attendees and reminders.

First we obviously have calendars. The plural is no accident, because each user can manage multiple calendars on her device. For example her personal calendar, her work calendar, a calendar of her colleague and one for her sports team. Android uses colors to distinguish between different calendars in the Calendar app:

Events of multiple accounts

Events of multiple accounts


Account selection when adding an event

Account selection when adding an event

For each calendar you can have multiple events. But each event belongs to exactly one calendar only. If the event is visible twice (e.g. in the private and in the business account of the user), the event is stored twice, each belonging to their respective calendars.

Many events are recurring. So in addition to events there are also event instances. An instance represents the specific event that takes place at just this one point in time. For a single event Android creates one instances entry, for recurring events Android creates as many instance entries as there are occurrences of this event.

Events can of course have multiple attendees as well as multiple reminders.

Furthermore the data model contains some helper tables that are only relevant for sync adapters. I don’t deal with these in this blog post.

What’s weird is that attendees belong to the event and not to the event instances. What happens if users want to cancel one instance of a recurring event? What if they want to add an attendee for just one event? For example changing the speaker of a recurring User Group talk? The solution chosen here is that another entry is added to the events table with the field Events.ORIGINAL_ID referring to the id of the base event. This is also the case for event instances for which only the time has changed – even though this information is already part of the instances table.

What are sync adapters

Sync adapters sync data of specific accounts to a backend. Sync adapters are usually an integral part of the app, that needs them. Thus users usually do not install them on their own.

In this blog post I do not deal in detail with sync adapters. But you should know that sync adapters possess more privileges and can access more tables and fields than normal apps. The most important privilege is that only sync adapters can create calendar accounts.

Of course a sync adapter has these privileges only for its account type. An adapter cannot mess with the privileged data of another account.

Sync adapters don’t deal with calendar data alone. They also sync contact data or any data specific to your app that has to be synchronized between the device and a backend.

You can find a list of available sync adapters in your Settings app in the section “Accounts”:

Sync adapters in the settings app

Sync adapters in the settings app

Using your Google Calendar within the emulator

To test your calendar-related code within the emulator, you first need to have a calendar. The best is to sync with an existing calendar of yours – and the easiest way to do this, is to sync with your Google calendar.

I have written another post that explains how to sync with your Google Calendar.

The Calendar content provider

To use the Calendar content provider you need to declare the necessary permissions within your manifest file first:

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

Keep in mind that the Calendar is part of the official API only from Ice Cream Sandwich onwards. The constants, defined in CalendarContract and its many inner classes are not available prior to API version 14. All code samples shown here need an API-level of 14 or higher as build target and the value for the android:minSdkVersion attribute of the <uses-sdk> element in the manifest file.

The content provider itself probably is available on older versions – though you cannot rely on this. For more on this see the section Calendar-usage before Ice Cream Sandwich.

Accessing calendars

If your app doesn’t act as a sync adapter chances are that you never have to deal with the calendars itself. Other than query for the correct id that is. The thing you are most likely to do is add, delete or change events. But for this you need to know to which calendar to add the event.

So let’s start by reading all calendars that are available on a device:

String[] projection = 
      new String[]{
            Calendars._ID, 
            Calendars.NAME, 
            Calendars.ACCOUNT_NAME, 
            Calendars.ACCOUNT_TYPE};
Cursor calCursor = 
      getContentResolver().
            query(Calendars.CONTENT_URI, 
                  projection, 
                  Calendars.VISIBLE + " = 1", 
                  null, 
                  Calendars._ID + " ASC");
if (calCursor.moveToFirst()) {
   do {
      long id = calCursor.getLong(0);
      String displayName = calCursor.getString(1);
      // ...
   } while (calCursor.moveToNext());
}

Of course you can also change calendars using the content provider. But here it starts getting tricky. That’s because apart from reading data, the access to calendars is limited. Sync adapters can do pretty much anything with calendars they own, but normal apps cannot do much at all. Of course they cannot delete calendars, but they also cannot create calendars. They only can change some trivial values like the name of the calendar.

There is one exception to this rule: You can create local calendars that do not get synced. So the following code shows how to create a local calendar which you will use later on for manipulating events:

ContentValues values = new ContentValues();
values.put(
      Calendars.ACCOUNT_NAME, 
      MY_ACCOUNT_NAME);
values.put(
      Calendars.ACCOUNT_TYPE, 
      CalendarContract.ACCOUNT_TYPE_LOCAL);
values.put(
      Calendars.NAME, 
      "GrokkingAndroid Calendar");
values.put(
      Calendars.CALENDAR_DISPLAY_NAME, 
      "GrokkingAndroid Calendar");
values.put(
      Calendars.CALENDAR_COLOR, 
      0xffff0000);
values.put(
      Calendars.CALENDAR_ACCESS_LEVEL, 
      Calendars.CAL_ACCESS_OWNER);
values.put(
      Calendars.OWNER_ACCOUNT, 
      "some.account@googlemail.com");
values.put(
      Calendars.CALENDAR_TIME_ZONE, 
      "Europe/Berlin");
Uri.Builder builder = 
      CalendarContract.Calendars.CONTENT_URI.buildUpon(); 
builder.appendQueryParameter(
      Calendars.ACCOUNT_NAME, 
      "com.grokkingandroid");
builder.appendQueryParameter(
      Calendars.ACCOUNT_TYPE, 
      CalendarContract.ACCOUNT_TYPE_LOCAL);
builder.appendQueryParameter(
      CalendarContract.CALLER_IS_SYNCADAPTER, 
      "true");
Uri uri = 
      getContentResolver().insert(builder.build(), values);

Now this code needs some explaining. First of all I create a ContentValues object that represents the values we want to add. This is the normal way to add data to a content provider. Here I add a name, the color for displaying this calendar’s data, the account it belongs to and its access rights. I also add a default time zone.

But what is not normal with the code shown above are the additional query parameters the calendar provider expects. These indicate to the provider that this code acts as a sync adapter and for which account type and account name it does this. Here the account type is ACCOUNT_TYPE_LOCAL – so this calendar will not be synced. Any other type would have to be accompanied by a full-fledged sync adapter.

Adding events using the content provider

While you cannot do anything without having at least one calendar first, you most likely want to create, change or delete events.

Since all events belong to a calendar account, I use this helper method to get the calendar id for the local calendar created in the previous section:

private long getCalendarId() { 
   String[] projection = new String[]{Calendars._ID}; 
   String selection = 
         Calendars.ACCOUNT_NAME + 
         " = ? " + 
         Calendars.ACCOUNT_TYPE + 
         " = ? "; 
   // use the same values as above:
   String[] selArgs = 
         new String[]{
               MY_ACCOUNT_NAME, 
               CalendarContract.ACCOUNT_TYPE_LOCAL}; 
   Cursor cursor = 
         getContentResolver().
               query(
                  Calendars.CONTENT_URI, 
                  projection, 
                  selection, 
                  selArgs, 
                  null); 
   if (cursor.moveToFirst()) { 
      return cursor.getLong(0); 
   } 
   return -1; 
} 

In addition to this calendar id you also have to add at least the following fields in order to create events:

  • Each event must have a time zone – even if the calendar account has a default time zone anyway
  • An event must have a start date
  • The event must either have an end date or a duration, but must not have both at the same time.

Actually the last rule is very strange. Duration in this case is not the duration of a single event. Instead it is the time span used to determine the last event of recurring events. So for single events a duration makes no sense at all. Also I consider it odd that you have to include these fields even for all-day events. But well, it’s not too hard to add them. So just be warned.

The following snippet shows how to add an all-day event:

long calId = getCalendarId();
if (calId == -1) {
   // no calendar account; react meaningfully
   return;
}
Calendar cal = new GregorianCalendar(2012, 11, 14);
cal.setTimeZone(TimeZone.getTimeZone("UTC"));
cal.set(Calendar.HOUR, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
long start = cal.getTimeInMillis();
ContentValues values = new ContentValues();
values.put(Events.DTSTART, start);
values.put(Events.DTEND, start);
values.put(Events.RRULE, 
      "FREQ=DAILY;COUNT=20;BYDAY=MO,TU,WE,TH,FR;WKST=MO");
values.put(Events.TITLE, "Some title");
values.put(Events.EVENT_LOCATION, "Münster");
values.put(Events.CALENDAR_ID, calId);
values.put(Events.EVENT_TIMEZONE, "Europe/Berlin");
values.put(Events.DESCRIPTION, 
      "The agenda or some description of the event");
// reasonable defaults exist:
values.put(Events.ACCESS_LEVEL, Events.ACCESS_PRIVATE);
values.put(Events.SELF_ATTENDEE_STATUS,
      Events.STATUS_CONFIRMED);
values.put(Events.ALL_DAY, 1);
values.put(Events.ORGANIZER, "some.mail@some.address.com");
values.put(Events.GUESTS_CAN_INVITE_OTHERS, 1);
values.put(Events.GUESTS_CAN_MODIFY, 1);
values.put(Events.AVAILABILITY, Events.AVAILABILITY_BUSY);
Uri uri = 
      getContentResolver().
            insert(Events.CONTENT_URI, values);
long eventId = new Long(uri.getLastPathSegment());

If you add an event in this way, you have not yet created any alarms or set any attendes. You will have to do so in an extra step and you have to use the id of this event to do so. I will show how to add attendes and alarms later on. In preparation for this I have already extracted the id of the event.

For a lot of fields reasonable default would be inserted if you left them out. The code shown here uses them anyway so that you know what you can do and how to do it.

Since the underlying datastore doesn’t suppport boolean values, the provider uses 1 for true and 0 for false. A common practise in Android when SQLite is involved. As you can see, I’ve set the values of ALL_DAY, GUESTS_CAN_INVITE_OTHERS and GUESTS_CAN_MODIFY to true.

The value of the rrule field looks a bit strange. First the name: “rrule” is short for recurrence rule and defines how recurring events should be inserted. In this case the rule states that the event should be repeated on every weekday for the next twenty days. The values for the recurrence rule as well as for the duration have to be given in an RFC 5545 compliant format.

Alas, the RFC formats for the duration and the recurrence rule are less than obvious. Believe me: You do not want to know the detailed rules for the recurrence format. This part of the specification alone is eight pages long and Google’s class EventRecurrence, representing and parsing those rules, is about 900 lines long. Explaining these formats is way beyond the scope of this blog post!

Reading, updating and deleting events

Getting more information about an event is pretty simple. You just need the Events.CONTENT_URI and the appropriate selection. If – for example – you know the event id you can access the event like this:

long selectedEventId = // the event-id;
String[] proj = 
      new String[]{
            Events._ID, 
            Events.DTSTART, 
            Events.DTEND, 
            Events.RRULE, 
            Events.TITLE};
Cursor cursor = 
      getContentResolver().
            query(
               Events.CONTENT_URI, 
               proj, 
               Events._ID + " = ? ", 
               new String[]{Long.toString(selectedEventId)}, 
               null);
if (cursor.moveToFirst()) {
   // read event data
}

Sometimes you want to find out more about events of the device owner. E.g. a concert planning app could have a look at the users calendar and check that she is free that evening. That’s what the next code snippet does. It checks if any event instances exist between a starting point and an ending point in time.

long begin = // starting time in milliseconds
long end = // ending time in milliseconds
String[] proj = 
      new String[]{
            Instances._ID, 
            Instances.BEGIN, 
            Instances.END, 
            Instances.EVENT_ID};
Cursor cursor = 
      Instances.query(getContentResolver(), proj, begin, end);
if (cursor.getCount() > 0) {
   // deal with conflict
}

Updating and deleting events are pretty easy tasks to accomplish. To delete you simply need the id of the event you want to delete:

String[] selArgs = 
      new String[]{Long.toString(selectedEventId)};
int deleted = 
      getContentResolver().
            delete(
               Events.CONTENT_URI, 
               Events._ID + " =? ", 
               selArgs);

For updating you need a ContentValues object containing those elements that you want to change:

ContentValues values = new ContentValues();
values.put(Events.TITLE, "Some new title");
values.put(Events.EVENT_LOCATION, "A new location");
String[] selArgs = 
      new String[]{Long.toString(selectedEventId)};
int updated = 
      getContentResolver().
            update(
               Events.CONTENT_URI, 
               values, 
               Events._ID + " =? ", 
               selArgs);

Adding attendees and alarms

As I’ve mentioned above, inserting an event is not enough. You probably want to add attendees and alarms.

Both tables reference the events table by using the field EVENT_ID with the appropriate id.

As usual you need a ContentValues object to get this done:

// adding an attendee:
values.clear();
values.put(Attendees.EVENT_ID, eventId);
values.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_REQUIRED);
values.put(Attendees.ATTENDEE_NAME, "Douglas Adams");
values.put(Attendees.ATTENDEE_EMAIL, "d.adams@zaphod-b.com");
getContentResolver().insert(Attendees.CONTENT_URI, values);
// adding a reminder:
values.clear();
values.put(Reminders.EVENT_ID, eventId);
values.put(Reminders.METHOD, Reminders.METHOD_ALERT);
values.put(Reminders.MINUTES, 30);
getContentResolver().insert(Reminders.CONTENT_URI, values); 

As explained in another post, you probably would want to use the class ContentProviderOperation to do a batch operation. But to keep the sample code as easy to read as possible, I have chosen the simpler approach shown above.

Other data

Reading, updating or deleting any of the other elements is pretty much like the sample code for events. Only the CONTENT_URI and the possible columns are different. Those are well documented in the CalendarContracts API.

If you want to insert data into other tables you have to be careful though: Only SyncAdapters are allowed to add records to Colors, SyncState or ExtendedProperties. If your ignore this restriction your code will result in an IllegalArgumentException at runtime.

Furthermore you are not allowed to manipulate data of the instances table – even if you are a sync adapter. Android will take care of instances on its own whenever an event gets added, deleted or modified. The only thing you are allowed to do with instances is to query them.

What about older Android devices?

As mentioned, the Calendar app and the content provider have been introduced with Ice Cream Sandwich. Even though the market share of 4.x devices increases steadily, most devices out there are still 2.x devices. Does anything comparable exist for them as well?

Well, kind of. Of course nothing official existed prior to ICS and the emulator images for older devices didn’t and still don’t have a calendar app. But nearly every device has. Even the sources existed – though marked with the @hide annotation, so that you can’t use the classes (e.g. the final static fields for the columns) in your code.

This is a sign that Google regarded the code as yet not ready for prime time. Well, rightly so, since it is still buggy in places – though mostly in the app, as I will explain in my next post.

If you take a look at the Calendar content provider of Android 2.3.7 at grepcode you can see that the data model is pretty similar to the one of the current provider. If you diff older code and newer code you will notice how close both are. Most changes deal with the new table colors. Obviously any code that relies on this table is doomed to fail on older devices.

So can you simply act as if the provider had been around for ages? Well, no. You can’t! The code used in this tutorial would result in NoClassDefFoundErrors.

But, and this is the good news, the old provider can still be used for most tasks without too much changes. For this to work you mostly have to exchange all constants with their actual values.

Be very careful if you do so! You have to test vigorously and you risk running into problems if the values (column names, content uris and so on) should ever change. And be prepared for content providers that do not match this data model – you simply cannot rely on it. Try to isolate the critical parts as much as possible.

If you want to know more about accessing calendars prior to ICS, you should read the blog post of Jim Blackler about the old internal calendar database.

Wrapping up

In this part of the tutorial I have shown you how to use Android’s CalendarContract content provider.

Using the content provider you can add events or get information about the user’s events and you can also check for conflicting dates.

To make full use of the content provider you have to be a sync adapter. But using a device local calendar account you can also deal with calendars in many ways.

In the next part of this tutorial I am going to cover the Intents that are provided by the Calendar app. For some use cases they might be the better choice – but of course you have much more flexibility by using the content provider.

Useful Android Libraries: Crouton

One way to notify users is to use Toasts. But Toasts have the problem that they might pop up in totally unrelated contexts. They are displayed for a defined duration on the screen no matter what the user does. The user might even have changed the app, with the result, that your Toast simply confuses the user.

Cyril Mottier outlined the problems with Toasts in his blog post about Prixing’s notification mechanism. He also outlined the solution he has chosen for Prixing – and why it is better.

What are Croutons?

His idea is to show in-app notifications (not to be confused with Android’s persistent notifications) at a fixed place of the Activity to which the notification is relevant. This way the context of the notification is always correct. Furthermore he used different styles for different types of notifications.

Since he couldn’t release any source code, Benjamin Weiss took this concept and created the open source library Crouton, hosted at Github.

To give you a better idea of what a Crouton looks like, let me show you these two screenshots:

A confirmation Crouton

A confirmation Crouton


An alert Crouton

An alert Crouton

These screenshots show a confirmation and an alert Crouton. Another style you could use would be the info style, which uses the Holo blue. As you can see a Crouton appears at the top of the screen and is displayed as an overlay atop of the current Activity. The content of the view remains exactly as it is.

How to use the library

Before you can use the library, you first have to get it. No binaries are available, but since the project is hosted at github, you can easily clone the project:

git clone git://github.com/keyboardsurfer/Crouton.git

If you are using maven, follow the instructions on the Crouton project page.

If you use Eclipse you simply add the library folder as an existing Android project to your workspace. Since no Eclipse-specific files are included in the sources, you have to change the “Project Build Target” in the Android tab of the Eclipse project to at least 4.0 (API-Level 14).

You also have to check the “Is Library” checkbox:

Project settings in Eclipse for the Crouton library project

Project settings in Eclipse for the Crouton library project

Next you have to include the Crouton library to the project that should use this project. You do this in the same dialog as shown above by selecting the “Add” button.

After these initial steps you can use Croutons as easily as Toasts. The methods to use are as close to those of Toasts as possible. You have two static makeText() methods as well as the show() method:

Crouton.makeText(
         this, 
         getString(R.string.crouton_message), 
         Style.CONFIRM)
      .show();

From using Toasts we all now how easy it is to forget the show() method call. Gladly the Crouton library adds the convenience method showText() to skip the extra step necessary with Toasts:

Crouton.showText(
      this, 
      getString(R.string.crouton_message2), 
      Style.CONFIRM);

The library queues Croutons and presents them subsequentially. But, please, keep the amount of notifications to a minimum. Otherwise you quickly will annoy your users.

You can also use custom styles. In this case you have to use the Style.Builder class, set the desired properties and build a style. As the next snippets shows, the Builder uses a fluent interface which you terminate with the use of the build() method.

int heightInPx = getResources()
      .getDimensionPixelOffset(R.dimen.custom_crouton_height);
Style style = new Style.Builder()
      .setBackgroundColor(R.color.horribly_pink)
      .setDuration(10000)
      .setGravity(Gravity.LEFT)
      .setTextColor(android.R.color.black)
      .setHeight(heightInPx)
      .build();
Crouton.makeText(
         this, 
         getString(R.string.crouton_message), 
         style)
      .show();

There is a bug (or a misunderstanding on my side) with the setHeight() method. It causes the text to move right for exactly the same amount of pixels. As I see it, this happens in the buildImageLayoutParams() method of the project’s ViewHolder class. I will dig into this as soon as I can and will either file a bug report or submit a patch for this.

On the project site is the warning, that the project “requires the Holo theme to be present on the device“. Now this would limit the use of the library significantly. But the warning is overblown.

You can use the library and the Croutons show up fine. Only the default Styles do not work – all Croutons are displayed in grey.

If your app needs to work on older devices (which it probably does), you have to use custom styles as shown above. I suggest to define constants in the same way, the project itself creates them – but with your resource definitions for the colors to use. Have a look at the Style class for how the Crouton project does it.

A Crouton using the custom style definition just shown, looks like this on a 2.2 device:

Sample Crouton on an Android 2.2 device

Sample Crouton on an Android 2.2 device

The demo project

When you clone the github project, you will notice that the sources include a demo project. The two screenshots shown at the beginning of this post are taken using this demo project.

You can either build this project using Maven or you can import it into Eclipse to have a look around and to try to change stuff. If you want to use it in Eclipse, you have to add the Crouton library. Assuming you have imported the Crouton library project, you can do this by simply opening the project properties and selecting the library:

Adding the library to the demo project

Adding the library to the demo project

Summary

I think Cyril Mottier made a good point about the problems of Android’s Toasts. Thanks to Benjamin Weiss Croutons are here to solve these issues for us. The library is very simple to use but thanks to the Style.Builder flexible enough to accommodate your needs.

I have found two minor issues – which both should be fixable. I will keep you posted about them.

And on a completely unrelated note: While I was busy completing this blog post, Marie Schweiz created the icon for the Crouton project. Nice coincidence :-)

Take These Steps to Make your Android App Accessible

Accessibility in an app helps users with disabilities to use your app with the help of the Android system. Android’s accessibility system tools as well as third-party developer’s tools provide features to help with visual impairment.

In this post I outline why you should care about accessibility and what steps to take to make your app accessible. I also deal with testing and why it’s useful to care about accessibility up front.

Accessibility is a usability requirement

When it comes to planning a project, accessibility often is neglected or just mentioned briefly – without ever detailing what it means for the project at hand. That’s odd. Why this neglect?

We agree that it’s useful to make users aware of the flyover menu. Or to add hints to ease the discoverability of a bezel swipe. We sometimes go to great lengths to improve the usability of an app. But not when it comes to accessibility.

Of the many Android development books that I know, only two cover accessibility: Reto Meier’s “Professional Android 4 Application Development” and Jasons Ostrander’s “Android UI Fundamentals“. That’s a sorry state, in my opinion.

Accessibility is no after-thought

It is up to the designers and usability experts to work out the concept of the app – including everything that has to do with accessibility. Even though, as you have seen, it is pretty easy to implement, it is probably not so easy to get the concept right.

For example, users can change the font-size of their device. This can have a big influence of which design works and which doesn’t.

With this in mind, you also have to decide what text shouldn’t get scaled when this setting changes. And if there is a good reason for this. What would an unscaled text mean for people that need big text?

For example the stock email app does scale the text of the message list, but not of the folders. You have to decide thoroughly – and if possible test thoroughly – if and where this is appropriate.

Furthermore the size of clickable areas should be designed to be big enough for all kind of fingers and hit-precisions. Think of kids, of elder people, people with tremors and so on.

Think of people with color blindness. Do use clear contrasts and do not use colors only to signal the state of something. I am currently writing an app for a server monitoring service. Obviously red signals a critical situation whereas green is used for a properly functioning server. But we do not rely on color alone, but add text to distinguish both furthermore.

Then the order in which controls should be focusable has to be agreed upon right from the beginning. This is a minor change, but it’s probably better not up to the developer’s imagination.

Also you should plan on the labels for textual descriptions early on. You think thoroughly about any text displayed on the screen, so do the same for these descriptions. Otherwise you have to quickly come up with descriptions when you want to go live. Or – even worse – you might end up with the placeholders the developers used ;-)

Finally do not forget to add feedback mechanisms beyond audio. No matter if people have hearing disabilities or not, you should always provide additional means to notify users. All users turn off audio in certain situations. So if you use a signal sound to alert a user of something, consider to also use haptic and visual feedback as well.

How to make your apps accessible

Gladly it is easy for most apps to make them accessible. Most often all you need to do is:

  • Support directional navigation
  • Add descriptions to UI elements

If you need controls not provided by the Android platform, you have to care about accessibility as well. Since I plan a mini-series about writing custom controls anyway, I will explain this as one part of this mini-series.

Support directional navigation

Users might find it easier to use the D-pad to navigate around your app. This is not only true for users with disabilities but might be more comfortable if users are distracted in some way or simply prefer this to touching depending on circumstances.

Without fine-tuning the navigational flow of controls, though, D-pad navigation might be cumbersome at some places.

Most often Android provides a reasonable navigational order. But if not or if you want to tweak it to improve upon this, you can do so by stating which element to focus next – for all four directions supported:

  • android:nextFocusUp
  • android:nextFocusDown
  • android:nextFocusLeft
  • android:nextFocusRight
  • android:nextFocusForward

Of these only nextFocusForward might need some explaining. This denotes the element to focus when the user hits “next” on the soft keyboard or uses a gesture to move to the next focusable element.

The following snippet shows an example of how to do this:

<EditText
   android:id="@+id/title"
   android:layout_width="fill_parent"
   android:layout_height="wrap_content"
   android:hint="@string/title"
   android:nextFocusDown="@id/description"
   android:singleLine="true" />

<!-- ... -->

<EditText
   android:id="@+id/description"
   android:layout_width="fill_parent"
   android:layout_height="wrap_content"
   android:hint="@string/description"
   android:nextFocusUp="@id/title" />

The target view must be focusable of course. All standard controls of android are focusable by default. But if you write your own custom-controls you have to take care of this yourself.

Add descriptions to UI elements

For some UI elements like image buttons and so on a screen reader is at a loss of what to read, if you do not help it. But helping is very easy to do. You just need to add a content description to these elements.

You can do so either when adding the views to the xml layout files:

<ImageButton
   android:id="@+id/btn_contact"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:contentDescription="@string/contact"
   android:src="@drawable/btn_contact" 
/>

Or you can add content descriptions programatically if they have to be dynamic:

ImageButton button = 
      (ImageButton)findViewById(R.id.btn_contact);
button.setContentDescription(getString(R.string.contact));

If a graphical element is used only to please the user visually without any further use, you should set the content description to “@null”. That way screen readers know that they should ignore this element.

Test accessibility

To test the accessibility features of your app you need to test whether your changes work.

You can test the D-pad functionality easily in the emulator. You can either use the D-pad of the emulator or the arrow keys of your keyboard.

For testing that users with limited vision can handle your app, you can use TalkBack. This app is included on most android devices and can also be downloaded from the play store. Alas it is not included in the emulator. So you need to test on a real device.

Try your app without looking at the screen. Can you navigate around? Can you identify all elements and understand what they are used for?

You also have to test if your app looks reasonably good, if the user changes the default font-size. Especially activities with many views and relatively small UI elements might look bad with bigger text.

Don’t forget to test the feedback mechanisms. Do they work without sound?

All in all testing accessibility should be based on the requirements outlined in the concept (see the section “Accessibility is no after-thought“). If the design of your app contains hints on what accessibility mechanisms to use, testing becomes straight-forward. Another reason why accessibility should be planned and described up front.

More on this topic

Google itself provides useful information about this topic in the training, developers and design sections of Android’s site.

The design section contains a page on accessibility within its pattern section.

The developer section contains a very detailed article about implementing accessible apps as well as an accessibility checklist.

The training section contains the guide “Developing Accessible Applications“.

And – oddly placed – the tools section has a nice document on accessibility testing.

TV Raman, an accessibility research scientist, blogs on Eyes-Free Android about accessibility features of Android. And he has uploaded many videos explaining Android’s accessibility features to Youtube. He points out that these features are not only for visually-impaired people but might be useful in certain conditions for all of us.

There is also the Google IO talk on accessibility.

Finally for Nexus devices you can find the accessibility user guide on Google’s support pages. For example, here’s the accessibility guide for the Nexus 7.

Not covered in this post

As mentioned, I didn’t cover adding accessibility features to your custom controls. This will be the topic of a post on its own.

I also didn’t cover how to write accessibility services. These are tools, that give the user some kind of feedback on certain events. This could be reading out a text when the user navigates to an element using his D-pad or it might be some haptic feedback on a button press and so on. Google provides some tools as part of the Android platform, other vendors might add their own. The user can select which services to use in Android’s settings. Google has a thorough guide on how to code an accessibility service.

Wrapping up

In this post I have outlined why you should care about accessibility and what steps to take, to make your app accessible.

I also covered that you should care about accessibility already while designing and drafting your app and how to test your app later on.

I mentioned hints on how to test accessibility and pointed out where to find more ressources.

So what’s holding you back? Anything you miss to make your app accessible?

Note: I am no native speaker. So if anyone feels offended by any expression used here, please leave a comment on why this is and what other expression to use.

Disclaimer: Some of the links contained within this site have my Amazon referral ID, which provides me with a small commission for each sale. Thank you for your support.

How to Use Loaders in Android

With the introduction of Honeycomb Loaders became the preferred way to access data of databases or content providers. They load data asynchronously and notify listeners when the results are ready.

Google did not only introduce Loaders but also deprecated the previous way to handle a Cursor within your activities. You shouldn’t use startManagingCursor() or managedQuery() in your projects anymore.

With managed cursors queries and requeries are executed on the UI thread. This could cause the app to feel unresponsive or to even display an ANR error message. With Loaders your queries will no longer run on the UI thread and your app remains responsive.

In this post I introduce the classes that form the Loader API and show you how to use them.

The classes and interfaces of the Loader API
Class Usage
LoaderManager Manages your Loaders for you. Responsible for dealing with the Activity or Fragment lifecycle
LoaderManager.LoaderCallbacks A callback interface you must implement
Loader The base class for all Loaders
AsyncTaskLoader An implementation that uses an AsyncTask to do its work
CursorLoader A subclass of AsyncTaskLoader for accessing ContentProvider data

In the following sections I describe most of these classes and what you need to know about them – starting with the LoaderManager.

LoaderManager

This class keeps your Loaders in line with the lifecycle of your activities or fragments. If Android destroys your fragments or activities, the LoaderManager notifies the managed loaders to free up their resources. The LoaderManager is also responsible for retaining your data on configuration changes like a change of orientation and it calls the relevant callback methods when the data changes. In short: The LoaderManager is way more powerful than the old startManagingCursor() or managedQuery() methods.

You do not instantiate the LoaderManager yourself. Instead you simply call getLoaderManager() from within your activity or your fragment to get hold of it.

Most often you are only interested in two methods of the manager:

  • initLoader() and
  • restartLoader()

initLoader()

The initLoader() method adds a Loader to the LoaderManager:

getLoaderManager().initLoader(LIST_ID, null, this);

It takes three arguments:

  • a unique ID for this loader,
  • an optional Bundle with arguments for your Loader and
  • a LoaderCallbacks interface

You might need the ID for further method calls. So using a final static field for the ID makes your code more readable. The Bundle can be used to pass additional arguments to your Loader, but isn’t used by the CursorLoader. The third argument, the callback interface, will be covered in detail later on.

The initLoader() method creates a new Loader only if for this ID none has been created previously. Keep in mind that Android deals with configuration changes for you, thus a simple change in orientation is enough to trigger a new call to initLoader(). In this case the method returns the existing instance and your query is not executed again.

restartLoader()

Because Android doesn’t execute the query again, you need a way to re-initialize the Loader when data, that is used to build the query, changes. Typical examples are search queries.

You reset your Loader by using the restartLoader() method. It takes the same parameters as initLoader(). Of course you have to use the same ID you used for initializing.

getLoaderManager().restartLoader(LIST_ID, null, this);

LoaderManager.LoaderCallbacks

The interface LoaderCallbacks defines methods you must implement to create your Loader, to deal with the results and to clean up resources.

Since the interface is parameterized you must specify the type of data your Loader holds. Most often the type will be Cursor:

public class YourFragment extends Fragment 
      implements LoaderCallbacks<Cursor> {
   //...
}

The methods you have to implement are:

  • onCreateLoader(),
  • onLoadFinished() and
  • onLoadReset()

In the next sections I show what to do in each of these callback methods.

onCreateLoader()

The LoaderManager calls this method when you call initLoader() for the first time. As mentioned, the manager only calls this method if no loader for the given ID exists.

The method gets an int value and a Bundle passed in. These are the same values you used for your initLoader() call.

A typical example creating a CursorLoader looks like this:

public Loader<Cursor> onCreateLoader(int id, Bundle args) {
   CursorLoader loader = new CursorLoader(
         this.getActivity(),
         SOME_CONTENT_URI, 
         projection, 
         selection, 
         selectionArgs, 
         sortOrder);
   return loader;
}

As you can see the parameters are a Context object plus those of the ContentResolver’s query() method. If you’re not familiar with these arguments, I recommend you read my post about accessing content providers.

If you need to track multiple queries and thus use different IDs for your Loaders, all you need to add is a simple case- or if-else-branch.

onLoadFinished()

This method is the most interesting one. Here you update the UI based on the results of your query.

For ListAdapters you simply swap the cursor of the adapter as described in the section “Changes needed for CursorAdapters“.

For all other cases you have to get references to the view elements and set their value according to the result.

This is how it looks in the sample project:

public void onLoadFinished(
      Loader<Cursor> loader, 
      Cursor cursor) {
   if (cursor != null && cursor.getCount() > 0) {
      cursor.moveToFirst();
      int idIndex = 
            cursor.getColumnIndex(LentItems._ID);
      int nameIndex = 
            cursor.getColumnIndex(LentItems.NAME);
      int borrowerIndex = 
            cursor.getColumnIndex(LentItems.BORROWER);
      this.itemId = cursor.getLong(idIndex);
      String name = cursor.getString(nameIndex);
      String borrower = cursor.getString(borrowerIndex);
      ((EditText)findViewById(R.id.name)).
            setText(name);
      ((EditText)findViewById(R.id.person)).
            setText(borrower);
   }
}

onLoadReset()

This method allows you to release any resources you hold, so that the Loader can free them. You can set any references to the cursor object you hold to null.
But do not close the cursor – the Loader does this for you.

See also the section about how to deal with CursorAdapters.

Loader, AsyncTaskLoader and CursorLoader

The Loader interface and its implementations are not very interesting – unless you write your own custom Loaders. You have to create a Loader of course. But other than using the constructor of CursorLoader, you normally do not interact with these objects yourself.

If you use multiple Loaders you need to access the ID in the callback methods. You can do so by calling getId() on the loader passed in to the callbacks.

If you want to write a custom Loader yourself, please have a look at Alex Lockwood’s tutorial on implementing loaders.

Changes needed for CursorAdapters

An important use of cursors in Android is to use a CursorAdapter as data source for ListViews, AutoCompleteTextViews and so on. When working with Loaders you have to adapt the old way slightly.

First of all: You do not have a Cursor object before the onLoadFinished() method of your callback has been called. In other words: The cursor is not ready when you create the adapter. Thus you create the adapter using null for the cursor argument:

SimpleCursorAdapter adapter = 
      new SimpleCursorAdapter(
            getApplicationContext(), 
            android.R.layout.simple_list_item_1, 
            null, 
            columns, 
            layoutIds, 
            0);

When the cursor is finally available you have to add it. You do this by calling swapCursor() on your adapter and passing in the cursor object of your callback method:

public void onLoadFinished(
      Loader<Cursor> loader, 
      Cursor cursor) {
   ((SimpleCursorAdapter)this.getListAdapter()).
         swapCursor(cursor);
}

And to clean up resources in the onLoadReset() method you also use swapCursor(), this time passing in a null value:

public void onLoaderReset(Loader<Cursor> loader) {
   ((SimpleCursorAdapter)this.getListAdapter()).
         swapCursor(cursor);
}

Use the Support Library for older Android versions

To support newer features on older Android versions, Google provides the Support Library. The two main components this library contains are Fragments and Loaders.

You can and should use this library for projects that must support older versions. All reasons for using the Loader-API are valid for pre-Honeycomb devices as well.

But you have to keep an eye on the import statements. All import statements for classes of the Support Library begin with android.support.v4:

import android.support.v4.app.LoaderManager;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;

Don’t mix classes of the support library with normal classes. The compiler will spot most problems for you and then there is also Lint to help you. But if you ever wonder why some code is marked as invalid, this most likely is the reason.

To use Loaders for older Android versions, you need to use activities or fragments of the Support Library. The normal ones do not have the getLoaderManager() method before Honeycomb. You project would still compile (if your build target is at least SDK 11) but at runtime you would get a NoSuchMethodError on older devices.

Using Loaders to access your SQLiteDatabase

Since Android’s CursorLoader is only for Cursors returned by content providers we need another Loader implementation if we want to use SQLite directly.

Thankfully Mark Murphy has written a library that offers enhancements to the Loader framework. This library also contains a SQLiteCursorLoader.

To use Mark Murphy’s SQLiteCursorLoader you have to create an SQL select statement and pass it to the constructor.The constructor also expects an SQLiteOpenHelper object. An example of the onCreateLoader() method could look like this:

public Loader<Cursor> onCreateLoader(int id, Bundle args) {
   String rawQuery = "SELECT ...";
   String[] queryParams = // to substitute placeholders
   SQLiteCursorLoader loader = 
   new SQLiteCursorLoader(
         getActivity().getApplicationContext(), 
         yourSqliteOpenHelper, 
         rawQuery, 
         queryParams);
   return loader;
}

But there is more to it. You have to use this loader also for deleting, updating or inserting rows as well. Only by doing so, the loader knows about a data change and can call the correct callback methods.

I won’t go into these details here. See the project’s github page or have a look at Mark Murphy’s sample project.

If you intend to use this library, don’t use the jar-file offered on the project page. It is outdated. Instead clone the git repository and add the project to your IDE directly.

When not to use Loaders

On reddit, cokacokacoh noted that my post missed a section on when not to use Loaders. He was right of course. So I added the following paragraph to this post.

As cokacokacoh points out, you shouldn’t use Loaders if you need the background tasks to complete. Android destroys Loaders together with the Activities/Fragments they belong to. If you want to do some tasks, that have to run until completion, do not use Loaders. You should use services for this kind of stuff instead.

Keep in mind that Loaders are special components to help you create responsive UIs and to asynchronously load data that this UI component needs. That’s the reason why Loaders are tied to the lifecycle of their creating components. Do not try to abuse them for anything else!

Lessons learned

In this blog post you have seen how to use the Loader framework. And since this framework is available for older Android versions using the Support Library there is no reason not to use it.

The CursorLoader provided by Android is useful for content providers. But not all projects use these. If your project uses SQLite directly you can use Mark Murphys SQLiteCursorLoader as shown above.

I didn’t cover how to write Loaders on your own. If you need to do so, +Alex Lockwood has a nice tutorial on implementing loaders.

Please let me know in the comments if this post was helpful or if you have any questions left. And don’t forget to plus one or tweet this post, if you liked it :-)

Android Quick Tip: Enabling and Disabling BroadcastReceivers at Runtime

BroadcastReceivers are good when you want to be notified about system events. But sometimes you do need to know about an event only once or for a short period of time.

A dynamically registered receiver doesn’t cut it because the Activity and it’s dynamically registered broadcast receiver might long be dead when the event occurs. The only way to achieve this is by enabling and disabling broadcast receivers in your code.

Enabling or Disabling BroadcastReceivers

You enable or disable receivers by using methods of the PackageManager class. With this class you can enable and disable app components at runtime:

PackageManager pm = getPackageManager();
ComponentName compName = 
      new ComponentName(getApplicationContext(), 
            YourReceiver.class);
pm.setComponentEnabledSetting(
      compName,
      PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 
      PackageManager.DONT_KILL_APP);

There are three states the PackageManager offers for this:

Supported states of setComponentEnabledSetting()
COMPONENT_ENABLED_STATE_DEFAULT Sets the state to the manifest file value
COMPONENT_ENABLED_STATE_DISABLED Sets the state to disabled
COMPONENT_ENABLED_STATE_ENABLED Sets the state to enabled

By default the PackageManager kills your app immediately, since a component state change could lead to unpredictable situations. The flag DONT_KILL_APP prevents this from happening and is safe to use for BroadcastReceivers. Of course if you do not use the DONT_KILL_APP-flag, you will spot your mistake even with the most cursory of testings :-)

Disabling BroadcastReceivers by default

If you want to enable your receiver at runtime, you can set the state to disabled initially. You can do so in the manifest file:

<receiver
   android:name=".YourReceiver"
   android:enabled="false" >
   <!-- your intent filter -->
</receiver>

When to use this

The basic reason why you should do this, is that you want to preserve valuable resources on your user’s devices. You do not want to drain the battery by running code that is not relevant to your user. Most often you can achieve this by using a dynamically registered receiver. But this does not always work.

Three examples where you should explicitly use the PackageManager-solution presented above:

1. You might need to know about the next boot, but only the next one. In this case you cannot use a dynamically registered receiver. You have to use a statically registered one. But you do not want to run it on every boot completion. Which means that you have to disable the receiver after it’s first run.

2. If one or more receivers depend on the state of a specific system service you can disable all of them for as long as the necessary service is not in the desired state. This could be true for network connectivity, while waiting for a GPS fix, for missing Bluetooth availabilty and such.

3. You intend to use notifications – but only if the app is not currently active. In this case your BroadcastReceiver has to be enabled by default. But you would disable it in your Activities’ onResume() method and re-enable it in the onPause() method.

See also Reto Meier’s blog post about location-based apps and have a thorough look at the accompanying source code. He covered these topics initially in his talk at Google’s IO 2011. I highly recommend to watch the video of this talk. It’s definitely a good watch!

Recording Audio using Android’s MediaRecorder Framework

Sometimes your app needs the ability to record and store audio files. As most devices come with a microphone it’s no surprise that Android offers app developers the possibility to do so. This post deals with the two most common ways to record audio on Android.

Recording audio using an Intent

As usual that’s by far the easiest option. You simply create the Intent object and check for its availability. When you have done so, start the Activity:

Intent intent = 
      new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION);
if (isAvailable(getApplicationContext(), intent)) {
   startActivityForResult(intent, 
         REQUESTCODE_RECORDING);
}

You have to use startActivityForResult() since you want to know which file has been created in your onActivityResult() method. REQUESTCODE_RECORDING is a final static int variable that you have to create. You can give it any name you like. The value of this field must be unique among all startActivityForResult() calls that you might want to use.

You need this int field again when checking for the result:

protected void onActivityResult(int requestCode, 
      int resultCode, Intent intent) {
   if (requestCode == REQUESTCODE_RECORDING) {
      if (resultCode == RESULT_OK) {
         Uri audioUri = intent.getData();
         // make use of this MediaStore uri 
         // e.g. store it somewhere
      }
      else {
         // react meaningful to problems
      }
   }
   else {
      super.onActivityResult(requestCode, 
            resultCode, intent);
   }
}

The standard activity to record audio looks like this:

Standard activity for audio recording

Standard activity for audio recording

Alas using an intent has the usual drawback: The intent might not be available. And, indeed, on my old LG Optimus One (P500) it doesn’t work :-(

So be sure to at least check for intent availability before trying to use it.

A problem with this solution is the location, that Android uses to store the recorded file. Android puts the file directly to the root of the SD card – something that often annoys users.

Doing it yourself

If you want to have more control over audio recording you could use the MediaRecorder.

But first you have to cover the basics – permissions. As usual you have to state that your app wants to record audio in the mainfest file. Add these lines to your AndroidManifest.xml:

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

For the intent-based solution above you do not need this permission. But for using the MediaRecorder framework, you do. Without the correct permission you get an exception that is very misleading:

java.lang.RuntimeException: setAudioSource failed.

This is Android’s strange way to tell us, that we forgot to ask for this permission :-)

Using the MediaRecorder

Recording audio with the MediaRecorder requires you to adhere to the MediaRecorder's state transitions, so you have to be strict with the ordering of your operations. To start a recording you have to do the following steps:

  1. Create a MediaRecorder object
  2. State the source to use
  3. Set the file format
  4. Set the Encoding
  5. Prepare a file
  6. Start recording

And here it is in code:

MediaRecorder recorder = null;

private void startRecording(File file) {
   if (recorder != null) {
      recorder.release();
   }
   recorder = new MediaRecorder();
   recorder.setAudioSource(AudioSource.MIC);
   recorder.setOutputFormat(OutputFormat.THREE_GPP);
   recorder.setAudioEncoder(AudioEncoder.AMR_WB);
   recorder.setOutputFile(file.getAbsolutePath());
   try {
      recorder.prepare();
      recorder.start();
   } catch (IOException e) {
      Log.e("giftlist", "io problems while preparing [" +
            file.getAbsolutePath() + "]: " + e.getMessage());
   }
}

You need the MediaRecorder object later on to stop the recording and to clean up, so you have to use an instance variable for it.

Please follow Android’s best practices about storing files. In most cases your app-specific folder is the best choice for recorded files.

What could go wrong?

Now the order in which you call the methods of the MediaRecorder object is very important. If you stick with the order of the code snippet above, all is fine. If not you will see one of the following exceptions or messages in your log file:

  • java.lang.IllegalStateException
    Happens when you do not follow the order above, miss some important steps or when prepare() failed, but you continue anyway. The latter is where it is most likely to go wrong. You should only call start() when prepare() did exit without exceptions. Otherwise you will get an IllegalStateException. Since it is a RuntimeException you might not catch it and your app will force close. That’s where the documentation sample code is misleading.
  • mediarecorder went away with unhandled events
    Happens when you release the Mediarecorder object while events were still being processed or queued. No need to worry, though. Everything went fine anyway – this message is just to notify you about this.
  • Fatal signal 11 (SIGSEGV)
    That’s a really bad one. Not a Force close, but your app simply vanishes and then restarts. This segmentation fault can occur when you call release() and still use the object afterwards.
    Can also happen if you call reset() on a MediaRecorder object that hasn’t even been prepared. The next method call on the MediaRecorder object will cause the segmentation fault to occur.

An example of what could go wrong: I tried to optimize above code by just calling reset() at the start of the method when the recorder object already exists. All nice and dandy if never ever anything goes wrong. But if for example the prepare stage goes wrong, this optimization fails miserably (segmentation fault again). That’s why I got rid of it and now simply call release() before creating a new MediaRecorder object.

Stopping the recording

With stopping you have to follow the ordering of the state chart in MediaRecorder’s documentation again. But since there are only two steps, this is easy:

private void stopRecording() {
   if (recorder != null) {
      recorder.stop();
      recorder.release();
      recorder = null;
   }
}

Releasing resources

When everything is done, you have to release resources. You should do so in the onPause() method of your activity, as you can see in the next snippet:

protected void onPause() {
   super.onPause();
   if (recorder != null) {
      recorder.release();
      recorder = null;
   }
}

Summary

You have seen how to leverage the RECORD_SOUND_ACTION action to use an intent for recording. Since this intent is not always available, or because you might want to present another UI, you can also use the MediaRecorder framework directly.

There is an even more fine-grained way to record audio in Android by using AudioRecord. This class allows you to deal with raw audio data. I will not go into this since it should be of interest only to few developers.

Adding Files to Android’s Media Library Using the MediaScanner

When you add files to Android’s filesystem these files are not picked up by the MedaScanner automatically. But often they should be.

As far as I can tell from a cursory search through Android’s codebase, Android runs a full media scan only on reboot and when (re)mounting the sd card. This might sound bad at first – but think about it. A full scan is taking quite some time and you do not want this to happen arbitrarily, maybe even while the user is using his device heavily.

This means, any file, that must be available in the media library instantaneously, has to be added by you. You have the responsibility to get this right. Even more so, as new devices might support MTP, which means that all files – not only media files – have to made known. Read more about this in the section “Not only relevant for media files”.

If you want your files to be added to the media library, you can do so either by using the MediaStore content provider, or by using the MediaScanner. In this post I deal with the MediaScanner only. In a later post I will cover how to use the MediaStore provider for making your files known.

Adding the files using a broadcast

The simplest way to do so is by sending a broadcast:

Intent intent = 
      new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
intent.setData(Uri.fromFile(file));
sendBroadcast(intent);

This works perfectly well and you should be fine with this solution most of the time.

But if you want to be more in control of what is happening, you could use one of the following two methods.

Using the static scanFile() method

If you simply need to know when the files have been added, you could use MediaScannerConnection’s static method scanFile() together with a MediaScannerConnection.OnScanCompletedListener.

The static method scanFile() is badly named, as it actually takes an array of paths and thus can be used to add multiple files at once and not just one – but it nevertheless does what we want :-)

Here’s how to use this method:

MediaScannerConnection.scanFile(
      getApplicationContext(), 
      new String[]{file.getAbsolutePath()}, 
      null, 
      new OnScanCompletedListener() {
         @Override
         public void onScanCompleted(String path, Uri uri) {
            Log.v("grokkingandroid", 
                  "file " + path + " was scanned seccessfully: " + uri);
         }
      });
Parameters for the static scanFile() method
Parameter Use
context The application context
paths A String array containing the paths of the files you want to add
mimeTypes A String array containing the mime types of the files
callback A MediaScannerConnection.OnScanCompletedListener to be notified when the scan is completed

The OnScanCompletedListener itself must implement the onScanCompleted() method. This method gets the filename and the URI for the MediaStore.Files provider passed in as parameters.

Creating an instance of MediaScannerConnection

This is the most complex way to make your files known. But it gives you even more control. You need to implement the MediaScannerConnection.MediaScannerConnectionClient for callbacks.

Your MediaScannerConnectionClient's implementation is not only called whenever a scan has been completed, but also as soon as the connection has been established. Since this might take a little while, as described in the next section, you might be interested in this callback. The way the API is constructed, you actually need to use this callback method to start the scan.

The following snippet shows you a sample implementation of the MediaScannerConnectionClient interface.

final class MyMediaScannerConnectionClient 
      implements MediaScannerConnectionClient {

   private String mFilename;
   private String mMimetype;
   private MediaScannerConnection mConn;
   
   public MyMediaScannerConnectionClient
         (Context ctx, File file, String mimetype) {
      this.mFilename = file.getAbsolutePath();
      mConn = new MediaScannerConnection(ctx, this);
      mConn.connect();
   }
   @Override
   public void onMediaScannerConnected() {
      mConn.scanFile(mFilename, mMimetype);
   }
   
   @Override
   public void onScanCompleted(String path, Uri uri) {
      mConn.disconnect();
   }    	
}

In this implementation I create the MediaScannerConnection within the constructor and here I also call its connect() method.

Note also that I start the scan within the onMediaScannerConnected() callback method.

This way the usage of this interface is as simple as it can get:

MediaScannerConnectionClient client = 
      new MyMediaScannerConnectionClient(
            getApplicationContext(), file, null);

Establishing a connection might take a while

Be aware that the connection is not established right away. That’s why the following snippet would cause trouble (that’s the first time I noticed problematic code in Mark Murphy’s otherwise highly recommended book):

/////// Do not do this ! ///////
MediaScannerConnection c = 
      new MediaScannerConnection(
            getApplicationContext(), null);
c.connect();
c.scanFile(file.getAbsolutePath(), null);
c.disconnect();
/////// Do not do this ! ///////

This snippet would fail with an IllegalStateException:

java.lang.IllegalStateException: not connected to MediaScannerService

When doing some tests it took on my Optimus One (with an sd card) and on my Galaxy Nexus (with an extra partition) about 20 to 40 milliseconds to establish the connection – on my Archos tablet (also extra partition) about 100 milliseconds.

The scan itself takes even longer – which took me by surprise. On my Archos and Optimus One about 200 milliseconds – on my Nexus even up to 900 (!) milliseconds on its worst run.

Not only relevant for media files

With devices that use MTP instead of the traditional USB storage protocol, the MediaScanner is also used to make arbitrary files accessible via MTP. Thus any files and folders that you do not add to the MediaScanner are invisible between full scans if you plug in your device to your computer!

With the introduction of Honeycomb Google has started to push for MTP. Even if not all handset makers follow Google’s decision, some might – and Google’s own Nexus line definitely does so.

This means that you should use the MediaScanner also for any files that the user might want to download to his computer. It could be anything, e.g. CSV backup files, PDF files and so on. If the user might want to use them on a traditional computer, you have to make these files known using the methods described above.

Happy coding!

Subscribe to RSS Feed My G+-Profile Follow me on Twitter!