Migrating to Android
I went to Google I/O in San Francisco this year, and lo and behold, they started off the conference by giving all 4000 of us free, unlocked Android phones! Not bad for a $300 conference.
I do like the iPhone (my current ride) a lot. The hardware is very sleek and the OS is pretty nice. But, their development philosophy seems very closed to a Linux geek like myself. First off, I have to live in the Apple world in order to build an iPhone app. I don't actually own a Mac, which is apparently a bit of a barrier right there. And if I actually want to geek out on my iPhone, I have to go around Apple's back and jailbreak it so I can run what I want.
Not so with the egalitarian Android. The SDK runs on Windows, Mac, or Linux. As soon as I installed the SDK, I had an easy shell from my workstation to the USB-connected phone. I'm not really a fan of Java, but this is already encouraging me to make some Android apps, a feeling I never really had from the iPhone.
In terms of general usability, here are some things I've noticed in my short time with Android so far:
- The virtual keyboard is slightly laggy when responding to my finger taps (compared to iPhone). This can cause more typos that I don't register until I've already gone a few keys past, and so I find myself backspacing more often to fix mistakes. This is mitigated by typing in landscape mode, as the keyboard itself is bigger.
- I love the Back button! IMHO, this functionality should really be on iPhones as well. If I open an email in Android, then click a URL inside, it will take me to the browser app. If I then click the Back button, it takes me back to my email message. With the iPhone, I have to click the Home button first and then load up the mail app again.
- No two-finger pinch/zoom stuff in the default Android kernel, though I'm told this can be enabled with a custom kernel. Based on that, I'd guess that two-finger tricks will be arriving in an official capacity soon.
- Google integration is really nice to have. If you already use Gmail et al. in your day-to-day life, then all this data is magically on your phone too. I'm sure MobileMe is like this too, but I think more people use Gmail than MobileMe. And Gmail is free.
- There's no 1/8" headphone jack on this handset (HTC Magic), only a USB connection. Of course, they do provide headsets (two!) with the phone, but it'd still be nice to have a standard stereo jack.
- In general, I find the app organization (or lack thereof) and both iPhone and Android to be annoying. Both choose to lay out all apps in a single panel, so it becomes time-consuming to find an app if you have a lot of them installed. I would prefer some sort of hierarchical system, so I could organize all my networking apps in one area, productivity apps in another, games in a third, etc.
So despite my general happiness with the iPhone as an end-user, I'm giving Android a solid chance. The first thing I had to do was get my contacts moved over to the new phone. This, surprisingly enough, proved to be non-trivial. You'd think it could be as simple as hitting an "Export to SIM" button on one phone and an "Import from SIM" on the other. The problem is that neither phone provides an "Export to SIM" function. They'll happily import contacts off a SIM, but I guess nobody has had a need to export yet. And any sort of third-party app was conspicuously absent from the App Store. Hmmm. It smells a bit like data lock-in, but I'd never say that out loud.
Eventually, I just SSH'ed into my iPhone and started poking around. Turns out the contacts are all centralized in a SQLite database! That made life a lot easier. I copied it over to my workstation and wrote a little Python script that massaged the contact data into CSV format. I imported the CSV file into my Gmail contacts and seconds later, they all showed up on my Android phone. Cinch.
If you're interested, the DB is in /var/mobile/Library/AddressBook/AddressBook.sqlitedb.
Here's the Python as well. It's a little rough around the edges -- I've been doing a lot of Javascript lately, and kept finding myself trying to do prototypal things in Python. That didn't work.
#!/usr/bin/python
import io, sys
import sqlite3
# get headers from CSV template
# <http://theregoesdave.com/wp-content/uploads/2008/10/gmail.csv>
hdrs = io.open('gmail.csv', 'r').readline().strip().split(',')
print '"' + '","'.join(hdrs) + '"'
conn = sqlite3.connect('AddressBook.sqlitedb')
conn.row_factory = sqlite3.Row
cContact = conn.cursor();
cContact.execute("SELECT * FROM ABPerson ORDER BY Last,First")
# From the ABMultiValueLabel table
Labels = ("", "Mobile", "Work", "Home", "Other")
for contact in cContact:
# get phone numbers
cPhone = conn.cursor();
cPhone.execute("SELECT value,label FROM ABMultiValue WHERE record_id=? AND property=3 ORDER BY label", (contact["ROWID"],))
phones = {}
for p in cPhone: phones[Labels[p["label"]]] = p["value"]
if(len(phones) == 0): continue # skip people without phone numbers
# get email addresses
cEmail = conn.cursor();
cEmail.execute("SELECT value,label FROM ABMultiValue WHERE record_id=? AND property=4 ORDER BY label", (contact["ROWID"],))
emails = {}
for p in cEmail: emails[Labels[p["label"]]] = p["value"]
# Use "Section 1" for home/mobile
# Use "Section 2" for work
rec = {
'Name': "%s %s" % (contact["First"], contact["Last"]),
'E-mail': emails["Home"] if emails.has_key("Home") else emails["Work"] if emails.has_key("Work") else "",
'Section 1 - Description': 'Home',
'Section 1 - Email': emails["Home"] if emails.has_key("Home") else "",
'Section 1 - Phone': phones["Home"] if phones.has_key("Home") else "",
'Section 1 - Mobile': phones["Mobile"] if phones.has_key("Mobile") else "",
'Section 2 - Description': 'Work',
'Section 2 - Email': emails["Work"] if emails.has_key("Work") else "",
'Section 2 - Phone': phones["Work"] if phones.has_key("Work") else "",
'Section 2 - Company': contact["Organization"] if contact["Organization"] else ""
}
# prune empty fields and section descriptions
def prune(dict, key):
for k in dict.keys():
if len(dict[k]) == 0: del dict[k]
ct = 0
for k in dict:
if k.startswith(key): ct = ct + 1
if ct == 1: del dict[key+' - Description']
prune(rec, "Section 1")
prune(rec, "Section 2")
# write out as CSV
row = []
for h in hdrs:
row.append(rec[h] if rec.has_key(h) else "")
print '"' + '","'.join(row) + '"'
Hope it helps. Of course, you will need a jail-broken iPhone in order to SSH in and copy a raw file off it. If you use Windows, you can probably just sync your contacts to Outlook and then export from there.

June 11, 2009 at 5:53 pm -0700
Of course, you can have multiple folders for multiple things or use one of the 3 desktops for its own thing.
June 11, 2009 at 7:45 pm -0700
Ah, very useful, thanks. I still tend to forget about trying a long-press!
- J
June 11, 2009 at 9:25 pm -0700
I had to do a little sqlite hacking as well on the iPhone- don't know if you saw my post about hacking the SMS database (http://www.toofishes.net/blog/iphone-sms-database-hacking/), but Python proved to be very useful for that.
June 27, 2009 at 6:25 am -0700
Thanks for sharing this code, but it gives me an error:
" File "csv.python", line 37
'E-mail': emails["Home"] if emails.has_key("Home") else emails["Work"] if emails.has_key("Work") else "",
^
SyntaxError: invalid syntax"
June 29, 2009 at 6:20 am -0700
What version of Python are you using? That line uses two nested ternary conditionals, which might not be supported in earlier versions.
You can always expand those ternaries into a normal if/then/else structure -- that should work with any version of Python.
- J
June 30, 2009 at 6:11 am -0700
"File "csv.python", line 8
print '"' + '","'.join(hdrs) + '"'
^
SyntaxError: invalid syntax"