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.

Advertisements

#Androiddev fiddling – package deploy error and using preferences

Hit an error this morning when deploying an Android project:

WARN/ResourceType(586): Bad XML block: header size 28024 or total size 1702240364 is larger than data size 776
WARN/PackageParser(586): Unable to read AndroidManifest.xml of /data/app/vmdl39451.tmp
WARN/PackageParser(586): java.io.FileNotFoundException: Corrupt XML binary file
WARN/PackageParser(586):     at android.content.res.AssetManager.openXmlAssetNative(Native Method)
[…]

ERROR/PackageManager(586): Couldn’t find a package name in : /data/app/vmdl39451.tmp

Eclipse had also given me some kind of error about being unable to parse the output of aapk and to check console output. I’ve deleted console output so I don’t remember what was there, but I’m not sure it was illuminating. This was pretty confusing. I played around with cleaning the project in Eclipse; I tried creating the apk myself to see if I got any interesting errors:

[luke@7of9 Listviews]$ apkbuilder bin/Listviews.apk -rf .
Using keystore: /home/luke/.android/debug.keystore
[luke@7of9 Listviews]$ ls -l bin
total 40
-rw-rw-r–. 1 luke luke  5060 Sep  5 07:54 classes.dex
-rw-rw-r–. 1 luke luke 18925 Sep  5 08:49 Listviews.apk
drwxrwxr-x. 3 luke luke  4096 Sep  5 07:52 net
-rw-rw-r–. 1 luke luke  6995 Sep  5 07:54 resources.ap_
[luke@7of9 Listviews]$ adb install -r bin/Listviews.apk
250 KB/s (18925 bytes in 0.073s)
Can’t dispatch DDM chunk 46454154: no handler defined
Can’t dispatch DDM chunk 4d505251: no handler defined
pkg: /data/local/tmp/Listviews.apk
Failure [INSTALL_FAILED_INVALID_APK]

Alas; not very helpful. But somewhere along the way, I did get something useful:

Parser exception for /Listviews/AndroidManifest.xml: The element type “activity” must be terminated by the matching end-tag “</activity>”.
org.xml.sax.SAXParseException: The element type “activity” must be terminated by the matching end-tag “</activity>”.

Ah – now that I can understand and do something about. Not sure why Eclipse didn’t give me a heads up on that. Adding that extra slash fixed everything :-)

Preferences

For some reason there’s virtually no documentation I can find in the developers guide about the handy Preferences activity and resource type. What I know I get from Ed Burnette’s book Hello, Android; and frankly that’s not too detailed about what’s going on.(OK; I found some very general stuff here and more specifics about using the activity and resource in the PreferenceActivity reference docs.)

This was a problem when I tried to set up my Preferences activity and got a runtime exception after choosing a setting from a list.

ERROR/AndroidRuntime(931): java.lang.NullPointerException
ERROR/AndroidRuntime(931):     at android.preference.ListPreference.onDialogClosed(ListPreference.java:218)
ERROR/AndroidRuntime(931):     at android.preference.DialogPreference.onDismiss(DialogPreference.java:359)
ERROR/AndroidRuntime(931):     at android.app.Dialog$DismissCancelHandler.handleMessage(Dialog.java:946)

[…]

Those are all Android classes – none of mine are in the mix. The pref I tried to change comes from this specification:

<ListPreference
android:key=”layout”
android:entries=”@array/layout”
android:entryValues=”@array/values”
android:title=”Layout”
android:summary=”Which layout do you want?”
/>

Nothing obviously wrong. It occurred to me, though, that the values array there is an integer array, while the entries are, naturally, strings (for the user to see).  I tried changing the values to strings (and then parsing the prefs into integers when they come out) and this seemed to work fine. So evidently you can’t use integer arrays directly as entryValues on a ListPreference.

preferences, more headers/footers on listview

Java tutorial entries here and here helped clear up my understanding of generics and what’s going on with my last post. Basically what I was trying to do with an array is not supposed to be done, for esoteric type-safety reasons. I tried refactoring to use List<Class<Activity>> instead of an array but the cascading errors/warnings I got from that were puzzling. Maybe try that again later.

Annoyance: my preferences class has to use PreferenceManager.getDefaultSharedPreferences(context) all over its static methods. Surely there’s a way to cache the result of this verbosity? Probably in an Application object. I tried creating an Application object and caching getSharedPreferences() during its constructor (so that it could go in a final member) but this gave me a null pointer error:
  java.lang.NullPointerException
     at android.content.ContextWrapper.getSharedPreferences(ContextWrapper.java:134)

Looks like the line it’s talking about is this:
     return mBase.getSharedPreferences(name, mode);
but mBase is set in the constructor so I don’t know how that would be null. I don’t know, but I did find out that if I called it in onCreate in the application, that worked fine. So I guess something is in place there that isn’t during the constructor.

I futzed a little bit more with setting headers/footers on the ListView. Here’s what I recall finding so far:

  • Forget about putting buttons or editors or anything like that which receives focus in headers/footers; that’s not going to work with navigation without some major magic (though they’ll be accessible by touch)
  • If I have header and an “empty” view, the header won’t show when the empty view is shown (list empty). I think the footer does, strangely. If I don’t define an empty view, headers will be shown all the time.
  • I can put stuff in my layout after the listview, and it looks kind of like a footer. But it can’t be navigated to, and more importantly if the list is longer than the screen it can’t even be seen.

I’m planning to just define adapters and views for custom list elements to do what I want (e.g. a “create a new entry” list item, no need for empty view), and otherwise use menus.

customization

Been fiddling with Twitter today. I used twitbacks to create a background picture so my profile isn’t quite so mundane. I finally started using lists (now if echofon would just let me see tweets from a list that would be something). I looked around for other #androiddev folks – not too many active at the moment, but then it is the holidays. I see some of the value of twitter but at this point it still seems like more of a time sink for me than a value. I have a feeling I shouldn’t miss out, though, ought to expand my usage, link everything I’m doing together and promote it. Because I do feel like I can add a lot of value to a corner of this internet.

I thought I’d take a crack at subclassing AlertDialog like I wanted. The goal is this: customize the view to include a text entry field linked to the positive button such that there has to be some content in the text entry field before the button is enabled. Simple enough for a single custom dialog – just set a key listener on the entry field and enable/disable the button accordingly. But I want to do this in several places so it seemed like a good idea to make a custom dialog class that could be configured a little differently each time but with the same general behavior. I thought all I need is AlertDialog with a custom view (it doesn’t allow any sort of text input) and some added behavior, right?

Subclassing AlertDialog turns out to be tricky. I mean, a simple subclass is trivial, of course, but the problem is the actual building of the dialog, which is normally accomplished by the AlertDialog.Builder inner class. This in turn relies on AlertController.AlertParams to hold the parameters that will be applied to the dialog when finally it’s built. Unless I’m mistaken, I’d have to recreate all that behavior in my builder. Suck! Forget it! I think what I’ll do instead is just have the custom dialog I already almost finished expect to be given a view with certain element IDs and wire them up. Well – even that is sucky because getting at the elements inside the dialog from outside the dialog pretty much sucks – which is why an AlertDialog would have been nice in the first place. Ok. I’ll make a fairly stupid class that just supplies the button-wiring behavior and let specific dialogs subclass from that and specify what gets wired. That works.

RTFMing some more. Defining preferences from XML isn’t described anywhere in the dev guide – but “Hello, Android” is helpful here. However, I had to use a little trial and error to figure out how to use a ListPreference (well – probably there are examples in the sample programs, but I figured it out). Here’s what a ListPreference definition looks like:

<ListPreference android:title=”Tracker click”
 android:summary=”What should happen when you click on a tracker?”
 android:key=”trackerClickBehavior”
 android:entries=”@array/trackerClickBehaviorVisible”
 android:entryValues=”@array/trackerClickBehaviorStored”
 android:defaultValue=”view”
 />

Of course the arrays are defined in values resources like so:

<string-array name=”trackerClickBehaviorVisible”>
    <item>Create instant log</item>
    <item>Create detailed log</item>
    <item>View tracker details</item>
</string-array>

Simple enough.

It’s good to know that you can create a custom toast layout if you so desire.