ActiveAndroid, an ORM package from Michael Pardo, is a great addition to your Android toolset. One of the things I missed dearly from web development was a decent ORM package, and this fills the gap pretty nicely – blending easy DB interaction with helpful adapters to bind querysets to ListViews. There are some problems, however (deriving from a particular AOSP ticket).

Essentially, using a Date field in an AA model will result in storage and recall as a Date, which is fine. Using an EntityAdapter to bind it to a ListView, however, will result in Date.toString() being called – as you’d expect. We don’t always want date output as “Mon Jun 21 08:00:00 Australia/Perth 2010” though. Especially in a ListView, where that’s defined as too damned long. We can’t easily change EntityAdapter (being a closed proprietary package) to do something other than call toString() though, so what are our options?

  1. Store the date as a Date and parse the field as necessary
  2. Attempt to mangle an extended TextView class with an onDraw() call
  3. Store the date as something other than Date (like a String) and parse it in/out as necessary
  4. Bind the TextView showing our date with a TransformationMethod

Some more unappealing than others, I tried all 4.

1. Store the date as a Date and parse the field as necessary

There’s a key problem with this solution – Android 2.x’s implementation of Date.toString() doesn’t match any accepted standard – the TimeZone IDs that are outputted with the rest of the date are unparseable, meaning that we either have to strip out the ID with replace() or simply ditch the idea altogether. Of course, the upside of storing it as a date meant that ActiveAndroid could easily sort it for return to our list.

2. Attempt to mangle an extended TextView class with an onDraw() call

By supplying a custom view class, we can extend the usefulness of particular View widgets and make them do different things. So if we know we’re being passed a String of Date format EEE MMM dd hh:mm:ss zzz yyyy, we can attempt 1) and strip the zone out, then parse the result and reformat it into something appropriate.

The problem with this approach was that onDraw() tended to simply write every DateView in the list with the same date, which is unacceptable.

3. Store the date as something other than Date (like a String) and parse it in/out as necessary

In this particular case, we didn’t really care about timezones (an approximate date (no time) was good enough). As such, we could simply store the date as a yyyy/MM/dd string. Unlike the MM/dd/yyyy string we were taking as input, the former can be sorted using an alphabetic sort algorithm – so it doesn’t matter that it’s not stored in the DB as a Date. While I was initially using Joda to parse dates, further research into android-DateUtils and SimpleDateFormat did the job. For example, to get a time period between dates we can just use:

String period = DateUtils.getRelativeTimeSpanString(new SimpleDateFormat("yyyy/MM/dd").parse(input.toString()).getTime(), new Date().getTime(), DateUtils.DAY_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE).toString();

In this, we take a yyyy/MM/dd datestring and get back something like “1 day ago” or “in 4 days”, or for exceptionally far-away periods, something like “September 1”. Bonus: cutting Joda (while it’s a terrific library) saved 300kb of install size + memory, and considering the non-Joda version is only ~60k, that’s a significant decrease.

4. Bind the TextView showing our date with a TransformationMethod

I haven’t seen anything about TransformationMethods anywhere else, so I’ll do that in more detail on another post.

Advertisements