GPS Still on After LocationManager.removeUpdates?

My most recent Caltroid update added map view with two location listeners: one coarse (=network) and one fine (=GPS) location listener. Or so I thought. Even though I was calling LocationManager.removeUpdates() for both location listeners, I could still see the GPS icon active at the top, and logcat showed GPS activity still going on. Users were reporting drained batteries. Not good.

It took me a few false starts until it occurred to me that I was also using MyLocationOverlay to automatically put the user’s location on the map. Well, this obviously requires location updates. I had somehow falsely assumed that as soon as the map view went away, MyLocationOverlay would stop listening to location updates. That is not the case. You must explicitly call MyLocationOverlay.enableMyLocation() to start updates, and MyLocationOverlay.disableMyLocation() to stop updates.

After figuring this out the fix was easy. In my map view’s onResume() I request location updates for my two location listeners and enable MyLocationOverlay, and in onPause() I do the opposite. No more drained batteries!

Caltroid with Map

My latest update to Caltroid added two notable new features: the next available train is highlighted and scrolled into view automatically, and the simple locate function was replaced with a custom Google Map view. The map has Caltrain stations as an overlay, and the map starts out centered and zoomed so that all stations should be visible. While the map is active, it will update the users current position (using GPS and/or network location), and showing the nearest Caltrain station (as the crow flies). It is possible to select this station as the starting station from the map. Additionally, it is possible to tap the directions button on the map to get driving directions to the nearest station.

Working on the map feature was a pleasure now that I actually have an Android device.

If you look at the map view carefully, you will notice the text is written in transparent grey boxes with rounded corners. A stackoverflow question showed me how to do this with custom shape resource. I also based the map overlay largely on this blog post.

I had agonized for the longest time about how to get highlighting for the the list view. Then I stumbled into a couple of stackoverflow questions that showed me the way. In the end the best example I found was the List14 sample in Android API demos. I just need to call convertView.setBackgroundDrawable() with the right drawable, and make sure to add the following attribute for the list view: android:cacheColorHint="#00000000"

A Month of Nexus One

My first Android phone is the Nexus One. I tried to wait for an AT&T one, but the battery in my 8525 wasn’t so good anymore so I decided I could wait no longer. So far I am pretty happy with the phone.

I ordered the unsubsidized phone, which leaves me an option to go with T-Mobile or staying with AT&T. Time will tell which one I choose. T-Mobile coverage is my main concern. If I knew roaming would work well in the US without obnoxious costs, I might switch. On the other hand, I have been reasonably happy even with AT&T’s EDGE speeds. It is fast enough for checking email, do light browsing, and navigation works as well. Most of the time when I need the higher speeds I am in some location where I can use wifi.

Setting up the phone turned out to be a breeze. I just went through all the settings on first boot and everything worked. I was initially somewhat concerned about being able to put in the settings for AT&T network, but I didn’t really need to do anything fancy, just looking in the wireless options and selecting the only carrier it found (AT&T).

Since this is my first actual Android device, I put my Android apps through some tests on actual hardware. Everything worked! I’ve also been able to work on some new features (namely location related) that I didn’t much attempt with just the emulator. Some updates will roll out shortly.

Getting adp working with Nexus One turned out to be a little more work. First I updated the SDKs and the Eclipse ADT plugin, but even after this the phone was just showing with question marks in the launch dialog and would not let me copy the apk and run it. I found a thread on xda-developers which gave me the answer. I am on Ubuntu 8.04 and this is what I did:

  1. Create file /etc/udev/51-android.rules with contents SUBSYSTEM=="usb", SYSFS{idVendor}=="0bb4", MODE="0666"
  2. Restart adp: adb kill-server && adb start-server
  3. Turn on USB Debugging on the phone in Application > Development settings
  4. Connect the phone with USB cable to the computer

I get great battery life on the Nexus One. And what I mean by that is that I can go for two full days before I need to recharge with my normal usage. I keep wifi, bluetooth and GPS off unless I am actually using them. I talk less than five minutes on the phone per day, browse a handful of websites for a few minutes, and read email several times a day. Surprisingly I also find myself playing some games, probably half and hour to an hour a day. You could say on most days I am not using the phone for anything that’d require a smartphone.

During my first week the phone had some stability issues, crashing several times when I had left it in the charger overnight. But once I uninstalled some applications that kept services running (SIPDroid, Fring and some other IM clients), I haven’t had a single crash. Best uptime when I remembered to check was over 300 hours.

I’ve encountered some annoying bugs as well. During my first week the phone got into a state where tapping the messaging icon always launched the browser. The only way I could recover was to reboot. This hasn’t happened since. Far more common problem is the soft keyboard not registering taps, or thinking I pressed one of the four buttons at the bottom (back, menu, home or search, with accompanied vibration feedback). Switching back and forth between apps fixes this eventually. I also seem to have situations when I don’t seem to get any GPS information. Some people have reported that this might require a reboot, but I haven’t been trying persistently enough to confirm if this is the only cure.

Voice recognition generally works great. There have been a few bizarre mistakes, like once I did a voice search saying “swype for android” and voice recognition thought I said “life on crack”. I find I miss a physical key I could press and do arbitrary voice commands, though.

One of my major issues with the phone, besides it not working on AT&T’s 3G network, is the inability to call out with Bluetooth without handling the phone. With my 8525 running Windows Mobile 5 and 6 I was able to tap my Bluetooth headset and say a name in my addressbook, and it would make the call. With Android nothing happens when I tap the headset, and I need to find my contact by handling the phone. This seems bizarre to me, since as far as I know California law requires hands free calling, and Google is based in California. Answering calls does work as I expect.

I haven’t been thrilled with voice quality on calls, which has been a surprise. I seem to get a fair bit of static. Also the earplugs and microphone that shipped with the phone have been practically useless; the other party can hardly hear anything when I try to use them.

I haven’t bought any apps yet, although I was tempted to get Locale. However, Locale author(s?) seem to have pissed off their potential customers by silently removing the free beta when they introduced their relatively expensive paid version while at the same time apparently making the paid version worse than the free version was. I’d also be interested in being able to buy Swype, since I don’t seem to be a very good typist with the builtin soft keyboard. Unfortunately Swype is not (yet?) available on the Android Market.

I don’t want to sync my data with Google, so I am looking for some way to sync with my PC. So far I haven’t had much luck. There are apparently paid solutions for Windows, and maybe Mac, but haven’t found anything for Linux. The most promising effort to me seems to be the Funambol Android Sync Client. AFAIK you could run your own Funambol server, which would solve my sync needs nicely. Except that at this point only contact sync is supported. If my phone loses data now, I am going to be sad…

I installed the Google update which added multitouch to Google apps, and I really like it in the browser.

I haven’t found the need to root my phone yet, but I will probably do that down the line to get some of the features that are not available otherwise.

Pulling Android Market Sales Data Programmatically

Android Market handles sales through Google Checkout. I haven’t tried selling anything else online before, but what this setup provides for me as the seller leaves a lot to be desired. One issue you will have trouble with is getting the data needed to file taxes.

Google provides a Google Checkout Notification History API that lets you programmatically query sales data. For my purposes the API requests are really simple: just post a small XML document with the date range I am interested in, get back XML documents that contain my data. If there is more data that fits in a single response, look for an element that specifies the token for the next page and keep pulling until you get all data.

Below is a really simple Python script that uses M2Crypto to handle the SSL parts for the connection (needed since Python doesn’t do secure SSL out of the box). You will also need to grab certificates. You should save the script as gnotif.py, save the certificates as cacert.pem and create gnotif.ini as described in the script below all in the same directory. When you execute it, it will ask for start and end date (in YYYY-MM-DD format) and then fetch all the data, saving them in response-N.xml files, where N is a number.

#!/usr/bin/env python
# Script to query Google Checkout Notification History
# http://code.google.com/apis/checkout/developer/Google_Checkout_XML_API_Notification_History_API.html
 
# Supporting file gnotif.ini:
#[gnotif]
# merchant_id = YOUR_MERCHANT_ID_HERE
# merchant_key = YOUR_MERCHANT_KEY_HERE
 
import base64
import re
from ConfigParser import ConfigParser
 
from M2Crypto import SSL, httpslib
 
ENVIRONMENT = "https://checkout.google.com/api/checkout/v2/reports/Merchant/"
XML = """\
<notification-history-request xmlns="http://checkout.google.com/schema/2">
%(query)s
</notification-history-request>
"""
 
config = ConfigParser()
config.read('gnotif.ini')
MERCHANT_ID = config.get('gnotif', 'merchant_id')
MERCHANT_KEY = config.get('gnotif', 'merchant_key')
 
rawstr = r"""<next-page-token>(.*)</next-page-token>"""
compile_obj = re.compile(rawstr, re.MULTILINE)
 
auth = base64.encodestring('%s:%s' % (MERCHANT_ID, MERCHANT_KEY))[:-1]
 
ctx = SSL.Context('sslv3')
# If you comment out the next 2 lines, the connection won't be secure
ctx.set_verify(SSL.verify_peer | SSL.verify_fail_if_no_peer_cert, depth=9)
if ctx.load_verify_locations('cacert.pem') != 1: raise Exception('No CA certs')
 
start = raw_input('Start date: ')
end = raw_input('End date: ')
 
data = XML % {'query': """<start-time>%(start)s</start-time>
<end-time>%(end)s</end-time>""" % {'start': start, 'end': end}}
 
i = 0
 
while True:
    c = httpslib.HTTPSConnection(host='checkout.google.com', port=443, ssl_context=ctx)
    c.request('POST', ENVIRONMENT + MERCHANT_ID, data,
             {'content-type': 'application/xml; charset=UTF-8',
              'accept': 'application/xml; charset=UTF-8',
              'authorization': 'Basic ' + auth})
 
    r = c.getresponse()
 
    f=open('response-%d.xml' % i, 'w')
    result = r.read()
    f.write(result)
    f.close()
 
    print i, r.status
 
    c.close()
 
    match_obj = compile_obj.search(result)
    if match_obj:
        i += 1
        data = XML % {'query': """<next-page-token>%s</next-page-token>""" % match_obj.group(1)}
    else:
        break

As you take a look at the data you will probably notice that you are only getting the sale price information, but no information about the fees that Google is deducting. Officially it is a flat 30%, but I have found out a number of my sales have the fee as 5%. So we need to get this information somehow. Luckily you can toggle a checkbox in your Google Checkout Merchant Settings. Unfortunately there is a bug, and the transaction fee shows as $0 for Android Market sales. I have reported this to Google, and they acknowledged it, but there is no ETA on when this will be fixed.

I also haven’t found any way to programmatically query when and how much did Google Checkout actually pay me. (I can get this info from my bank, but it would be nice to query for that with the Checkout API as well.)

Last but certainly not least, working with the monster XML files returned from Google Checkout API is a real pain. If someone has a script to turn those into a format that could be imported into a spreadsheet or database that would be nice…

2009 Statistics

Looking back at my website traffic in 2009 shows pretty amazing growth:

year	#reqs	#pages	 
2006	2362	1657	+
2007	14982	9867	+
2008	335897	227906	++++++++++++
2009	1149221	826814	++++++++++++++++++++++++++++++++++++++++++

Compared to 2008, I blogged much more frequently. I actually tried to post at least once a week, but couldn’t quite keep up during the second half. I also put up the M2Crypto API documentation, which gets a fair bit of traffic.

The most popular 2009 blog post was Multicolumn ListView in Android with about 5,100 page views according to Google Analytics. Most of my posts dealt with Python, however, and I posted several snippets of code that touched projects like Django, CherryPy, boto (Python interface to Amazon Web Services), Fabric and so on.

I continued to maintain Caltroid, the first Caltrain schedule application for Android. I also released three other Android applications: d20 Ability Calculator, 20 Ability Calculator Lite and ChandlerQE for Android. All in all I made about $250 on Android software sales (gross), but since the expenses were less I actually made a slight profit. Hourly pay would counted in pennies, though.

I continued to maintain CaltrainPy and CaltrainJS as well. Other projects that I did maintenance releases on were M2Crypto.

I ported Office Resource Locator to Django.

I created a little encryption library called m2secret in Python.

2009 Donations

Time for my yearly donations to Open Source projects I use. As I mentioned when I launched ChandlerQE for Android I planned to donate 20% of the sales to OSAF. Here are the statistics:

Market           Price  Copies
==============================
Android Market   $1.99  4
Android Market   $0.99  6
SlideMe          $1.99  0
SlideMe          $0.99  3
==============================
TOTAL                   $16.87
20%                      $3.37

Somewhat of a disappointment, but understandable given that nobody seems to be working on Chandler code anymore. But I sent the 20% to OSAF anyway.

I decided to donate to three other projects this year:

Like last year, I would have wanted to donate to Mozilla Thunderbird, Pidgin, Enigmail and Pylons but didn’t see any ways to donate directly to them.

Buildbot Slave on Windows XP

Today I installed Buildbot on Windows XP and even got it to run successfully as build slave. It was both harder and easier than I expected. Or more specifically, installing all the dependencies was harder than I thought, but configuring and running the slave was easier than I thought.

I have run Buildbot server and slaves on Linux based systems without any problems. Once you have setuptools installed, it is just a matter of easy_install buildbot and creating the configuration files. I thought once I had Python and setuptools installed on Windows it would be equally simple. Not so.

First of all I tried to use somewhat nonstandard Python 2.5, for which there was no registry entry. This meant that none of the dependencies that had their own executable installers worked. When I tried the zipped versions, these also failed because apparently my Python was compiled with different version of Visual C++ than the extensions I was trying to install. I got past this by grabbing a standard Python installer and going from there.

Next I tried to install setuptools from the exe installer. That failed because it could not find msvcrt71.dll. I downloaded that and put one in Python\DLLs directory and one if the directory from which I was trying to run the installers. Then I was able to get setuptools installed.

After I modified PATH to include the Python and Python\Scripts directories I tried to use easy_install, but it failed to install any package that contained native code. I also added .py to PATHEXT as described in the Buildbot README.w32 file. I continued by grabbing the exe installers of the dependencies and running them one by one. Note that this might be too long, but I found these from various pages describing how to run Buildbot on Windows: pywin32, zope.interfaces, twisted, pycrypto and pyopenssl. I used the latest stable release of each.

Next step was buildbot itself. I downloaded the sources, unpacked, and run python setup.py install.

For a change, easy_install worked for the next two packages I wanted to use for the actual tests: easy_install nose, easy_install coverage.

buildbot create-slave command worked fine, but when I first tried to start my slave it would just sit there without doing anything nor giving any errors. It turned out I had given wrong machine name as the buildmaster. I would have expected an error when trying to start. Once I fixed that, the buildbot slave was running correctly. (Note that I have not yet tried to run it as service.)

Finally it was just a matter of fixing broken tests and installing any dependencies the code I was testing had (like .NET 3.5). Feels good having some assurance that the changes I make on Linux won’t hose the Windows world.

Secure Password Scheme for Turbogears2 Application with repoze.who and bcrypt

I am working on a little Turbogears2 application, and wanted to use the repoze.who and repoze.what packages that integrate nicely with Turbogears2 and have gathered fair amount of positive feedback. It seems usage is simple, but I was quite disappointed in how password hashes are done by default, especially after I had read Thomas Ptacek’s educational rant about secure password schemes.

It turns out by default the authentication code that gets generated for you if you tell paster quickstart you want authentication is the weak kind of way Thomas warns about. Luckily the correct way he advices is easy to put in by using the py-bcrypt module. If you want pointers, see repoze.who Issue 85, or the py-bcrypt homepage.

Unfortunately py-crypt is kind of annoying to put in dependencies. The pypi entry is named bcrypt, but the download page links to py-bcrypt, so setuptools machinery can not find the right package. This can be worked around relatively easily by adding the py-bcrypt download link to setup.cfg:

[easy_install]
find_links = http://www.pylonshq.com/download/
             http://www.mindrot.org/projects/py-bcrypt/
# ...

and then in setup.py you do something like this:

#...
 
install_requires=[
    "TurboGears2 >= 2.0.3",
    "Catwalk >= 2.0.2",
    "Babel >=0.9.4",
    #can be removed iif use_toscawidgets = False
    "toscawidgets >= 0.9.7.1",
    "zope.sqlalchemy >= 0.4 ",
    "repoze.tm2 >= 1.0a4",
    "repoze.what-quickstart >= 1.0",
]
 
try:
    import bcrypt
except ImportError:
    install_requires.append("py-bcrypt >= 0.1")
 
setup(
    install_requires = install_requires,
 
# ...

Now when you go deploy your TG2 app with easy_install or similar tools, it will download the py-bcrypt package the first time, and won’t bother you if it already exists.