Adding ActionBar Items From Within Your Fragments

This tutorial shows you how to add action items to the ActionBar from within your Fragments and how to disable or enable action items depending on the state of your fragment. All code works with the stock Android ActionBar object as well as with ActionBarSherlock.

Why do you want to add action items?

In a previous post you have seen that Android makes it easy to provide responsive interfaces. And given the vast distribution of devices sizes that is something you should do.

This post continues with the app, you have developed in the first posts of this series. The user has a list of items, a detail screen and an edit screen.

On a phone the startscreen simply shows a list of items. Here you have an action item to add a new item to the list:

Screen showing the ListFragment only

Screen showing the ListFragment only

The detail screen shows all the relevant details of this item and offers you to edit the item:

Screen showing the DetailFragment only

Screen showing the DetailFragment only

Now here’s what the tablet version looks like:

Top part of the tablet version containing both fragments

Top part of the tablet version containing both fragments

As you can see, it now sports both icons since it also presents both fragments. Now this app is pretty dull so far, and the detail screen even more so, so let me show you a real app. I take Timetable, an app that does this nicely.

Timetable app showing action items of multiple fragments

Timetable app showing action items of multiple fragments

In this screen the edit and trash icon are from the selected element and are only added when a lecture has been selected. The plus and the search icon though are from the ListFragment.

Adding action items programatically

As the post title implies the additional action items are inserted by the fragment. In fact not much different from how you add the items within your Activities. You use the onCreateOptionsMenu() method for it:

@Override
public void onCreateOptionsMenu(
      Menu menu, MenuInflater inflater) {
   inflater.inflate(R.menu.activity_itemdetail, menu);
}

As you can see the signature of this method is different from the one of the Activity class. With fragments your method takes a MenuInflater as second parameter. And the fragment’s onCreateOptionsMenu() method also doesn’t return a boolean value.

Even with this method in place Android still doesn’t display any additional menu items unless you explicitly tell it to call this method. To do so, you first have to call setHasOptionsMenu() within the fragment’s onCreate() method:

setHasOptionsMenu(true);

Now your fragments menu items will be displayed in the ActionBar.

Of course, if you do so, you have to stop using the same entries in your Activity. Otherwise it would look fine on any screen that makes use of your multi-pane layouts but you would end up with doubled icons on single-pane screens.

For the sample screen I have used a menu xml file containing only the about entry in the activity. And I use an xml file containing only the edit entry in the fragment. The about entry is not needed in the fragment, because it is added by the activity, no matter whether you are in single-pane or double-pane mode.

Changing the order of action items

By default Android displays the action items of your fragment behind those of the activity. If you use multiple fragments Android displays them in the order the fragments are created.

This default order is not necessarily what you want. For example it is very likely that you want to display an about item as the very last entry within your overflow-menu – no matter if fragments later on add more overflow-menu entries. Also some actions are more often used than others. You want to put them more to the left, so that, depending on the screen size, those are always visible. And which items are more important depends on which fragments are in use.

Tio help you achieve this you can make use of the android:orderInCategory attribute of the item elements.

The fragment_itemdetail.xml menu file:

<menu xmlns:android="http://schemas.android.com/apk/res/android" >
   <item
      android:id="@+id/edit_item"
      android:icon="@drawable/ic_action_edit"
      android:showAsAction="ifRoom"
      android:orderInCategory="10"
      android:title="@string/edit_item">
   </item>
</menu>

The activity_itemdetail.xml menu file:

<menu xmlns:android="http://schemas.android.com/apk/res/android" >
   <item
      android:id="@+id/about"
      android:icon="@drawable/ic_action_info"
      android:showAsAction="never"
      android:orderInCategory="9999"
      android:title="@string/about">
   </item>
</menu>

And finally the activity_itemlist.xml menu xml file:

<menu xmlns:android=
      "http://schemas.android.com/apk/res/android" >
   <item
      android:id="@+id/add_item"
      android:icon="@drawable/ic_action_add"
      android:orderInCategory="20"
      android:showAsAction="ifRoom"
      android:title="@string/add_item">
   </item>
   <item
      android:id="@+id/about"
      android:icon="@drawable/ic_action_info"
      android:showAsAction="never"
      android:orderInCategory="9999"
      android:title="@string/about">
   </item>
</menu>

The higher the number the later your action item is displayed. So use an absurdly high value for the about item. And order all other elements by importance.

Since all elements have an order value of zero by default, you have to use numbers for all action items as soon as you want to move one to the front.

android:orderInCategory="9999"

Handling events

As usual you can handle menu item clicks in the onOptionsItemSelected() method:

@Override
public boolean onOptionsItemSelected(MenuItem item) {
   // handle item selection
   switch (item.getItemId()) {
      case R.id.edit_item:
         // do s.th.
         return true;
      default:
         return super.onOptionsItemSelected(item);
   }
}

But bear in mind that the Activity’s onOptionsItemSelected() method is called first. Your fragment’s method is called only, when the Activity didn’t consume the event!

Adding and removing action items

So far I have only mentioned how to use action items, that are defined within an xml file. But that often is not sufficient.

See this screenshot of a German TV program app. Please ignore, that it’s in German, I explain what is relevant to you.

The Prime Guide app showing icons only when relevant

The Prime Guide app showing icons only when relevant

In this app you can select a program to get more information about it. If you select a program that hasn’t started yet, you can bookmark it and set an alarm for it. If on the other hand the program either has already started or ended those actions are not displayed. It simply makes no sense to add an alarm for some program that has already started. But all other icons are visible in both cases.

So how to do that? You could either use different menu xml files for a static set of action items. Or you could add or remove menu actions programatically. Since the second approach is more flexible I deal with it in the next paragraphs.

First you need to get hold of a Menu object. You can do so in the onCreateOptionsMenu() method shown above.

Now you can use this Menu object to either change the visibility of action items or to remove them from the menu entirely.

You can change the visibility like this:

if (mMenu != null) {
   mMenu.findItem(R.id.edit_item).setVisible(false);
}

The null check in the code is necessary, because of the order in which the methods of your fragment are called. Of course Android executes the lifecycle methods before finally calling onCreateOptionsMenu(). Thus your Menu object is null in the onCreateView() method, which you probably use to create event handlers.

If you want to entirely remove the action item, you can do so easily as well:

if (mMenu != null) {
   mMenu.removeItem(R.id.edit_item);
}

As you will see in the next few paragraphs, adding a menu item is slightly more cumbersome than just making it visible again. Thus if you want to toggle action items depending on the state of your fragment, I suggest to change the visibility instead of removing and re-adding the items.

So far I have only shown you how to get rid of action items. Of course you also can add new items. You can use one of the several add() methods of the Menu class. For example the following code fragment would add the item in the same way, as with the xml code for inflating it:

MenuItem item = 
      mMenu.add(Menu.NONE, R.id.edit_item, 10, R.string.edit_item);
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
item.setIcon(R.drawable.ic_action_edit);

The first parameter sets the group. If you have no groups, use Menu.NONE. The second parameter sets the id you want to use. It’s the same one you later use in the onOptionsItemSelected() method to react to menu item clicks. The third parameter is the order value and the last one the title text you want to use. Remember, that Android uses the title text to display usage hints or to read out this value with a screenreader app. So never use null for this value.

As the code sample shows, you have to add the icon and the showAsAction attribute value with separate method calls later on.

I don’t know why there is no way to add a complete MenuItem object directly. To me that would be better readable. But that’s just a tiny detail :-)

Wrapping up

In this post I have shown that you can use the onCreateOptionsMenu() method to add action items related to your fragment to the existing icons of the ActionBar.

Since the default order in which Android adds these items is often not the preferred one, you can change this easily by adding the android:orderInCategory attribute to your menu items in the xml files.

Finally you have seen how to change the visibility of menu items, how to remove them completely or how to add new menu items.

Please let me know in the comments, if any questions are left open and which way you prefer to remove action items from the ActionBar. And don’t forget to plus one and share this post if you found it useful!

ActionBarSherlock: Up Navigation

In this tutorial I am going to cover some initial thoughts on navigation. The ActionBar allows multiple navigation modes. In this post I state which navigation modes exist, how to get to the ActionBar object in the first place and how to deal with the so-called up navigation.

Less code than in the previous parts because code-wise there is not much to it. But the concept is the cause for user confusion mostly caused by inconsistent implementations. I hope this post helps to get you on track to implement the up navigation in a correct way so that users understand your app easily.

Back and up navigation is a topic that’s always causing lot’s of discussions. When in doubt, please go to the developers community on Google+ or the user experience equivalent of StackOverflow and discuss the way you want to use it and if this is correct or not. If you have technical issues, though, please use StackOverflow.

The structure of the ActionBar

Let me go back for a second and explain the way the ActionBar is partitioned. I follow the terminology of the ActionBar pattern page in Android’s design guidelines here.

ActionBar of the detail screen on a 2.2 device

ActionBar of the detail screen on a 2.2 device

On the left side of the ActionBar is the app icon and possibly some indicator for the up navigation or the slide-out menu. Next comes the View control. This is often a text showing where you are. It could also contain tabs or a spinner as alternative means for navigating your app. Those elements are aligned to the left.

On the right side we have the action buttons followed by the action overflow – if an overflow menu is present.

What is important is to distinguish both areas. The left area is the part where you put navigational elements. Like moving up, tabs or a spinner. The right part is where you put actions that belong to the view. Like actions to add or delete elements, to update a list or for adding songs to your favorite lists.

I do not discuss those action items in this post. I have already described how to add action items and how to use the overflow menu. I probably cover the split ActionBar in another post and I will also deal with Action Providers and Action Views in a later post.

Navigation types of the ActionBar

Basically there are four types of navigation supported by the ActionBar pattern:

  • Go to the start screen of the app
  • Go up in the navigational hierarchy
  • Switch to another tab
  • Select another Spinner item

Accessing the ActionBar object

Before you can use any of the navigational modes, you need access to the ActionBar object, though. Since there is no public Javadoc page for the ActionBarSherlock library, I recommend to use the documentation of Android’s own ActionBar class, if you need to know about methods or fields of the ActionBar class. ActionBarSherlock’s class offers the exact same methods. Of course the signature is different since it uses ActionBarSherlock counterparts for any parameters or return values that use object of the ActionBar API – but should you migrate to ABS from a project that already makes use of the stock ActionBar, just changing the imports should suffice.

Using ActionBarSherlock you get the ActionBar object by calling getSupportActionBar() from within your SherlockActivity:

ActionBar actionBar = getSupportActionBar();

If your API target is at least API level 14 you could be tempted to use getActionBar() instead. After all this method appears first among the code-completion suggestions. But that won’t work on older devices – which is the reason to use ActionBarSherlock in the first place. So do the right thing and always call getSupportActionBar()!

Gladly Lint reminds you to do so:

Class requires API level 11 (current min is 7): android.app.ActionBar

But Lint cannot do so, when you added the Annotation SuppressLint("NewApi") to your code – so better be careful right from the beginning.

The method getSupportActionBar() is not available for fragments. But you can get access to the activity by calling getSherlockActivity(). And from here you can go on as shown above.

Be very careful here as well. If you call getActivity() instead, you will get an object of type android.support.v4.app.FragmentActivity. Since this class doesn’t have the getSupportActionBar() method you are even more prone to call the wrong method.

How does ActionBarSherlock’s ActionBar relate to Android’s ActionBar

ActionBarSherlock creates a wrapper to a native ActionBar object on all devices with an API level of at least 14 (ICS). And it creates a compatibility object for older devices. Both inherit from com.actionbarsherlock.app.ActionBar.

This is important to know. Because if you develop using an emulator or a device with a newer API level, you get away with being sloppy. Well, until you start to actually use an older device. When you start your app on an older device, your app will force close because of a NoSuchMethodError. Not too surprising, since the method getActionBar() was not available for Activities back then.

Navigating to the start screen

The ActionBar pattern has emerged before an official implementation existed. That’s the common way patterns emerge. The very first post on Juhani Lehtimäki’s excellent blog androiduipatterns.com shows some examples of apps that implemented the ActionBar in 2010. With the introduction of Honeycomb the Android team added the ActionBar to the API in 2011.

Back then it was all the rage to use the app icon as something akin to the home button or the logo on websites. It would get you immediately to the initial screen of the app. Of course back then it looked as if the dashboard would gain traction and so a click on the app icon would immediately bring you back to this hub. This all has changed, but you still could use the app icon that way.

But I advise not to use the home navigation model. Instead make use of the up navigation. Using the up model is the recommended pattern of Android’s design guidelines. I explain the up navigation in the next section. The code for home navigation would be nearly identical (it’s just another target Activity) – but you prefer to follow the guidelines anyway, don’t you?

Up navigation

With Ice Cream Sandwich the Android team has introduced the so-called up affordance to the platform. Perceived affordance is a visual clue helping users understand what a certain element does. In this case the up affordance helps the user to understand that he is going up (or back) a hierarchical level.

To be honest: I am still not convinced that this button is a useful addition to the Android platform, but that’s just me. In fact it’s a common pattern found in many current apps, it’s pushed by Google and it’s recommended on the design site. It looks like this pattern has established itself firmly. Thus, I recommend to use it, despite my misgivings!

So what is up navigation?

Up means getting up a hierarchy level. If you go up a level you usually also go back a level, thus the affordance, this tiny little left pointing caret, is not as weirdly chosen as it might sound.

Let me give you an example: Say you have a list of items and select one item. The detail screen would be the next hierarchical level. Up in this case would go back to the list of items. That’s what the app used in this series does so far. But apps often have multiple levels in their hierarchy. And using the up button can help users navigate the app faster – an important consideration in the mobile environment.

Before discussing this any further, let me first show you how to do this.

Adding the up affordance

Navigating within hierarchies means that the up affordance should be visible as soon as you navigate down one level. Which basically means that all screens after the initial screen of the app should sport the up affordance.

If you followed this series so far, you added the following lines to the ItemDetailActivity in the first part about using fragments and the support library:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
   getActionBar().setDisplayHomeAsUpEnabled(true);
}

You had to add the if statement as a temporary workaround to make your project compile before you have added ActionBarSherlock to it. Remember that getActionBar() only works on newer devices.

Now that you have added ActionBarSherlock in the last post of this series, you can use getSupportActionBar() instead. And since this works on older devices as well, you can drop the if statement instead:

// Show the Up button in the action bar:
getSupportActionBar().setDisplayHomeAsUpEnabled(true);

Reacting to an up click

When a user clicks on the home button of your app (not the home button of your device), you should navigate to the Activity that is logically one hierarchy above the current one. You do so within the onOptionsItemSelected() method of your ItemDetailActivity. In fact the Android developer tools did that already when you created the project:

@Override
public boolean onOptionsItemSelected(MenuItem item) {
   switch (item.getItemId()) {
      case android.R.id.home:
         // This ID represents the Home or Up button. In the case of this
         // activity, the Up button is shown. Use NavUtils to allow users
         // to navigate up one level in the application structure. For
         // more details, see the Navigation pattern on Android Design:
         //
         // http://developer.android.com/design/patterns/navigation.html#up-vs-back
         //
         NavUtils.navigateUpTo(this,
               new Intent(this, ItemListActivity.class));
         return true;
   }
   return super.onOptionsItemSelected(item);
}

NavUtils’ navigateUpTo() method takes care of setting the flag FLAG_ACTIVITY_CLEAR_TOP and finishing the current activity. This flag ensures that a previous instance of this Activity within your task stack is reused, if available, and removes any activities on top of that activity.

Disabling the home button on the initial screen

Now when you already are at the root level of your app, there is no more up navigation possible. Thus you should disable the home button and the up affordance.

Doing so is simple. Just call setHomeButtonEnabled() and pass in a false value. For the project you have created in this series the home Activity is your ItemListActivity. Thus add this line to the onCreate() method somewhere after your call to the super method.:

// disable the home button and the up affordance:
getSupportActionBar().setHomeButtonEnabled(false);

So how is up different to the back button?

From what I have said so far, you might think that up is nothing else than the usual back button. But that’s not the case!

Both buttons can go back to the same screen, that’s true, but that’s not always the case.

Granted, for the app, you have developed so far in this mini-series, there is no difference between up and back. But most often you can get to a certain screen in your app in multiple ways. And that’s when back and up start to behave differently.

Up takes you always to a defined point of the app: The previous point in the hierarchy – no matter how you got to this point. Back though follows the trail you have chosen in reverse.

This is even more important if your app offers exported Activities for use by other applications. In this case the user might get to a screen of your app from an external app.

Up would still bring him up a hierarchical level within your app. Back on the other hand should bring the user back to where he came from – in this case the other app.

Even within your own app there can be differences. Say you are creating a conference app. From the schedule view the user selects one talk. The detail screen of the talk also links to other talks which are related as well as to talks that are by the same speaker. The user now might follow those links and thus view multiple talks. Now to return to the schedule fast, the user simply clicks the up icon and goes back to the schedule. With the back button though she would have to use multiple clicks – depending on the number of talks she has viewed.

Be wary of bad examples!

Don’t have a look at how other apps do it. Have a look at the design guide instead. There are way too many apps that do not behave correctly. There might be good reasons for doing so, but usually it adds to user confusion. It hurts the Android platform in general. Something you do not want to do, do you?

For example Google Play behaves incorrectly. You can go to the games category directly from the start screen. Or you can get there by going to apps and then selecting the games category. But up behaves differently, depending on where you came from. I can understand the reasoning behind it. They want to avoid confusion. But one level deeper the up button suddenly starts to work as up and not as back aynmore. What should users make of this?

As good-intentioned as this might be, I think it actually adds to the confusion. How should users grok the up button when it sometimes behaves incorrectly and works differently depending on where you are in the app? So, please, if you want to deviate from the guidelines, think twice. Think thrice. Discuss it with peers. Discuss it with usability experts. Do some users tests! Do your users and us all this favor!

What about the slide-out menu?

When discussing how to add the up affordance I said that you shouldn’t show an up affordance and that the home button shouldn’t be enabled when the user is on the initial screen of your app.

Obviously that’s not the case for the YouTube app, not for the Google+ app and an increasing number of other apps. YouTube as well as Google+ show the up affordance on the home screen and when you press the app icon again, the so-called slide-out menu appears. This is a relatively new pattern that has replaced the dashboard.

The slide-out menu has its name because it appears in a slide-out motion from the left side of the screen. It can be accessed using the up button – and sometimes by using a bezel-swipe from the left.

Keep in mind that this pattern is highly controversial. Jake Wharton, the creator of ActionBarSherlock, for example is a strong opponent of it. I personally like it, though I agree that it has some shortcomings. But with this in mind, carefully think whether your app needs one or not. Do not just add one, because it seems to be en-vogue right now. :-)

I won’t deal with this pattern here. There is a short paragraph on the ActionBar pattern page in the design guide. And you can find a detailed presentation of the slide-out navigation pattern on Juhani’s androiduipatterns.com site.

What you need to know is, that the trigger for this menu with regards to the ActionBar is also the up button. You react to it as described above. Of course you do not simply start a new Activity but have to show the menu instead. One library to help you do so is Jeremy Feinstein’s SlidingMenu library. Of course, there are others as well, but I happen to like this one.

Lessons learned

In this post you learned about the up navigation and how to implement it. You added the up affordance to one activity and disabled the home button on the other one.

You also learned what the difference between up and back is and how well-intentioned deviations from the standard can cause user confusion.

I didn’t cover how to properly deal with Widget or Notification clicks to your app. I am totally opposed to the recommended behavior and probably would rant too much. Well actually this might be a topic for another post so as to vent off a bit and explain why I don’t like it :-)   But not in this series.

I also left out how to create a new task and a simulated back stack when using the up button after a user got to your app from another app. I might do so in some other post if the need arises. But currently I think it’s explained sufficiently in the Android training class about ancestral navigation. The current app is too simple to explain this properly.

Has this post helped you? Do you have any more questions about up vs. back? Let me know about questions left open, any remarks you want to make or any mistakes you have spotted in the comment section.

Adding ActionBarSherlock to Your Project

In my last post I had a look at how to set up a Fragment project that uses a master detail view. This week I am going to show you the necessary steps to make this project use the ActionBarSherlock library.

ActionBarSherlock is a library by Jake Wharton, that enables you to use action bars even on older devices without having to code an action bar from scratch. This library deals with all the details you do not want to care about. Even Googlers like Roman Nurik or Nick Butcher recommend to use this library and the Google IO app also makes use of this lib. So should you!

With this post I start my series about the action bar by simply adding an action bar to the project of my last post, adding some action items to it and explaining some basics of action bars. The focus of this post is mainly on getting ActionBarSherlock to run on your project. As such it is an introductory post about the action bar.

Since the action bar offers many functions and is the most important navigational component Android has, more posts about it will follow in the next weeks.

Getting ActionBarSherlock and adding it to Eclipse

The first thing you have to do, is to download the library from actionbarsherlock.com. After this unzip/untar it. The download contains three folders: library, samples and website. The samples folder contains four sample projects to showcase what you can do and to show you how to do it. You should have a look at them. The website folder contains the code of the project website. The library folder finally contains ActionBarSherlock’s source code.

Now go to Eclipse and add the ABS-library as an Android project. Do not use Eclipse’s import tool to import the ActionBarSherlock library – it would not work immediately and you would have to fix some settings. Instead use the project creation wizard of the Android Developer Tools.

Open File -> New -> Project -> Android Project From Existing Code.

Creating an Adnroid project from existing source

Creating an Android project from existing source

In the next screen select the folder, which Eclipse then uses to search for projects. If you select the ActionBarSherlock root folder, Eclipse suggests a list of projects to create. Leave the “library” project checked and uncheck all others:

Select the library project

Select the library project

Click “Finish” to create the project.

Eclipse will now create a new project named “library”. I prefer a more useful name, so select the project called “library” and hit F2 to rename the project. I have renamed the project in Eclipse to “ActionBarSherlock”, so all following screens will refer to this name.

Adding the library to your project

Now that ABS is a library project you have to tell your own project to use it. I will use the project of my last post for this. Go to FragmentStarter’s project settings and switch to the Android tab.

If necessary scroll down until you see the Library panel. Click “Add”:

Adding libraries to an Android project

Adding libraries to an Android project

In the next window all available library projects will be listed. Select ActionBarSherlock and click “Ok”.

Select ActionBarSherlock from the list of libraries

Select ActionBarSherlock from the list of libraries

When the window disappears the library should be listed in the library panel of the Android properties tab:

Check if the library has been added succesfully

Check if the library has been added succesfully

What is slightly annoying is that the Android Developer Tools do not use the name of a project to reference it, but instead point to the directory itself. And what’s even more annoying is that a symlink gets translated to its real path, which is bound to change more often than a symlink.

Should you ever want to change the directory, you have to delete the reference to the library with the old path and link to the newly imported library. But unless you do a fresh install for blog posts, this probably won’t happen too often :-)

That’s it. Your project bundles the lib from now on.

But wait a moment! If you have a look at your project, you will notice that it now sports a red warning icon. Go to the error tab and you will see lots of warnings. Eclipse states for Fragment, FragmentActivity and all the other classes of the support library, that they “cannot be resolved to a type”. Whoops!

Unknow type problems after adding ActionBarSherlock

Unknow type problems after adding ActionBarSherlock

The reason is, that ActionBarSherlock comes bundled with the library as well. And most of the time the support library added when following my post about fragments is different from the one bundled with ActionBarSherlock. In the console tab of Eclipse you see the message “Jar mismatch! Fix your dependencies” with the hashcodes of the mismatching files and where these files are stored:

[FragmentStarterProject] Found 2 versions of android-support-v4.jar in the dependency list,
[FragmentStarterProject] but not all the versions are identical (check is based on SHA-1 only at this time).
[FragmentStarterProject] All versions of the libraries must be the same at this time.
[FragmentStarterProject] Versions found are:
[FragmentStarterProject] Path: /opt/workspace/FragmentStarterProject/libs/android-support-v4.jar
[FragmentStarterProject] 	Length: 385685
[FragmentStarterProject] 	SHA-1: 48c94ae70fa65718b382098237806a5909bb096e
[FragmentStarterProject] Path: /opt/libs/ActionBarSherlock/library/libs/android-support-v4.jar
[FragmentStarterProject] 	Length: 349252
[FragmentStarterProject] 	SHA-1: 612846c9857077a039b533718f72db3bc041d389
[FragmentStarterProject] Jar mismatch! Fix your dependencies

To fix this, simply delete the support library from you project. Go to the libs folder, select the android-support-v4.jar file and delete it. You can still use the support library because it’s also part of the ABS project.

If you need a newer version of the support library than the one bundled with ActionBarSherlock, remove the support library from both projects and add it anew to the ActionBarSherlock project.

With this done your project is good again. The next step, of course, is to change some code.

Changing the types to their ActionBarSherlock counterparts

Just adding the library won’t magically add an action bar to your project. Instead you have to change some of your code.

The first thing you have to do, is to change the classes from which you inherit. Instead of Activity use SherlockActivity, instead of FragmentActivity use SherlockFragmentActivity, instead of Fragment use SherlockFragment and so on. Now do this for the two activities and the two fragments you created while reading the last post.

You will notice that the ItemDetailActivity won’t compile any more. Whenever you add ActionBarSherlock to an existing project, this is bound to happen. Why is that?

Have a look at the error message Eclipse is displaying: “Cannot override the final method from SherlockFragmentActivity“. The method is the following:

@Override
public boolean onOptionsItemSelected(MenuItem item) {
   // ...
}

ActionBarSherlock overrides every method of it’s superclasses that takes either a Menu, MenuItem or MenuInflater object of the android.view package as parameter and declares those methods as final. You need to know that all the items in an action bar are actually menu items. Thus only by doing it this way ActionBarSherlock can take control of all the menu-handling and change it for older Android versions.

While the error message might sound bad, it actually isn’t. ActionBarSherlock provides
for every final method another method with the same name and the same number of arguments. Even the class names of the arguments are the same. Only the packages differ.

This way ActionBarSherlock makes migration of existing projects very easy. You can keep your methods and do not have to change and adjust any logic. Simply delete all import statements to the menu-related classes of the package android.view. After this hit Ctrl-O to optimize and correct the import statements and when Eclipse displays the list of imports to choose from, choose those of the ActionBarSherlock library:

Choose ActionBarSherlock's classes for imports

Choose ActionBarSherlock’s classes for imports

If you do this for ItemDetailActivity the warnings disappear. Whenever you use Eclipse (or any other IDE for that matter) to generate the import statements for you, take care to select the types of the ActionBarSherlock library. Those all start with com.actionbarsherlock.

Now that your code looks fine, you should try to run the project. Whether this run is successful or not depends on the Android version of your device. If you run this code on a device with at least Android 4.0 everything runs fine. But not so on older devices. Here you will get a Force Close dialog due to an IllegalStateException:

java.lang.IllegalStateException: 
      You must use Theme.Sherlock, 
      Theme.Sherlock.Light, 
      Theme.Sherlock.Light.DarkActionBar, 
      or a derivative.

Gladly the exception is pretty specific: You have to use one of the Sherlock themes to get your project to run. ActionBarSherlock needs many definitions to get the action bar styled correctly. Thus it needs these styles and you have to use the ABS themes. You can use your own theme, of course, but you must use one of Sherlock’s themes as a base for yours. See the Sherlock theming guide for more information on this.

Since adding the action bar to older devices is the reason to use ActionBarSherlock in the first place, you have to change the theme. Change your AndroidManifest.xml file to use a Sherlock theme:

<application
   android:allowBackup="true"
   android:icon="@drawable/ic_launcher"
   android:label="@string/app_name"
   android:theme="@style/Theme.Sherlock.Light" >
   <!-- ... -->
</application>

Now finally the app runs fine. And yes, there is an ActionBar. As you can see it works fine on current as well as on older devices:

Initial action bar on a 2.1 device

Initial action bar on a 2.1 device


Initial action bar on a 4.2 device

Initial action bar on a 4.2 device

Adding items to the ActionBar

The action bar is basically what the old menu was before Honeycomb. This means that you have to create a menu to see an action bar.

Create a file with the name activity_itemlist.xml in the folder /res/menu and add the following code to it. This file describes the structure of your action bar:

<menu xmlns:android=
      "http://schemas.android.com/apk/res/android" >

    <item
        android:id="@+id/add_item"
        android:icon="@drawable/ic_action_add"
        android:showAsAction="ifRoom"
        android:title="@string/add_item"
        android:titleCondensed="@string/add_item">
    </item>
    <item
        android:id="@+id/about"
        android:icon="@drawable/ic_action_info"
        android:showAsAction="never"
        android:title="@string/about"
        android:titleCondensed="@string/about">
    </item>

</menu>

Note: I use icons from the icon pack “Android Developer Icons 2″ of opoloo. The values I use for the android:icon attributes are specific to this icon pack. As soon as you want to try this code, you have to prepare icons that match this code or change the attribute value to fit your icons. See the appendix for more on this.

Only the xml file won’t suffice. You also need to override the onCreateOptionsMenu() method to inflate this file:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
   MenuInflater inflater = getSupportMenuInflater();
   inflater.inflate(R.menu.activity_itemlist, menu);
   return true;
}

This is the result when you run the project again. Note the differences between the 2.1 device and the 4.2 device:

Action bar with icons running on a 2.1 device

Action bar with icons running on a 2.1 device after pressing the menu button


Action bar with icons running on a 4.2 device

Action bar with icons running on a 4.2 device

The overflow menu

When you compare the screenshots of the previous section, you quickly understand what the so-called overflow menu is. This is the menu hidden behind the three dots are the far end of the action bar on modern devices.

Android shows as many items as it can directly on the action bar. But especially on phones the place for action items is quite limited. Thus Android shows all those items, that do not fit on the action bar directly, in the overflow menu. You can also tell Android to put items in the overflow menu regardless of the space. You can do so by using the value never for the attribute android:showAsAction of these items. More on this attribute later on.

The items of the overflow menu are only visible if you click the three dots on the right of the action bar or press the menu button on those devices that have such a button.

The three dots appear only on devices without a menu button. That’s the way to indicate to the user that more items are available and it’s also the only way how users can access these items. On devices with a menu button on the other hand you do not have any indicator, but users can always check if an overflow menu is present by clicking the menu key. I actually think the solution for devices without menu button is way better than the solution on devices with a menu button. On these latter devices users always have to guess if an overflow menu is present or not. Alas Samsung, the vendor that sells by far the most Android devices, still ships devices with menu buttons :-(

Sort your items by importance so that the most important actions are visible all the time. Those actions are the ones your users are most likely to perform on this screen. Think thoroughly about the importance of each item.

Other items should always be in the overflow menu, no matter how much space is left. For example if your app has an about screen, or some license info for the open source libs you use (well, ActionBarSherlock for example) or some information about what has changed with the recent version, I would put all those items into the overflow menu – no matter how much screen estate you have left.

The Android design page has some more guidance on this on their action bar pattern page and also shows you roughly how many items fit on the screen for some selected devices.

Use the android:icon as well as the android:title attribute

As you can see the action bar shows only an icon for the “Add Items” action. But that doesn’t mean that you should neglect the strings. First of all these strings are used for screenreaders so that visually impaired people still can use your app. And secondly, if users do not understand an icon immediately, they can longpress those icons until a hint appears that displays the text. Of course this hint is no excuse for confusing icons!

Another thing to note. Overflow menu items show only text. But not so on older devices. There you see the icon and the text. So always provide values for android:icon and for android:title!

Use android:showAsAction to define the appearance of the action items

The item element of the xml file for the action bar can contain four attributes that are only relevant for the action bar: android:showAsAction, android:actionLayout, android:actionViewClass and android:actionProviderClass. Of these I describe only the frst here, the other three are a topic for an extra post.

You can use the attribute android:showAsAction to decide which icons to display and in which way. It can take the following five values:

Possible values for the android:showAsAction attribute
Value Meaning
ifRoom Display item if enough room is left for this item
always Display this item, regardless of the existing place
never This item is not displayed in the action bar directly but in the overflow menu
withText Display the icon together with the title text
collapseActionView Only relevant for collabsible action views – not covered in this post

As a rule of thumb you should always use ifRoom, if you want the icon to be part of your action bar and you should use never if you want the item to always be part of the overflow menu.

The Android documentation for menu resources explicitly urges you to use always with great care. You should use this attribute only if the element is absolutely necessary. If you use this on multiple items and the place in the action bar is too small to do this properly Android displays overlapping icons. In this case the items are even less accessible than those of the overflow menu.

While you have to pick one of the first three, you can combine these with the value withText. This value decides whether Android shows only the icon or the icon plus the text of the android:title attribute. But Android does show the text only, if enough room is left.

As an example change the menu file to the following code:

<menu xmlns:android="http://schemas.android.com/apk/res/android" >
   <item
      android:id="@+id/add_item"
      android:icon="@drawable/ic_action_add"
      android:showAsAction="ifRoom|withText"
      android:title="@string/add_item">
   </item>
   <item
      android:id="@+id/reload_list"
      android:icon="@drawable/ic_action_reload"
      android:showAsAction="ifRoom|withText"
      android:title="@string/reload_list">
   </item>
   <item
      android:id="@+id/about"
      android:icon="@drawable/ic_action_info"
      android:showAsAction="never"
      android:title="@string/about">
   </item>
</menu>

Now these are the resulting screens depending on the Android version:

Action bar with multiple icons running on a 2.1 device

Action bar with multiple icons running on a 2.1 device


Action bar with multiple icons running on a 4.2 device

Action bar with multiple icons running on a 4.2 device after selecting the overflow menu

As you can see on phones Android still only displays the icons. Note what happens on small devices that have no menu button. Android had to put the second item into the overflow menu because it had not enough place for it. On devices with a menu button the second item would be directly visible in the action bar.

This is what the same app looks like on a tablet:

Action bar with multiple icons and text running on a 4.2 tablet

Action bar with multiple icons and text running on a 4.2 tablet

Now the withText value makes a difference. While I like items to display text on tablets, it is very unusual. Have a look at other apps to see how they do it, before you decide whether to display text or not. But do not use text to gloss over weaknesses in your icon design.

Dealing with user input

So far you have created the action bar and made it look right. But when you click on an item, nothing happens.

What you have to do is to implement the onOptionsItemSelected() method. You do this as you always did for menu item selections:

@Override
public boolean onOptionsItemSelected(MenuItem item) {
   // Handle item selection
   switch (item.getItemId()) {
      case R.id.add_item:
         // do s.th.
         return true;
      case R.id.about:
         // do s.th.
         return true;
      default:
         return super.onOptionsItemSelected(item);
   }
}

Action bar interaction is like any other user interaction and all the usual rules about the UI thread apply. So don’t put any stuff in here that’s long-lasting. Use Loaders, AsyncTasks, IntentServices or whatever you prefer for the specific job to do the heavy lifting.

What’s next?

In this first part of the ActionBar tutorial you have changed your project so that your activities and fragments now inherit from ActionBarSherlock’s classes and then made it compile and run.

After this you added items to the action bar, learned about the overflow menu and about the ActionBar-related attributes of the menu xml files. But with this you barely scratched the surface of what the action bar covers.

According to the design guidelines the “action bar is one of the most important design elements you can implement”. No wonder it features all sorts of possibilities. And, yes, it brings with it some complexity as well. So there is more to the action bar than just this post.

In the next post I am goig to cover the different navigation types the action bar supports. After this I will write yet another post about the so-called contextual action bar.


Appendix

As mentioned all icons that I use are icons of opoloo’s Android Developers Icons 2. You have to either get this pack, create icons for yourself or download the “Action Bar Icon Pack” from the design site. The latter icon pack contains no “Add” icon, so you have to adapt the XML.

You can also use totally inappropriate icons and hassle with the design later on. For the sake of following this tutorial you could use something like @android:drawable/ic_menu_add. But you should never use these for anything else than for just getting on with this tutorial.

You have to add the icons you want to use to the respective folders within your /res folder. For details on how to name these folders see the Android documentation about providing resources.

Getting Started With Fragments and the Support Library

Fragments are modular building blocks for creating a responsive Android UI. They help you to target different devices and screen sizes while at the same time avoiding code duplication.

This post starts with the Android Developers Tools generating a project for you. I then explain some of the generated code and will show you how to make this code work on older devices. To do this kind of post was suggested by a user of a German forum. Let’s see if this works well.

This project will grow over time. In the next post I will add the ActionBarSherlock library to the project – but this time it’s all about understanding the basics.

This post is not taking the approach, Martin van Z or Sebastian Kaspari are taking with their respective series “Creating your own Android app” and “Instant Moustache“. They cover planning stages, sketching and the like as well. Both series make for very interesting reading!

The basics of fragments

Fragments constitute a part of your Activity you present to the user. Most often they are used to display some section of the screen and to react to events that happen within this section. Fragments are modular, come with their own lifecycle and help you to avoid code duplication.

Fragments come in two varieties. Either the normal Fragment class within the android.app package that was introduced with API level 11 (Honeycomb). And the support library Fragment of the package android.support.v4.app. Both have subclasses like DialogFragment or ListFragment. You cannot use a mix of normal and support libary fragments.

If you want to target older devices in your project, always go for the classes of the support library. If you only care about newer Android releases, you can use the normal fragments.

In this post I specifically have a look at how to make fragments work with the support library. Given the vast amount of Android 2.2 and 2.3 devices currently out there I still recommend to always try to target these devices where possible. And since the differences in the Android framework between Android 2.1, 2.2 and 2.3 are not too big, I suggest to try with 2.1 and only increase the minimum API level if you have a need for this.

Fragments have a UI on their own. As usual you inflate your view, which you have defined in XML files. While Fragments without any UI are possible, I won’t cover those in this post.

View creation is nothing special. Apart from one thing. If you use the android:onClick attribute for your view elements, you cannot implement the method in the fragment. You have to implement it in the activity. If you want to deal with click events in your fragment you have to add OnClickListeners for those view elements programmatically.

Fragments have a lifecycle of their own. It is a bit more complex than those of Activities, because Fragments can be attached to and detached from their enclosing activities. I will cover some of the lifecycle methods below.

So much for the background on fragments. If you want to learn more, head over to Google’s documentation about Fragments after reading this post.

In the next section I start by creating the project using Android’s Developer Tools.

Creating the project using the Android Developer Tools

To follow the steps outlined here you must have Eclipse together with the Android Developer Tools plugins installed. You can download complete bundles on Android’s SDK page.

In Eclipse go to File -> New Project -> Android Application Project and click “Next”. A wizard pops up:

Android Developer Tools project wizard

Android Developer Tools project wizard

Fill in the required fields as shown above and click next until you come to the screen where you can select the type of project you want to create:

Project wizard for master detail projects

Project wizard for master detail projects. Note the API level warning.

Select the master detail project type. That means that you have a list of elements on the left and a detail view on the right – if your screen is big enough.

As you can see the API level we have chosen isn’t supported. So go back to the beginning and switch to API level 11. You will correct this later on:

Switch to API level 11 in the project wizard

Switch to API level 11 in the project wizard

Now go on until you come to the screen showing the name for the entities you want to deal with:

Select an enity name in the wizard

Select an enity name in the wizard

Item is the default here. I do not bother to change this, but you can change it to whatever your app is going to deal with – like Book, File and so on. Keep in mind that the wizard creates the names of the classes accordingly. So whenever I refer to Item, ItemListFragment and so on, substitute Item with the noun you have chosen in this dialog.

Now click Finish and the Eclipse generates the project for you.

If you never have downloaded the support library and do not use the adt bundle, the following dialog will pop up. It forces you to download the library and attaches it to your project:

The wizard alerts you that the support library is missing

The wizard alerts you that the support library is missing

Fragments lifecycle

I do not cover the entire lifycycle of Fragments. Much of it is comparable to the lifecycle of Activities. But by having a look at the code, I will show some common lifecycle methods you should know about.

First, have a look at ItemDetailFragment. This fragment inflates a view. But it does not do so in the onCreate() method like you would do in an activity, but within the onCreateView() method. Android passes the inflater you need to use for view inflation into the method, as well as the container for your view.

The inflation always looks like this:

View rootView = inflater.inflate(
      R.layout.fragment_item_detail,
      container, false);

The last parameter states that your view should not be attached to the container. It must always be false for fragment view inflation. Android adds your view to the container itself!

Since fragments can be attached or detached from an activity, there also exist onAttach() and onDetach() methods. After your fragment has been attached, you can use getActivity() within your fragment to get to the enclosing activity.

The ItemListFragment makes use of both methods to set a callback interface. I explain this interface in the next section.

The other lifecycle methods are the same as for activities. You can read more about the lifecycle in Android’s documentation.

Callback interface

Fragments and activities need to communicate with each other. But you do not want to couple them closely. Your fragment might end up in multiple activities, so it should never have a direct link back to the activity it belongs to. Instead it is common practice to use interfaces to deal with this. Your fragments define callback interfaces and your Activities must implement these interfaces.

Have a look at the ItemListFragment. It contains the interface named Callbacks. The fragment uses this interface to communicate to the activity that an item has been selected:

public interface Callbacks {
   /**
    * Callback for when an item has been selected.
    */
   public void onItemSelected(String id);
}

This interface is used in the onListItemClick() method:

@Override
public void onListItemClick(ListView listView, View view, int position,
      long id) {
   super.onListItemClick(listView, view, position, id);
   
   // Notify the active callbacks interface (the activity, if the
   // fragment is attached to one) that an item has been selected.
   mCallbacks.onItemSelected(DummyContent.ITEMS.get(position).id);
}

See also the methods onAttach() and onDetach(). That’s where the internal member mCallbacks is set to the enclosing activity or to a dummy implementation, if this fragment is no longer part of an activity.

The ItemListActivity implements this interface. So look at the activity’s onItemSelected() method. It first checks if you are in dual pane or single pane mode (see next section). And depending on the result it either starts another activity or replaces the second fragment.

When you have many callbacks and multiple fragments using interfaces for every fragment can become pretty unwieldy. In this case I recommend to use a bus based approach like EventBus or Otto. But that’s a topic for another post. At the current state of the project the interface approach works fine.

Dual pane or not

The most important reason to use fragments is to make your app responsive. That means make it adapt nicely to a change in orientation and to different screen sizes and aspect ratios.

The generated project shows this by using two panes on tablets and a single pane on phones. It defines two layouts for the ItemListActivity in the layout folder: activity_item_list.xml and activity_item_twopane.xml.

The files in the layout folder

The files in the layout folder

Wait a moment. Two layouts for the same activity with different names and both in the default layout folder? What’s up with that?

Well have a look at the values folders in the res directory:

The different values folders

The different values folders

Of interest in this case are the values-large and the values-sw600dp folders. Both contain a file named refs.xml which references the activity_item_twopane.xml file:

<resources>
   <!--
   Layout alias to replace the single-pane version of the layout with a
   two-pane version on Large screens.
   
   For more on layout aliases, see:

http://developer.android.com/training/multiscreen/screensizes.html#TaskUseAliasFilters

   -->
   <item name="activity_item_list" type="layout">@layout/activity_item_twopane</item>
</resources>

Note the name attribute of the reference. Its value is activity_item_list. This means that when you use R.layout.activity_item_list in your code, Android will replace this value with R.layout.activity_item_twopane. Of course only if one of the two qualifiers large or sw600dp applies. That way you have to declare the layout only once and can refer to it in multiple places.

You have to use the large qualifier even though a more specific qualifier is present, because the swXYZdp qualifier will only be recognized from API level 13 onwards. Prior to this only the screen qualifiers folders (small, normal, large and xlarge) have been recognized.

If you look at the activity_item_twopane.xml file, you will see that a fragment and a FrameLayout is declared.

You can declare fragments either directly in your layout files or you can use FragmentTransactions to add fragments programmatically. In this case one fragment is added using the xml file while the other is added using a transaction. The FrameLayout within this file is the container that you later add the fragment to in your transaction.

In the ItemListActivity you finally find this code snippet:

if (findViewById(R.id.item_detail_container) != null) {
   // The detail container view will be present only in the
   // large-screen layouts (res/values-large and
   // res/values-sw600dp). If this view is present, then the
   // activity should be in two-pane mode.
   mTwoPane = true;
   
   //...
}

So by either finding this container or not, the activity knows whether Android selected the two pane layout or not.

Depending on this, the activity handles a list item click differently in the onItemSelected() callback method.

There is also another approach used on some of Google’s Android training pages and mentioned by Kiran Rao in the comment section. The other approach uses a boolean variable in xml files. Have a look at Kiran’s comment to learn more about this approach.

You should really dig deeper into how to deal with multiple screen sizes. It is a very important concept for supporting multiple device sizes in Android. Thus I recommend you take the training about multiple screensizes on the Android site after reading this post. Do not write apps for phones only. As has just been published, the tablet market for Android is gaining serious traction. Don’t miss out on this. And don’t risk bad ratings just because you ignored bigger screens!

FragmentTransactions

FragmentTransactions are Android’s way of adding, removing, hiding or replacing fragments to or from your activity. As the name transaction implies either all actions of the transaction are done or none. So you transfer from one valid state of your app to another one.

But you not only manage the fragments themself, you can also define custom animations to display for fragments being attached and detached.

And you can add a complete transaction to your backstack. In this case clicking back would do everything within the transaction in exactly the reverse order. If you do not use the addToBackStack() method to add your transaction to the backstack, this transaction will be ignored. You can see this in the sample app in two pane mode. Clicking on different list items starts a transaction and causes a replacement of fragments. But by clicking back you just leave the app. Which makes sense, since you do not want every list item click to be added to the back stack.

I have covered here just some basics of FragmentTransactions. To learn more about them, visit Daniel Lew’s blog post about FragmentTransactions.

Make it run on older devices

The code was generated by the wizard for API level 11. But it uses the support library already – so shouldn’t it run on older versions as well?

The heavy lifting already has been done. The wizard created both activities as subclasses of FragmentActivity. This is a class of the support library that allows you to access an FragmentManager using getSupportFragmentManager().

Two changes are needed to make it run on older versions.

First the generated class ItemListFragment contains a layout constant that has been introduced in API level 11: simple_list_item_activated_1. This specific layout highlights activated list items. Which is useful, if you have a multi-pane layout, because it shows the user which list item is selected and displayed in the details pane.

I ignore the highlighting for now and just try to get the code to run:

int layout = android.R.layout.simple_list_item_1;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
   layout = android.R.layout.simple_list_item_activated_1;
}
setListAdapter(new ArrayAdapter<DummyContent.DummyItem>(
      getActivity(),
      layout,
      android.R.id.text1, 
      DummyContent.ITEMS));

The second problem is the ActionBar, which is not available on older devices without adding an additional library. Since this is the topic of my next post, I simply ignore the ActionBar on older devices for now. Change the line with getActionBar() to this in the ItemDetailActivity:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
   getActionBar().setDisplayHomeAsUpEnabled(true);
}

Now you get a Lint warning. That the call requires API level 11 but that your manifest file states API level 7 as a minimum. Add an annotation to suppress the new API warnings for the onCreate() method:

@SuppressLint("NewApi")

Now change your AndroidManifest.xml file to support API level 7:

<uses-sdk
   android:minSdkVersion="7"
   android:targetSdkVersion="16" />

With these changes, you can now run the project on older devices.

Lessons learned

You have seen how to create a master detail project using the Android Developer Tools and how the generated project works.

I have given you details about what fragements are and what to use them for. And I have shown you some code snippets of the generated project and explained why it is done that way.

Also I have shown to you how to make this project backwards compatible. That way you can run the code on older devices as well.

Keep in mind: The Android Developer Tools project is evolving at a rapid pace. So if you read this post in a few months some of this probably has changed – though the basics should remain the same.

I strongly recommend to keep an eye on updates to the developer tools. Check for updates in Eclipse regularly.

And if you want to know of what’s to come, visit the project page of the ADT team and especially their announcements of recent changes. The team announces preview versions there on a regular basis.

Next week you will add an ActionBar using the library ActionbarSherlock to your project. Until then have a good look at the code and jump to Android’s training session about fragments to learn more. Fragments are very important and it is essential to fully understand them!

Android: How to Use Two Data Sources in ListViews?

Sometimes in our apps we have the need to link to data of other sources. You have a database of your own for your app, but some records also point to the MediaStore content provider to calendar entries or to contacts.

For all detail pages this is not much of a problem. You get the data from your database and query the external content provider for additional data based on the id or lookup uri that you stored in your database.

But what to do, if you want to show data in ListViews? Say contact pictures, event titles for multiline list elements and so on. Then it gets complicated.

From what I see, we have the following choices. Neither is fully satisfying – which is why I would like to hear how you deal with these kind of problems.

  • You get a list of all elements of the ListView by querying your database. And you query the content provider for each list entry using an AsyncTask. This is the a very memory efficient solution.

    For images (album covers, contact pictures…) this is probably the best option. Especially if combined with an in-memory cache. Even if users would love to see images immediately, they know this is not feasible and accept some lag before images are displayed.

    But not so for any text elements. The second line of a multiline list element should be visible instantaneously. So for text this option is not acceptable.

  • You could query the content provider for all entries at startup, put them in a HashMap and use this map later on. Well this might work if the content provider has only a few entries. But there might be thousands of media files on a SD card or thousands of contacts for a big company. In cases like these this won’t work. It would be a big waste of memory – especially since you probably only need very few of these rows at all.

    The Hashmap would also need too long to be created in the first place. The waiting period for the inital screen would be inacceptable. So this is basically a no-go.

  • You could keep redundant data in your own database. For every record that contains a link to the external content provider you also include the data, you need in the listview (e.g. the band name). That’s the option I have chosen. You only cache the results you actually need.

    There are two drawbacks to this solution: First you are wasting device memory. That is flash memory not RAM. No problem for most high end devices but low cost devices often have very limited memory available.

    Second you have to sync the data whenever your app restarts. If your app stays in the background for quite a while, the user might have changed the external data source. In fact this might even happen while your app is in the foreground if a sync adapter changes values based on changes of the backend. Now your list reflects the wrong values until your database has re-synced with the external provider.

    You could choose to listen to changes in the content provider in a service and update your database when needed. Activities then could query for changes.

    Whether these two issues of this option pose problems for you depend on the type of app. In most cases a correction of a changed value later on should be okay. And if you do not have hundreds or thousands of rows in your own table the inefficiencies are not that bad as well.

Now I would really like to hear what you think. Probably there are other options, I have overlooked. Or better ways to make one of these three options work. Please let me know in the comments, on G+ or on Twitter. Thanks!

If you are curious about the G+ discussion, have a look at my G+ announcement of this post.

Use Android’s ContentObserver in Your Code to Listen to Data Changes

When you are using a content provider as a client, chances are that you want to know whenever the data changes. That’s what Android’s class ContentObserver is for.

To use the ContentObserver you have to take two steps:

  • Implement a subclass of ContentObserver
  • Register your content observer to listen for changes

Implement a subclass of ContentObserver

ContentObserver is an abstract class with no abstract methods. Its two onChange() methods are implemented without any logic. And since these are called whenever a change occurs, you have to override them.

Since Google added one of the two overloaded onChange() methods as recently as API-level 16, this method’s default behavior is to call the other, older method.

Here is, what a normal implementation would look like:

@SuppressLint(&quot;NewApi&quot;)
class MyObserver extends ContentObserver {		
   public MyObserver(Handler handler) {
      super(handler);			
   }

   @Override
   public void onChange(boolean selfChange) {
      this.onChange(selfChange, null);
   }		

   @Override
   public void onChange(boolean selfChange, Uri uri) {
      // do s.th.
      // depending on the handler you might be on the UI
      // thread, so be cautious!
   }		
}

Some things are important with the above code. The first thing you must know, is that the second method is only available from API level 16 onwards. That’s why I added the SuppressLint annotation. The code works fine on older devices, but in this case Android obviously always calls the old one. So your code should not rely on a URI to work properly.

Also notice the Handler parameter in the constructor. This handler is used to deliver the onChange() method. So if you created the Handler on the UI thread, the onChange() method will be called on the UI thread as well. In this case avoid querying the ContentProvider in this method. Instead use an AsyncTask or a Loader.

If you pass a null value to the constructor, Android calls the onChange() method immediately – regardless of the current thread used. I think it’s best to always use a handler when creating the ContentObserver object.

Register your content observer to listen for changes

To register your ContentObserver subclass you simply have to call the ContentResolver's registerContentObserver() method:

getContentResolver().
      registerContentObserver(
            SOME_URI, 
            true, 
            yourObserver);

It takes three parameters. The first is the URI to listen to. I cover the URI in more detail in the next section.

The second parameter indicates whether all changes to URIs that start with the given URI should trigger a method call or just changes to exactly this one URI. This can be handy for say the ContactsContract URI with its many descendants. But it can also be detrimental in that the actual change, that caused the method call, is even more obscure to you.

The third parameter is an instance of your ContentObserver implementation.

The URIs you can observe

As described in my introduction to content providers content URIs can be directory-based or id-based.

Both of these URI-types can be used for your content observer. If you have a detail screen you would use an id-based URI for your observer, and when you use a list of data a directory-based URI is more appropriate.

This does not always work, though. ContactsContract for example always triggers a change, whenever any contact was changed, even if you are listening to a more specific URI. It depends on the correct implementation of the content provider. I have filed a bug report for the ContactsContract provider. Please vote for this issue, if you agree.

When you write a content provider for your app, take care of notifying the correct URI. Only if you do so, the feedback mechanism described here works. This is important for your observers – or if the provider is exported for your clients’ observers as well. And it is also important for Loaders. See my post about how to write content providers to learn more about this.

Note: If you use Loaders you do not need to listen to changes yourself. In this case Android registers a ContentObserver and triggers your LoaderCallbacks onLoadFinished() method for any changes.

Do not forget to unregister your content observer

When you have registered a content observer, it is your responsibility to also unregister it. Otherwise you would create a memory leak and your Activity would never be garbage collected.

To unregister you call the unregisterContentObserver() method of the ContentResolver:

getContentResolver().
      unregisterContentObserver(yourObserver);

You register your observer in the onResume() lifecycle method and you unregister it in the onPause() method.

This is not relevant to ContentObservers alone but applies to everything you register. As a general rule of thumb: Whenever you start coding registerXYZ() immediately also add the code to unregisterXYZ(). Otherwise you might later forget about it and inadvertently create memory leaks.

Sometimes you wish you would know more about the changes

The main downside with content observers is, that you do not get any additional information about what has changed. For directory-based URIs it would be nice to get a list of IDs that have changed. Some providers can have many records and re-reading all records, if only a few have changed, is a waste of resources in a mobile environment. Also some hint of the type of change would be nice. Like if a record was deleted or inserted.

The ContentProvider responsible for the change knows all this. So this would be no problem to add. Of course it has to be added in a backwards-compatible way.

API level 16 added a URI to the onChange() method, but this isn’t sufficient for updates of multiple records and also doesn’t tell you anything about the type of change.

Wrapping up

This quick tip showed you how to leverage Android’s ContentObserver. This makes it easy to react to updates of the data you are using.

As you have seen, it is pretty easy to implement the ContentObserver subclass. You have also seen that you can register it to listen to id-based as well as directory-based URIs.

Of course this works only, if the content provider for the URI you are observing, is correctly implemented and notifies clients of changes. If you are implementing a content provider yourself, have a look at my tutorial on writing a content provider for how to do so.

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.

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