Monday, May 28, 2012

Sony SmartWatch as name tag

As I mentioned earlier, I got a Sony SmartWatch at AnDevCon. I have been trying to come up with app ideas, but most of them didn't pass the "I can just pull out my phone" test. I was chatting with some friends, and one of them unclipped the watch from its strap and clipped it on his shirt. Now that's an awesome name tag!

I decided to write an app that reads images out of a folder on the SD card and show them on the watch as a slideshow. I first wrote a widget, but that did not work well because there was a loading screen between each refresh. I looked into the SDK some more, and decided to write a control instead. I refactored my widget code to add a control, but the control simply would not show up on the watch. I was studying the sample code very carefully and just could not figure out why. So I went to bed.

The next morning I started a fresh project to write the control. That worked. I then added a widget. Now the widget would not show up. It suddenly occurred to me that perhaps the LiveWare manager stored the app properties in a database, and did not reset the state when I reinstalled the app, so it did not recognize the additional control/widget.

To test that theory, I went to "Manage Applications" and cleared the data of the LiveWere manager. Well well, now it didn't even know about the SmartWatch! I uninstalled the SmartWatch app and reinstalled it from Google Play. And then I did the Bluetooth pairing again. Finally I installed my extension, and indeed, now my control showed up. That was really difficult to debug!

At this point the control was just displaying some hard-coded images. I swapped in a MediaStore query to get images from the phone:

String[] projection = {
Cursor cursor = mContext.getContentResolver().query(
  MediaStore.Images.Media.DATA + " LIKE ?",
  new String[] { folder + "/%"},

A few notes here:

  • I use ORIENTATION to rotate the image before displaying.
  • The "where" part of the query is specified by data binding. It is a safe way to say MediaStore.Images.Media.DATA + " LIKE '" + folder + "/%'"

The SmartWatch dims the screen to save battery, but a dimmed name tag is a bit useless, so I added an option to leave the screen on.

setScreenState(alwaysOn ? 
  Control.Intents.SCREEN_STATE_ON : 

After adding the option to change the slide duration, I was ready to deploy. Very curiously, I got a fatal lint error, complaining that I did not localize my app to zh-rTW and ja. I could disable the lint, but as luck has it I speak both Chinese and Japanese, so I decided to localize the app!

The extension is now available on Google Play:

You can also check out the Chinese and Japanese descriptions. I got lazy for Japanese, so I did not talk about selecting a folder or using it as a name tag. If you have a good translation, let me know!

The extension took me 2 days end to end. Overall it was a pretty good experience, except the part where I needed to clear data from the LiveWare manager when adding a control to an installed extension.

If you are an Android developer and you would like to get a SmartWatch for writing your own apps, you can submit a request to Sony using this link: If you do, ping me. I'm curious what you'll do with the SmartWatch!

Friday, May 25, 2012

Monkey Write: Now with pronunciations!

When I showed Monkey Write to new users, 8 out of 10 people would ask me, "why doesn't it pronounce the character?" My philosophy has always been "launch early, launch often", so I put Monkey Write on the market even though it was missing this obvious feature. Not any more - the latest version of Monkey Write includes pronunciations!

I recorded the characters over the weekend:

See the fancy microphone? My friend does voice-over for videos as a hobby, and I spotted the microphone when I went over to his place for board games. I borrowed it for the recording, and the sound was wonderful.

For software, I used Audacity. It was fun to see the sound wave of the characters. Look like tropical fish to me!

I sounded out all the characters in a workbook, usually twice per character, and then exported each character as a separate ogg file. Audacity made that pretty easy, which was a relief, since I always ended up yelling at iMovie whenever I needed to edit videos. A media editing UI that makes sense! Such a pleasure. If you know any one who worked on Audacity, please give them a pat on the shoulder from me.

Monkey Write pronounces the character automatically when you enter this screen. If you want to hear it again, press the sound button on the top right corner. When you are done writing the character, you will hear the character one more time.

The pronunciation-enabled version of Monkey Write is now available on Google Play Store, Amazon Appstore and the NOOK shop. Grab a copy, and let me know what you think. Will be great if you can write a review on the store too!

Thursday, May 17, 2012

AnDevCon III

When I decided to get onto the speaking circuit, my very first acceptance was my proposal to AnDevCon, way back in January. Four month later I suddenly realized that the conference was coming up in a week, and got really excited. The anticipation reminded me of the way I felt when I was going to visit an amusement park. But instead of roller coasters and free fall machines I was looking forward to tech talks and new gadgets. And AnDevCon did not disappoint.

Great talks

The speaker lineup was quite impressive:

  • My favorite talk was by Jake Wharton, the author of ActionBarSherlock. He showed us some powerful design patterns, each with a dead simple drop-in library for implementation. On top of that he was very candid, so his talk felt more like a friendly chat than a stiff lecture.
  • Saša Gargenta is so knowledgeable about the Android stack that my head was spinning just after a quick glance at his architectural diagrams. I learned a lot from his session.
  • I have watched Romain Guy and Chet Haase on video, but I had no idea that they are so funny in a live show!

My talk

I'm very honored to be speaking among such luminaries. Last month I read Confessions of a Public Speaker, and the author suggested bringing books as presents to encourage audience participations. I have not written any books, so instead I baked some Android cookies to give away.

I gave them to people who asked and answered questions, and I think that lightened the mood quite a bit. As the crowd warmed up we had a pretty good dialogue going, and I really enjoyed giving the talk.

Great discussions

Throughout the conference I got to geek out with my fellow Android developers, share our common frustrations over loading bitmaps, swap tips and tricks about xml declarations, and support each other in making outstanding applications. I am very happy that there was ample time between sessions for coffee breaks, where most of these conversations happened.

Great swags

The exhibition hall was a treasure trove. I got many t-shirts and fun little toys, but the exhibitors were also very generous in handing out devices to aid developers. I submitted a few app ideas to Sony and got a SmartWatch to develop them. The Blackberry folks was so impressed with Monkey Write that they gave me a Playbook to port it over. And on top of that, I won a Sony Xperia Arc S in the raffle!


Much like a visit to an amusement park, I am still abuzz with excitement from AnDevCon. I have quite a few new things to try for Monkey Write, and I may write an app or two for the SmartWatch too. I highly recommend AnDevCon to all Android developers.

More AnDevCon III coverage

Saturday, May 12, 2012

Popcorn.js with html5 slides

I heard about popcorn.js for a while, but did not have a chance to try it out. When the video of one of my talks became available, I knew I found a nail for this lovely hammer: I'll use popcorn.js to synchronize my slides to the video!

It only took a minute to embed the vimeo video. Next I wanted to change the size, so I added style="width: 160px; height: 120px" to the div container. No video. Baffled, I scoured the web for popcorn.js demos, and compared the source code with mine. By process of elimination I discovered that my html5 slides was to blame. The body tag has display: none so the unstyled articles tags do not show, but this means when popcorn.js cannot measure the size of the div container, it got zero width and height. As a work around, I added = "block" after the html5 slides are initialized, but before I create the popcorn object.

Next I annotated the slides with timings from the video. I extracted the timings and use the Code plugin to update the slide. The first jump worked. But when I added a second one, the slides were no longer moving.

My code looked like this:

for (var i = 0; i < articles.length; ++i) {
  var article = articles[i];
  var timing = getTiming(article);
    start: timing,
    onStart: function(options) {

After a lot of experimentation, I finally got it to work:

for (var i = 0; i < articles.length; ++i) {
  var article = articles[i];
  var timing = getTiming(article);
    start: timing,
    slideNumber: i,
    onStart: function(options) {

Notice how I stash away the value of i in the hash as slideNumber and grab it from the options inside the onStart callback. What happend was that when I use the variable i directly in the callback, its value has been changed by the for-loop when the callback is executed, so it ended up calling gotoSlide(article.length), which is not valid.

My slides now have a small video playing in the top right corner, which auto-advances the slides when you start watching. Have a look and let me know what you think!