Running into (minor) Sqlite limitations

Sqlite is pretty nifty and has lots of great features for such a small footprint. It’s deliberately lacking a number of things. I ran up against one of those today: you can’t drop or rename columns. (Of course, you could create a new table with the right columns, copy the data from the first table, drop the first table, and rename the copy.) Also, foreign key checks were evidently implemented fairly recently, and aren’t included in the stock sqlite shipped with Android.

Now here’s another interesting bit. You can create an index on a table and specify ASC or DESC, but unless the database is created with the right file format, the ASC/DESC won’t do anything. The default is to create the legacy file format (for compatibility). How would I create a database in the newer format? It’s not clear in Android where everything is expected to be filtered through the SQLiteOpenHelper, which doesn’t really have a lot of options. There’s some hints at this StackOverflow answer – you can use SQLiteDatabase directly to set flags when opening. I’m not sure that’s worth the trouble for me. I’m writing a personal logger, so I know things will be accessed in pretty much the same order. I don’t know how much difference it makes to order the index though. I’ll let it be for now, but would like to know if there’s an easy way to set flags via SQLiteOpenHelper.

Advertisements

Restricting text to one line with EditText/TextView

I’ve let slide my habit of writing about what I’m doing. Let’s try to pick it back up.

I spent some time style-izing some things in WhenDidI. For one thing, I wanted to restrict text fields to just one line (not allowing newlines). It’s not enough to have android:lines=”1″ on the EditText. That keeps the display to one line, but doesn’t keep the user from using newlines to create a multi-line entry. This was worth creating a style for:

    <style name=”NameEditor” parent=”@android:style/Widget.EditText”>
      <item name=”android:layout_height”>wrap_content</item>
      <item name=”android:layout_width”>fill_parent</item>
      <item name=”android:lines”>1</item>
      <item name=”android:inputType”>textCapSentences|textAutoCorrect|textAutoComplete</item>
    </style>

The inputType attribute allows many different values, so check them all out. Somewhere in there is the one that lets the on-screen keyboard suggest words while you’re typing. Anyway, the point here is – if you specify inputType but don’t include textMultiLine, the user doesn’t get to use newlines.
N.B. it can be nice to put layout_height and layout_width in a style to reduce clutter – of course this isn’t a good idea all the time.
So the user can’t enter multiline text now, but they can still enter giant amounts of text, and I’m wanting these values to display in one line in a ListView item. How do you cut text short in display? Well, there’s the android:ellipsize attribute, but this doesn’t actually seem to work. That bug report contains a workaround, however, without even using any deprecated attributes:
    <style name=”EllipseText” parent=”@android:style/Widget.TextView”>
      <item name=”android:lines”>1</item>
      <item name=”android:scrollHorizontally”>true</item>
      <item name=”android:ellipsize”>end</item>
   </style>

This does the trick, although in my scrolling ListView the ellipses seem to only show two dots instead of three. Maybe judicious use of padding / margins will fix that.

Playing around with DD-WRT

I finally got around to trying DD-WRT. I have a friend who raved about it and I always meant to learn a little more about networking, but I just never got around to it until now. My major motivations for doing it now:

  • Friends/family coming over wanting to use the wireless; but I don’t want to tell them my password and don’t particularly want them on the same network as my other stuff. So I want to offer multiple SSIDs and segregate networks.
  • OK, this isn’t really specific to DD-WRT, but my old router only supports WEP “security” – needed to get with the times!
  • Want to set up outgoing VPN connections at the router level instead of doing this per machine (having this is another good reason to isolate visitors from the main LAN).
  • And hey, a local VPN accessible from the internet too, though most routers probably do this out of the box?
  • Static DHCP and DNS allocations so all my devices get names without having to cart around a hosts file or set up my own DNS server. Since I’ll be setting up a ton of VMs this is kind of important.
  • Ability to set up virtual LANs and fiddle with various networking things I don’t really understand yet.
  • Ownership of my router. Ability to do all this stuff on my terms instead of with whatever capabilities the manufacturer deigns to put in the thing (reserving the best for a more expensive model).

I am not foolhardy enough to try this with the router I currently have in place and risk having no internet connection except my phone. After a little searching on Amazon (having Prime pretty much rules where I look for things now) I decided on the Linksys WRT160N. This looks pretty decent even out of the box, and for $31 I wasn’t going to cry much if I bricked it. I didn’t have any way of knowing which version I’d be getting – according to the router database v1 and v3 are supported, but not v2 – but as luck would have it I got v3.

The installation guide for DD-WRT in general (and the “Peacock” thread) is full of all kinds of warnings about how you need to read *everything* on the wiki to avoid bricking your router. In that way it’s worse than manufacturer manuals! Sorry, fellas, I know you’re trying to help but it’s a bit much. The main thing I got out of this is that it’s really important to do a 30/30/30 reset before and after flashing the firmware.

The basic procedure for flashing DD-WRT onto a router from scratch is as follows:

  1. Comb the DD-WRT site for all the instructions and firmware images you’ll need, and have them on your computer before you try to do anything.
  2. Connect your computer to the router with a cable (doing it wirelessly isn’t gonna fly).
  3. Do a 30/30/30 reset on the router.
  4. Using the router’s standard administration UI (which allows you to reflash firmware in order to enable manufacturer updates), flash the first firmware image – this will usually be a special bootstrap image (or a “kill image”) just to make the transition off the factory router firmware.
  5. Do a 30/30/30 reset on the router.
  6. Now using the DD-WRT UI, flash the real firmware you’ll be using. You may not have a lot of choices – I had two listed in the router database.
  7. Do a 30/30/30 reset on the router.
  8. Start looking through all the crazy options DD-WRT offers, and enjoy!

The specific instructions for the WRT160Nv3 are pretty succinct and I got things working pretty quickly. Yeah – it’s really not too bad! I had it working right away with only one hitch – my browser kept caching the “apply.cgi” page which is used to alter settings. The cached page was simply blank and doesn’t actually apply anything (of course). There used to be a simple way to clear cached items in Firefox but it doesn’t seem to be in the settings anymore – maybe it’s extended settings or needs a plugin now. I solved it simply by setting up a /etc/hosts entry for the router and referring to it by a new name.

DD-WRT offers the capability to set up virtual SSIDs, but this is a little trickier than the interface would have you believe. Once you create a virtual SSID, at least if you want to isolate it as I mentioned above (and why else would you want to do this?), you need to do some hackish stuff. I found a guide here that was very helpful. Alas, I think my stock G1 phone ran afoul of some of the problems with virtual SSIDs mentioned there, because it holds onto the main SSID from an early configuration I tried and completely refuses to see the new setup (even after I manually entered the SSID and WPA2 password for it). My modded G1 seems to see everything fine as does my laptop. I will probably have to play around with SSID visibility to get this right (or – mod my stock G1 – but it’s my main phone currently and I’m reluctant to lose all my settings etc.).

The DD-WRT help that comes with the firmware (displayed from the UI so you don’t even need working internet) is pretty decent (to say nothing of the help you can find on the internet). There is an unbelievably variety of features built in. This should keep me busy for a long time!

filtering listviews like the contacts app

I thought it would be nice to provide filtering capabilities in my ListViews – they could get long. It would be nice if this was kind of done automatically for you when you put android:textFilterEnabled=”true” on the ListView, but this is not the case; I suppose in the end it’s better to have to be explicit about exactly what’s being filtered and how, and this is Java after all.

Enabling a filter as above just enables the view to gather text the user types; it doesn’t actually do anything with it unless I set up the adapter to use it. I got a pointer from this thread. My ListAdapter is a subclass of SimpleCursorAdapter so all I had to do in my ListActivity is:

  adapter.setFilterQueryProvider(this);
  // implement FilterQueryProvider
  public Cursor runQuery(CharSequence constraint) {
        Cursor cur = mDba.fetchTrackers(mCurrentGroupId, constraint.toString());
        startManagingCursor(cur);
        return cur;
  }

I’m really hoping this doesn’t leak cursors or something. Anyway, having properly implemented fetchTrackers such that it returns a filtered query, that’s all there is to it – typing next now filters the list items.

Next problem: what happens when I type in a filter, select an item to start another activity, and then return? In the Contacts app (my model for this), the filter actually remains in place, which is a little odd. In my app, the filter *text* remains, but the items in the list are not filtered unless I start typing again! Very odd. I would like to either clear the filter or have the persistent filter applied when I resume the Activity.

There’s pretty much zippo on d.android.com about this. A Google search found me this interesting tidbit – not actually relevant to my problem I don’t think but it gives me a lead on something else I discovered the other day: if I used an alternate IME (say the chinese one) on a textfield, the text entered didn’t seem to get through to the OnKeyListener I set on the EditText. I’ll have to try a TextWatcher.

So anyway, my best bet is probably looking through the Contacts app – open source, right? Yay. Except I sometimes wonder if making something open source leaves less incentive to provide decent documentation. In any case, a quick look through the Contacts app didn’t yield any clues and I’m gonna have to stop for the night.

extending Android’s TimePicker with seconds

My current quest: the TimePicker provided with Android doesn’t have a field for seconds. Boo. So I want to add my own. TimePicker internally uses a widget called NumberPicker, so I should be able to just another of those at the end for seconds. But it’s an internal widget, and we are strongly cautioned by the Android devs not to use it directly.

So although it would be bad practice to use internals directly, I can clone the source and implement it myself, right? In theory. In practice, I’m feeling a major WTF coming on…

Incidentally, those source bundles I provided in my “see the source” article? Don’t include much from com.android.internal. I don’t know why; just because it’s internal doesn’t mean we won’t debug into it. I’ll see about including those (will have to modify the script for gathering source).

NumberPicker is using resources from com.android.internal.R; fine, those can be cloned too (grumble grumble). And it’s using another internal widget, NumberPickerButton, which can also be cloned (grumble).

Also, it’s accessing mContext from its parent class, View. Why is eclipse claiming problems with this? The member in question is a protected field, so it ought to be visible in subclasses. WTF… perhaps this has to do with being marked “hide” so the SDK doesn’t know about it? But it should work anyway… hmm. Well, getContext() should accomplish the same thing, so I’ll just do that.

Track down all references to internal and point to my own clones. And… it works! Well, that’s something.

Of course, what I probably really want to do is subclass TimePicker into my own widget and add the NumberPicker there. If that’s possible. Because then I could leave the AM/PM button in place.

I think this is hard enough that it warrants a feature request. Including seconds or not oughta be a simple option on the TimePicker…