Friday, November 12, 2010

C2DM on older versions of Android

Back in May, I made my Android Wish List. The #1 item on my list was push notifications. The Android team delivered in spades with Cloud to Device Messaging. C2DM is much better than push notifications as they exist in the iOS world, because the user does not have to see the message being pushed. Instead your app can have a background service process the message and decide if a notification should be shown or not. That allows apps to receive many more types of events from the cloud, since they don't have to worry about bothering the user (or the user just ignoring the event.)

Of course there was one obvious downside to C2DM: it required Android 2.2. This is obvious, but is a bummer since it meant that developers needed to wait until 2.2 adoption became very high before they could start using C2DM. Here we are six months later, and only about half of all Android phones in use are running 2.2 (for the US, this is lower in other countries.) Who wants to ship an app that can only reach half of the universe of users, or update an existing app if half of your loyal users will be left in the cold?

This is definitely one of the places where you have to envy the iPhone. When iPhoneOS 3.0 came out, the adoption curve was pretty fast, even though users had to hook up their phone to iTunes. The reason for this was simple: OS 3.0 was available on all iDevices, and it was immediately available to everyone via iTunes. On Android, updates are pushed out over-the-air, which is great. However, it is up to vendors to do this. Plus a specific flavor of Android must be made for each Android device (this is true of iOS as well, but there are a lot less devices), and sometimes an update is not made for some devices for whatever reasons. For example, there are a large number of devices out there running Android 1.6 that will never be updated to Android 2.x.

Don't get so down though. The point of this blog post is that all is not lost with regards to C2DM. It is actually entirely possible to add C2DM to your application, even if your application is compiled at API level 4 (Android 1.6.) Actually, it would probably work against API level 3 (Android 1.5), but there aren't too many of those devices out there. The key is that the way that you communicate with C2DM is through Intents, no 2.2-specific APIs needed. Let's take a look.

First things first, you still need the proper permissions and receivers setup in your manifest. In particular you need a BroadcastReceiver that receives Intents with an action of com.google.android.c2dm.intent.RECEIVE or com.google.android.c2dm.intent.REGISTRATION. The names of actions are not constrained in the manifest schema, so that you can use custom/application actions. Thus this causes no problems on devices not running Android 2.2. Check out the official docs for all of the gory manifest details.

Next, your application needs to check if the device is running Android 2.2+ or something older. If it is, then you can register for C2DM. Here's how you check:

if (Build.VERSION.SDK_INT >= 8){
    registerForC2dm();
} else {
    // continue to use your pre-C2DM solution here
}

The only thing hacky here is that normally if you compare Build.VERSION.SDK_INT to something, it would be a defined constant like Build.VERSION_CODES.FROYO. However that constant is not available in Android 1.6, so instead we use its value of 8. I sure hope that constant doesn't change in future versions of Android!

Finally, you need to handle the registration and message Intents that C2DM will send you. Again, you are just specifying a string for the actions on these Intents, so no API dependency. That's it! No reflection, no compile against API level 8, but declare you only need level 4 hack (as is commonly done to enable an app to be installed on the SD card.) I've tested this approach on a device running Android 2.2 and on a device running Android 2.1. The 2.2 device gets C2DM messages, just as you'd hope, while the 2.1 device simply ignores the C2DM code and is unaffected.