Wednesday, June 9, 2010

app-assistant handleLaunch and large numbers

I've been working in the world of webOS for the last month, and while there's a lot of new APIs to learn I've not really run into an amazing "gotcha" moment until now.

In our app, we show some banner notifications. When you click on that banner notification, it takes you to a page with some information. On that page is some information about time, which means we're passing a timestamp in launchArguments (part of showBanner()).

The intriguing thing that happens is that, unlike the rest of Palm (nay, JS in general), integers passed using launchArguments are limited to 32 bits. Guess what's typically larger than 32 bits: timestamps measured in milliseconds from the epoch. It confounds me that such a limitation exists, but any number larger than 2^31 - 1 is converted to the max integer instead.

The solution? Wrap the number as a string. Bizarre that they'll let any length string through, but not a number larger than an integer. If anyone can explain, please do.

Thursday, June 3, 2010

Android 1.5 Widgets, Google Maps API and VerifyError

Here are two things that don't get along: Android 1.5 widgets and the Google Maps API. Trying to include both in your application results in a dreaded VerifyError when you try to create a widget on the home screen.

The basic cause, according to Dianne Hackborn:

Hi, it looks like there is a bug initializing a process when it is launched to handle a broadcast sent to an explicit component. In this case its shared libraries are not linked to it.


This manifests itself in two ways, basically whenever a class imports any com.google.android.maps classes. The first is if your widget needs to initialize any classes that import maps. The second is if any of your broadcast receivers initialize any classes that import maps. In both cases, the widget will attempt to load com.google.android.maps and fail, throwing a VerifyError.

The solution to the first is to not have any references to com.google.android.maps. This is sometimes easier said than done, but if you divide out your maps classes from your widget classes this is doable. Worst-case scenario, you can use reflection to call your maps classes; they will throw exceptions when the widget tries to load them, but hopefully you don't need them anyways. (This happened when I had my Application try to clear some maps cache data onLowMemory()).

The solution to the second is to put all of your receivers in a different process.

Wednesday, June 2, 2010

Headset Blocker

If you own a Nexus One, you may be familiar with this scenario: you load up your N1 with some rocking tunes. You plug in your headphones and go out exercising with it. Suddenly, the tunes start skipping. Why?

The problem is that the headset jack is interpreting what you are doing as a control signal. The exact cause hasn't been shown to me yet; there are a number of theories from a lack of software noise filtering to the difference between TRS and TRRS connectors. Regardless, it's a serious issue - some people can't even listen to music in their cars with the N1 because it skips so much.

Luckily, in the last few days I was shown an answer. The control commands from headsets are actually sent out as a chain broadcast, MEDIA_BUTTON. As with any chain broadcast, your receiver can abort the broadcast. So the simple solution is to make a BroadcastReceiver with an ultra-high priority that intercepts and aborts the MEDIA_BUTTON broadcast.

To that end, I've put a small app on the Market, Headset Blocker. It's a small, free widget that allows you to enable/disable blocking of the MEDIA_BUTTON broadcast. You can get it here:

QR code for Headset Blocker

The source code is here.

One interesting choice I had to make when writing this app was how to toggle the blocking. I had one of two options:

1. Use SharedPreferences to track when blocking is enabled. Capture all MEDIA_BUTTON broadcasts, but only abort when the preference is enabled.

2. Enable/disable my app's BroadcastReceiver. It will always abort when enabled, but is only enabled via the widget.

I went with the latter option, as I felt that would save just a little bit more battery when disabled (as the BroadcastReceiver itself would be disabled).