Exploring ApiDemos; misc Android

Android 1.5 is down below 8% market share. If my wife’s Cliq ever gets an upgrade I’m dropping that platform like a hot potato. What’s interesting is that Eclair is actually still more common than Froyo (40%) – you’d think if they’ve done the work to get the device to 2.1, and seen how quickly versions change, it shouldn’t be too big a step to 2.2. Oh well.

I’ve been looking through ApiDemos to learn. While most of the demo classes seem well-commented, amusingly the ApiDemos.java file itself is not, and it does some non-obvious things involving searching the package manager for intents to dynamically create the lists being browsed. The upshot is it’s less than obvious how to find the sample code that matches the sample functionality you browse to – nothing tells you how to make the mapping, and it’s not always obvious. The actual mapping is between the label listed in the AndroidManifest.xml file for the activity (which is what you see in the app’s list browser, except that in many cases the actual text is in the strings.xml file) and the class name listed with it, e.g.:

<activity android:name=".view.Tabs1" android:label="Views/Tabs/Content By Id">

I would argue that the mapping between what you see (Views/Tabs/Content By Id – which again is often in strings.xml for no good reason I can see) and the class (com.example.android.apis.view.Tabs1) is far from obvious. Call me crazy, I think a README or at least a couple lines of comments in the main class would be in order. I should submit a patch.

Found a little nugget in ApiDemosApplication.java (which is otherwise empty):

PreferenceManager.setDefaultValues(this, R.xml.default_values, false);

This is a one-time setting of default prefs. Now of course, default_values.xml couldn’t be something simple like key-value pairs. It’s a PreferenceScreen like you would use for defining a PreferenceActivity. You wouldn’t know about those from the dev guide, unless you like decyphering reference docs. But as it happens I learned about these at some point and created a prefs screen with them in LogMyLife… which currently has no effect, so I need to do something about that.

I finally got around to gathering sources for the 2.2 platform to attach in Eclipse. I’ll have to update my article one of these days. Aside from the new sources, I should include the Apache license files in order to be proper.

I’m looking at migrating this blog to Blogspot. WordPress has neat themes but it deliberately blocks integration with tools like Adsense and Google Analytics. Also this editor drives me nuts. We’ll see.

Permissions automatically added to android app manifest

Last week when I displayed a QR code to download my app, the one person who talked to me about it was a security person, and of course she asked: why does it need these permissions?

Storage: modify/delete SD card contents. Phone calls: read phone state and identity

Say what? I didn't ask for those!

Truth be told, I had no idea (except for the start-at-boot, which is in the manifest and needed so it can install alarms at boot). I noticed them when I did a test install but didn’t think much about it because I was in a hurry. No, LogMyLife doesn’t need to write to the SD card or read the phone identity. OK, it will need to write to SD once I implement export/import of data; but it will never need to read the phone identity.

Tonight I finally got a chance to look into this. It turns out, if you build your app to run on the 1.5 platform (which LogMyLife does so that my wife’s Cliq can use it), these permissions are silently added when the APK is created. Believe it or not this is “working as designed” according to this Google Code issue. Romain Guy could have been a little more helpful there and provided a link to some kind of useful explanation, but I get the drift: because these permissions were introduced in 1.6, they’re grandfathered in to apps built for previous versions (which didn’t have to declare them), and so have to be included for them to work on platforms 1.6 and up, just in case they’re needed.

There’s some kind of voodoo in the Android Market that keeps these added permissions from showing up at download/install time. But they’re still technically there, and they’ll show up if the user looks at the application permissions later. Also they’ll show up at install via non-Market path, like during beta testing. Sigh! Fortunately, or un-fortunately, most users are pretty oblivious to permissions. But not security people.

So, what to do? I could exclude my wife and the other unfortunates stuck on 1.5 and just target 1.6+. Perhaps I could provide different APKs, one just for 1.5, and another for 1.6+? Well, regardless of that, what I think every app author should be doing is explaining exactly what they’re doing with those permissions. I’ll include a section in the app README, and also in the Market description (once it’s on there), explaining what permissions are used and why, and I can explain where these two come from if needed.

Android packaging and publishing

I’m giving a lightning talk on LogMyLife and other Android-y stuff this Friday at SplatSpace. I thought it’d be nice to allow people to download the app and try it out if they so desire, even though it’s still pre-release at this point. So I looked around on github and found where I could create downloads (in addition to the source download that’s automatic).

On a different machine from what I usually develop on, I created a new LogMyLife package. Actually, it wasn’t quite that simple, because I discovered my SDK debug keystore cert had expired. Which wasn’t quite that simple either, as I had to track down the error message (which I forget at this point). But, congrats me, I’ve been at this for a year now! Or not congrats so much, as I still don’t have a complete app on the market to show for it. But I have my excuses.

Anyway, I deleted the existing keystore and the Eclipse ADT plugin kindly made another one behind the scenes, and I carried on. I uploaded the package and got an error, but it seemed to have succeeded. The cool thing was when I found github automatically pulls a QRcode from a Google service for the uploaded APK:

So I can just include that in my slide and people can use their Android device’s barcode reader to scan it and download if they so desire. So can you while you’re looking at this.

Of course I tried this, and I tried it on my phone, where I already had LogMyLife installed. It downloaded and nicely showed me the permissions it needed and notified me it would replace the current app. Then it proceeded to fail to install. There wasn’t really an informative message, but I think I know what happened.

That key I was talking about earlier? Well, it was of course different on my two dev machines. I’m pretty sure the phone refused to install the app because the key had changed from what was previously installed. I would have to un-install the app in order to install the version signed with a different key. Which leads me to wonder whether I’ll have the same problem once I sign it with my official market signing key for release? I don’t want to lose what data I already have been collecting on my phone, so if that’s the way it will be, I’m gonna be prioritizing the “Export/import data” feature way up.

I went ahead and signed up for an Android Market account finally. I’ll probably post something for beginners about the process on my neglected “Android from scratch” blog at some point. It’s pretty simple, but you don’t know what your $25 gets you to start with. Strangely enough, there doesn’t seem to be anything about signing your app on the market site. There is on the dev site, of course. It seems somewhat complicated to do by hand, but pretty simple from the Eclipse plugin. Nothing seems to mention what happens when you change keys – what the practical consequences of doing that would be. I’ll find out soon enough. Just for fun I tried uploading my dev LogMyLife.apk, and received only the error message “Upload a valid APK.” I guess if you don’t know about this stuff they’re not gonna help you find it!

Showing HTML assets in Android

I want to show a nice intro dialog when someone first visits my app (and thereafter on  request). Showing the dialog is easy, but how do I show it with HTML styling? Because that’s what I really want – paragraphs, bullet points, headings – those things are difficult to pull off in Android XML layout.

The obvious candidate is the WebView. Just get my HTML content in there and I’m golden. One problem: WebView seems mostly geared toward loading external URLs. That’s what all the tutorials show. There’s also a WebView.loadData method where you can just give it a string, but that just pushes the problem back, because you can’t really include HTML styling of any significance in an Android resource; as far as I can tell it just gets stripped out unless it’s bold, underline, or italicize.

So what about just putting my HTML in a file and loading that in? Seems like a good idea, but the only apparent way to do this is to use Resources.loadRawResource which returns an InputStream, which is pretty low-level stuff. Seriously? Has to be a better way.

And there is. I just need to put my file in the right place (the “assets” folder at the top level of my project) and refer to it with the right URL (file:///android_assets/file.name) in the WebView.loadUrl method. Would have been nice for the official docs to mention that… I found mention of it in this forum post.

Next problem: the WebView doesn’t load the content right away – it takes a second to display in the emulator. Any way to make it preload? Not that I can see.

Tangent: I still don’t know what all the inputType settings on an EditText do. Particularly, I’m looking for the one that causes input to suggest and correct words as you’re typing. I set up a simple example project to try out all of the ones listed in the InputType class. None of them had that particular effect, so it seems I need to look further.

It seems I need to combine types. textAutoCorrect does not imply text –  have to put inputType=”text|textAutoCorrect” to get what I want. Looks like that does what I want, but not if I add “textAutoSuggest” – intuitive, no?

 

Android testing

After a nice presentation introducing TDD on Android, I wanted to jump right in with LogMyLife. Of course, the very first thing I tried, I got a lovely error:

java.lang.RuntimeException: Unable to resolve activity for: 
Intent { action=android.intent.action.MAIN flags=0x10000000
 comp={net.sosiouxme.logmylife.activity/net.sosiouxme.logmylife.activity.Main} }
at android.app.Instrumentation.startActivitySync(Instrumentation.java:447)
[...]

I just love errors like this which pretty much just give me no idea what’s going on. But I got a clue from this StackOverflow question on the same error – when I followed the tutorial, I adjusted it for LogMyLife’s package structure, in which all Activities are in a sub-package (.activity) from the main one. So my constructor looked like this:

public MainTest() {
  super("net.sosiouxme.logmylife.activity", Main.class);
}

The tutorial doesn’t really say what these parameters are. Evidently the first one should be the package of the app (in this case, “net.sosiouxme.logmylife”), not the activity being tested; I guess this makes sense for the instrumentation to hook into. But it’s an easy mistake to make…

Next problem: one of the things I’m most interested in testing is my BroadcastReceivers, particularly the one that receives BOOT_COMPLETE (hard to attach a debugger in time to see that one go through). Oddly, while there are instrumentation classes for the other three major Android components, there’s none for a BroadcastReceiver. I asked about it on StackOverflow, we’ll see if there’s an answer (I seem to have a talent for asking questions that get no answer). Maybe this can just be mocked somehow?