I went fishing for some age-appropriate games to put on the phone, and found a couple. One of her favorites is Doodle Toy, a delightful little drawing game. She really enjoyed that, but at some point it went ad supported (not great in a toddler app).
Inspired by the time I taught her to draw circles in Doodle Toy, I played around for a few weeks in a very lackadaisical fashion and put together my first real Android App. It's a very simple tracing game; glide your finger over the black stencil to turn it pink. A little congratulatory sound cue plays when you trace the whole image, and the next one appears. There are only six images; no randomized order, and they loop forever.
Now that the project is done, I have some time to decompress and take stock. Here's a brief tour through the process: What I used, what went smooth, what got bumpy, and what I'd like to do in the future.
The Build Environment
Configuration of an Android project was pretty simple. I started at developer.android.com and basically followed the steps, the key ones being installation of the Android development environment and ant1.8. My primary machine was a Windows Vista box, but for the finishing touches I used a small laptop running Ubuntu Lucid Lynx 10.04. The Ubuntu box was actually a little quicker to set up (with the one exception of the need to switch in ant1.8 instead of the regular ant package; no need to build from source, just a different package).
In lieu of using Eclipse, I opted for the command line tools and emacs. What I lost in autocompletion and refactoring support I'm pretty sure I made up for in portability, small footprint (I dread the notion of running Eclipse on my tiny laptop), and nearly-nonexistant garbage collection cycles in my IDE.
The only other issue I ran into was a gotcha that multiple people have reported about the tool suite in Ubuntu: the first time you "ant installd," you're likely to get a permissions error because adb running as a user probably can't access the devices it needs to talk to your Android. The easiest (maybe not so secure ;) ) workaround is to sudo killall adb, then sudo adb devices to force the daemon to run (and stay running) in root. A maybe more secure and less hackish option is given here, but I haven't tried it.
Writing the App
The app is a bit of a hackjob, and it shows... Quite literally, as you can browse the source code here. There's a lack of division of interest (Traceview.java is pretty overloaded to do not just rendering and events, but state change from image to image also). I think there's a fine line one can walk between too much modularity and too much complication; the rule of thumb I use these days is to hack a solution in first, then refactor later. You'll also notice a lack of test cases; the project is simple enough that end-to-end testing takes about thirty seconds, so this hasn't been an issue. Topic for another day: I also find unit testing of essentially visual / event apps a real chore on most architectures, and I haven't yet gone looking to see if there's a clean tool for such things on Android.
One thing I'm pretty happy about is standardizing on SVG for the image format. It really freed me up in the test-and-iterate cycle, as I was able to lean on the svg-edit demo to do all my trace data. I'm pretty enamored with SVG for something this simple; the ability to pop back and forth between visual and text representation of the data turned out to be pretty awesome. I also lucked out and found this awesome SVG to Android Picture converter library, which is Apache licensed. I hacked some specific tweaks into it for my needs, but it was already a 95% fit for my project and saved me the drudgery of hooking up an XML parser (which, I'm told, causes the death of a kitten every time it's done).
Apart from those bits, the only fiddly pieces were the code that senses touch drags and applies them to the image. I originally put a lot of effort into making only the endpoints of a tracing "hot," so you had to trace contiguously. Observation of my niece playing the game showed that it was not only hard to get right, it was irritating to use and placed unneeded restrictions on the player---after all, she's not angling for a high score, she just wants to turn the image pink, and if she wants to do that by squeegeeing with her finger like she's cleaning a window, maybe I should get out of her way! YAGNI.
(There's a future treatise on "Games vs. Toys" here, but it's a story for another day. Suffice to say, I wish I'd remembered the lesson I learned watching her play when I was naming the project, because I should have called it "Frances's Tracing Toy").
Things I Liked
- The documentation on Android is very decent. It walked me through most of the fiddly bits and platform-specific pieces. I walked through the "hello world" cat example and went from there. Anything I couldn't immediately glean from the documentation, I was able to find in the blogging community and stackoverflow.com.
- The test-and-iterate cycle is pretty dang short with my phone plugged in. It's smart about the details, like quitting the app if there's already an instance running.
- The resource abstraction is a pretty good abstraction, and the way resources bind into the code layer is extremely convenient. I ran into a couple of tiny issues where I had behavior in the wrong place in the Traceview code that crashed because it was being run at resource-loading time and tried to touch nonexistant resources, but that was an easy issue to diagnose and solve.
- Publishing flow to the Play Store was pretty much a breeze. I paid my dues and churned out art assets using svg-edit, Paint.NET, and Gimp. The "promotional" shots I screen-grabbed from the phone itself (Thanks, screenshot hard-button!).
- One program, compatible with 1,318 device configurations according to the Play Store. Awesome!
Issues
- The aforementioned device issues on Linux. But that can probably be filed under "Every experience I've ever had plugging a new hardware device into a desktop Linux machine."
- I tried to get cute by centering my trace object using layouts instead of rolling my own offsetting code for the event and rendering handlers. It ought to work, but it got fidgety; I had to force both a re-render and a re-layout of the containing view as well as the child view (it appears that resizing a child doesn't necessarily trigger a repaint for the child's old position rectangle in the parent, though to be honest I didn't track down the exact details). There's still a squeaky little bug in the interactions between parent and child layers, and I'm sadly increasingly convinced that it hasn't been worth it to try and shortcut writing my own centering code by using the layout engine.
- I got very weird behavior when I tried to specify the <uses-sdk android:minSdkVersion="15"/> rule in my manifest (as that was the only system I'd tested on): the toy broke utterly (played sound but drew only a white background and nothing more). This was very unexpected behavior, and I haven't yet tracked down a root cause. As a result, I set my minSdkVersion to "1", and I'm hoping I don't get too many bugreports from people who's Android phone
was made by cavemenis a bit older. - My program is ostensibly compatible with 1,318 device configurations. That's... a lot more than I have access to for testing purposes. I've tested, in fact, on a grand total of one phone. It's a bit freaky to think about all those people who are going to be using this toy on a hardware config for essentially the first time... On the other hand, that's half the fun of PC development, right?
Future Ideas
There's plenty of places I can take this in the future, if I feel like it (and since the code's public, so can you! Just buy me a coke some time if you end up making a mint off of your version ;) )
- More shapes! My niece's parents have already asked for numbers and letters. I could have fun with that. :)
- Fading from a sketch outline to a full-color version when you complete a picture.
- Suppression of the menu bar and soft controls, with a "parent access" secret to get you back to the rest of the phone (personally, I wouldn't recommend letting a toddler have a phone all to herself anyway, but if this offers peace-of-mind to some parents, I could probably make it a thing).
- Usability tweak: Currently, the event listener does the simplest thing and takes the first-index touch point when detecting a touch. But one thing I've noticed about little users is that they aren't great at balancing the phone yet and tend to grip from the side, meaning there's often a little palm touching the screen providing a false reading. I bet I can work around that if I index through all the touch points. :)