Monday, July 30, 2012

Android: Swipe Image Viewer

EDIT: I have a much simpler implementation with ViewPager, and the github project has been updated. See post here.


I have a GrideView of images, which enlarges to full screen when you tap on any image. When I showed it to my friend he swiped to get to the next image, but that did not do anything because I was just using a plain old ImageView. I looked around for a standard widget to do that, and couldn't find any. So I unwillingly rolled my own solution: http://github.com/chiuki/android-swipe-image-viewer.

I show the image in an ImageSwitcher. It contains two views, one displays the current image, and the other one holds the image to swap in. I lifted the gesture listener code from the Android Gallery app:

private class SwipeListener extends SimpleOnGestureListener {
  private static final int SWIPE_MIN_DISTANCE = 75;
  private static final int SWIPE_MAX_OFF_PATH = 250;
  private static final int SWIPE_THRESHOLD_VELOCITY = 200;

  @Override
  public boolean onFling(MotionEvent e1, MotionEvent e2, 
      float velocityX, float velocityY) {
    try {
      if (Math.abs(e1.getY() - e2.getY()) > SWIPE_MAX_OFF_PATH)
        return false;
      // right to left swipe
      if (e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE
          && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
        moveNextOrPrevious(1);
      } else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE
          && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
        moveNextOrPrevious(-1);
      }
    } catch (Exception e) {
      // nothing
    }
    return false;
  }
}

moveNextOrPrevious() swaps in the previous or next image with the appropriate animation:

private void moveNextOrPrevious(int delta) {
  int nextImagePos = mCurrentPosition + delta;
  if (nextImagePos < 0) {
    mOverscrollLeft.setVisibility(View.VISIBLE);
    mOverscrollLeft.startAnimation(mOverscrollLeftFadeOut);
    return;
  }
  if (nextImagePos >= mImages.length) {
    mOverscrollRight.setVisibility(View.VISIBLE);
    mOverscrollRight.startAnimation(mOverscrollRightFadeOut);
    return;
  }

  mImageSwitcher.setInAnimation(
    delta > 0 ? mSlideInRight : mSlideInLeft);
  mImageSwitcher.setOutAnimation(
    delta > 0 ? mSlideOutLeft : mSlideOutRight);

  mCurrentPosition = nextImagePos;
  mImageSwitcher.setImageResource(mImages[mCurrentPosition]);
}

Notice how I show an overscroll grow when the user reaches the start or the end of the list of images. I make it visible immediately, and then use an animation to fade it away.

Right now all the logic is in MainActivity. I thought about encapsulating the viewer code into a custom view and bundling it into a jar file, but I am not sure how many people will find it useful, so laziness got the better of me. If you want to get a jar file to reuse this code, please let me know.

Until then, here is the source code: http://github.com/chiuki/android-swipe-image-viewer.

Tuesday, July 24, 2012

#weareallawesome for public speaking

Every Tuesday at 1pm Pacific Time We Are All Awesome holds office hours on #weareallawesome on freenode. This week I asked Lorna Mitchell to share her experience at OSCON.

lornajane: I have never been to OSCON before, and I got two talks accepted that were kinda out of my league (I just submitted in the vague hope that something would happen)
chiuki: What do you mean by out of your league?
lornajane: both were scheduled on the same day - and they both went well!!! One of them was so full they shut the doors 5 minutes early
lornajane: chiuki: they were topics I didn't feel expert on
chiuki: ah
chiuki: but topics people want to hear
lornajane: yes, exactly
chiuki: what are the topics?
lornajane: I am very good at writing abstracts now - I have lots of practice at that! It was a PHP 5.4 talk (which actually was quite fun) and one about REST
lornajane: I am expert in REST but I hate speaking about it because it's contraversial and that part of the industry seems to make a habit of flaming and then I get upset
lornajane: the rest of my week was amazing, I've never been to Portland before and I loved it
chiuki: were there pointed questions at the REST talk?
lornajane: I met new friends and caught up with old ones, attended talks and tutorials in all kinds of technologies that I know nothing about, met loads of exhibitors and had fun at the parties
lornajane: chiuki: almost none. I mean, I got some questions laid down as a challenge, but I gave my (not-the textbook-version) opinion and the guy thanked me for my input
chiuki: cool
chiuki: so no flaming, then
lornajane: none!
lornajane: actually the OSCON crowd were all very friendly. I didn't know what to expect which was maybe fuelling my fears

I was curious about topics to submit, and Lorna has some insights:

chiuki: Any tips on submitting proposals to OSCON? What kind of topics go well?
lornajane: chiuki: it seems to be complicated, because there are tracks, so you're sort of submitting to subconferences
lornajane: so I aimed my talks firmly at the PHP track, and I know those people (and I stalked them on twitter!)
chiuki: That's a good way to look at it, to treat each track as a subconference
chiuki: I bet each track has its own review committee too
lornajane: chiuki: it exactly does
lornajane: I'm well-known in the PHP community so I pitched two talks to them (which they took) and one more general one (which didn't get in)
chiuki: the general one for a different track?
lornajane: yeah, it was like "algorithms for a geek lifestyle" or something like that
chiuki: oh I remember now
chiuki: I actually submitted something
lornajane: because I saw a few lifehacker type talks
chiuki: for being an indie developer
chiuki: but got rejected
lornajane: but mostly it was just good, clean tech all the way through
chiuki: next year I'll submit some hardcore Android talks for the mobile track
lornajane: chiuki: definitely do that!
chiuki: I think I got confused by the OS part of OSCON
chiuki: focusing too much on open source
chiuki: Android is open source, but that's not the part I work with
lornajane: they aren't militant about open source
chiuki: so I was trying too hard to cater to the OS part
chiuki: I'm looking at the program
chiuki: my existing Android talks will go really well

chiuki: I wonder if the twitter stalking helped you got your proposals accepted, lornajane :-)
cczona: what was the twitter stalking?
lornajane: chiuki: I don't know! I figured it didn't hurt :) Laura told me though that they had loved my proposals and that it was unusual to take two
lornajane: cczona: I know who runs the PHP track at OSCON so I tweeted at her when I submitted, just a little touch to remind her
cczona: Good idea.
chiuki: yeah, it's good to get your name in front of people

Rebecca Murphey, Carina Zona and Estelle Weyl chimed in when we discussed speaker packages:

lornajane: the main thing with oscon is that they have no speaker package
lornajane: so unless you work for a big company, it's hard to go
chiuki: no financial aid whatsoever?
lornajane: officially, not at all. In fact they can help out if you ask them, sometimes, but it seems a bit hit and miss
rmurphey: how much are oscon tickets and oscon sponsorships?
lornajane: rmurphey: I am not sure, I think the tickets are 1200 USD
chiuki: 1200USD????
chiuki: OMG
rmurphey: i am a tad bit puzzled as to why they can't cover speaker costs
rmurphey: sell one ticket per speaker, done
lornajane: rmurphey: the speakers are confused about that too
lornajane: rmurphey: most of them are big company people so it's no pain to them
lornajane: OSCON did cover my flight when I asked them to (I'm coming in from Europe and I'm self-employed)
rmurphey: good for you for asking lornajane :)
lornajane: rmurphey: actually, estellevw told me to
lornajane: and the devchix helped me find somewhere cheap to stay
lornajane: then the phpwomen helped me with my slides because it was all quite short notice
lornajane: hurrah for women in tech, is all I can say!
cczona: yeah
chiuki: hurray!
cczona: that's a great testament to the support available in the community
lornajane: *and* the systers had a lunch while we were there and I met more cool ladies doing amazing things. I felt so inspired by everyone's support
cczona: :-)
lornajane: oscon gives a pass, I should have said that. But nothing else
estellevw: O'Reilly does pay for some speakers. I assume if they want you to speak and don't think you can fund it thru a company.
rmurphey: if you would like to read a host of opinions on the topic, https://gist.github.com/3098860
rmurphey: but be warned
rmurphey: there are 181 comments

This is just a sample of the things we discuss on #weareallawesome. What do you want to talk about? Come join us some time!

Wednesday, July 18, 2012

Sony Smart Wireless Headset

I got a Sony SmartWatch from AnDevCon, and published two apps on Google Play:

Slideshow for SmartWatch

Fake Call for SmartWatch

I told Sony about my apps, and as a result they sent me a Sony Smart Wireless Headset to develop more apps!

I went to Google Play to get some apps for the headset, and there aren't many available. Here is the Twitter app:

The headset has a display, a microphone, and comes with an SD card to store your music. Besides playing music from the SD card, you can listen to the radio, and stream music from your phone or computer. It also has quite a few buttons:

  • Back key/Menu key
  • Previous key
  • Music key/Select key
  • Next key
  • Action key
  • Power key
  • Volume controls

With earphones and microphone, a display, and all these buttons, I'm sure there is a cool app waiting to be written. Guess it's time to study the SDK!

Sunday, July 1, 2012

Public speaking mid-year score card

Can you believe it's July already? I started the year with a resolution to be a public speaker, with the specific goal of giving 5 lightning talks and 3 full-length lectures. Let's see the mid-year score card!

Lightning talks

  1. April 2, 2012: Ignite Where 2012, San Francisco, CA
  2. June 28, 2012: Ignite Google I/O, San Francisco, CA

Full-length lectures

  1. April 6, 2012: Fluid Android Layouts, WindyCityGo, Chicago, IL
  2. April 11, 2012: Caching Strategies for Mobile Apps, Philly ETE, Philadelphia, PA
  3. April 17, 2012: Mobile Caching Strategies, Twitter Engineering Submit, San Francisco, CA
  4. May 16, 2012: Reusable Custom Components, AnDevCon III, Burlingame, CA
  5. June 8, 2012: Progressive enhancement for Android web apps, Dutch Mobile Conference, Amsterdam, the Netherlands

Looks like I'm coming short on the lightning talk side, which is quite surprising. I thought it would be more difficult to get accepted to give lectures. Also interesting that all my lightning talks were in Ignite talks, which is way more challenging than the speak-at-a-meetup quick talks I had in mind when I came up with the goals.

Ignite at Google I/O

Ignite talks are challenging because it follows a very specific format: 5 minutes, 20 slides, auto advancing. The auto-advancing slides is the trickiest bit, since you don't control the rhythm any more. I rehearse way more for Ignite than my full-length lectures, because I need to internalize the timing to sync my speech to my slides. It's almost like lip-syncing!

The first time I gave an Ignite talk was at Where 2012, to a crowd of 30 people or so. I was rather scared of the auto-advancing slides, but I found that I just need to wait until the next slide to appear before transitioning to a new topic.

Ironically, because I felt I did pretty well at my first Ignite, I was more nervous when preparing for my second one. I kept thinking that it was not quite as good. I mentioned that to my friend Julia, and did a practice run with her. She loved it! I notice that I really feed off the energy from the audience, even if only one person was listening. So the practice run was all I need to get back my confidence.

Ah, confidence is such a fragile thing. I walked into the room, and wow, it's big! It probably seats a thousand people. A thousand people! Plus the event was live streamed. I never spoke to such a large crowd, and I was so ridiculously nervous while waiting for the show to start. Fortunately once I got on stage I was back to my elements.

Here is the recording of the Ignite show at Google I/O. Let me know what you think!

Tuesday, June 19, 2012

How to be a confident speaker

Zach Holman posted an excellent article on tips and tricks for public speaking, which got me thinking about my own experience. I have given a few speeches now, and I'd say the most important thing is to be confident. I know, that's like saying you need to breathe in order to stay alive. So here are some practical tips.

Practice until you are comfortable, but not more

I have heard many contradicting advice on practicing: practice until you memorize your speech, don't memorize your slides because the presentation will be stiff, record your speech to find things to improve, etc etc. Truth is, everyone is different, so the rule of thumb is practice to the point you feel comfortable.

Personally I never write down what I plan to say, for I fear that I will miss a sentence when I'm on stage. Instead I use my slides as guidelines and talk over them. This gives spontaneity to my talks, makes them lively. I also don't do speaker notes, but some people swear by them. Again, do what's comfortable for you.

Have a mock session

Now, how do you make sure you don't over-practice until you beat the life out of your speech? Schedule a practice session! With a solid deadline you will have to stop practicing and actually give your speech.

When you give your mock session, do it for real: hook up your laptop to a projector, and speak to a live audience. This serves many purposes:

  • Time your speech. How many minutes do you take to go over a slide?
  • AV issues. How do you change the resolution of your laptop? If you are playing video, where does the sound go? How is the contrast on the images? Font size OK?
  • Learn to look at your audience. Connect with them, speak to them, clarify if they look confused. Chances are, you will feel encouraged by their approving nods, and forgot that you were nervous.
  • Stumble onto random problems, and recover from them. There is never a perfect speech, but knowing that you can get yourself out of a situation is very reassuring.

Most importantly, the practice session proves one thing: you can talk!

The audience assumes you know more than them

You've done your practice session, now you're ready for prime time. It's natural to be nervous, but remember, once you stepped on that stage, you are the expert. People come to listen to you because they want to learn from you. They could go to another talk, chat in the hallway, or stay home to watch TV. But no, they chose to come to your talk, because they believe you can teach them something.

The scale is tipped in your favor because you are on stage. Everybody wants you to shine, so whenever you have self-doubt, remember that.

It is okay to say I don't know

You gave your speech, and now comes the terrifying time: questions! I used to feel like if I don't know the answer I would disgrace myself in front of a crowd. But guess what, it's not a test! You are there to share what you know, not to be a walking encyclopedia. If you don't have a ready answer, just say so. Turn the question to the audience and ask if anyone knows. Usually an interesting discussion will unfold. If not, have them contact you afterwards, and promise to follow up.

Just do it

You cannot learn to ride the bicycle by reading a book. Start small, give a short talk at a local meetup. Be yourself, and go with the flow. You will be alright.

Tuesday, June 12, 2012

Dutch Mobile Conference

Ladies and gentlemen, I am proud to announce that I am now an international public speaker, for I just came back from Amsterdam after giving a talk at Dutch Mobile Conference.

Proposal

Since I just got started in public speaking this year, I had no idea how conference proposals are selected, and I was submitting to pretty much every conference that I have something to share. Android conferences are obvious, but I even submitted something to Fluent Conf, a javascript conference. Now, the normal thing to do is to propose a javascript talk, since I did work on web development for a year and a half. But no, I ended up drafting a talk on embedding a WebView in an Android app and writing a javascript bridge to call native Android functions from a web page. I wasn't expecting it to be accepted, but I thought I'd try anyway.

Fast forward a few weeks, and I was browsing Lanyrd for Call for Speakers, and stumbled upon the call for Dutch Mobile Conference two days before the submission deadline. DMC focuses on mobile web apps, so a talk on bridging web and Android with a javascript bridge sounds pretty relevant. Copy-and-paste I went, submitting my Fluent Conf proposal. On a fine morning in February I got an email from the organizer for DMC. They accepted my proposal!

Preparation

I did not prepare my talk until May, a few weeks before the conference. At the point I was panicking a little, because I don't use WebView in my day-to-day work, and I didn't feel comfortable giving a talk about it. I was speaking at AnDevCon III around then, and Stephan Branczyk asked me to repeat my session at his Android meetup. I told him that I'd rather give the DMC talk since I wanted to rehearse, and he liked the idea.

As I prepared the slides, I discovered that I am actually describing the Android implementation for PhoneGap. More panic ensued. After all, they could just use PhoneGap, so why bother listen to me explaining how to roll your own? I thought about not giving the talk at all, but flights and hotel have been booked months in advance, and I really couldn't get myself to give up the chance to see Amsterdam. I went ahead and give my talk at the Alameda Android meetup, and the audience found it interesting. One of them was using PhoneGap, and he was happy to learn the inner workings. That gave me back my confidence, knowing that I am still sharing valuable information.

Talks

Comes June, and off to Amsterdam I went. DMC ran alongside DPC, Dutch PHP Conference. The same conference pass was good for both conferences, and I ended up going to quite a few sessions on the PHP side. They were not strictly PHP talks though, but rather related technologies that are useful to PHP developers.

Scalability talk
Scalability talk

For instance, Thijs Feryn gave an overview of scalability, and many of the same technologies can be used for a Django or Rails server.

Frontend and backend
Frontend and backend

My talk was in the afternoon on the first day.

I reviewed my slides the night before my talk, and realized that I prepared it for the Android meetup, but this audience probably little or no experience in Android development. I did not really have time to prepare more slides to give an Android overview, so I just added a slide with the Android logo, and talked over it.

At the beginning of the session I warned the audience that it was going to be very heavy on the Android side, and invited them to ask me to clarify if anything was not clear. I was very happy when the questions got raised, because it meant people were following along. And they seemed to like the talk, according to feedbacks on joind.in. Yay!

Socials

The organizer took great lengths to make sure that everyone enjoyed the conference. There was a social gathering for all the attendees at a bar after the first day, and I stayed until 3am, chatting with everyone.

As a speaker there were even more events. The day before the conference we met in the hotel bar to get to know each other, then went to dinner at a restaurant next door. On the first night there was a speaker dinner, where we were presented our speaker gift - an Arduino!

Arduino Uno
Arduino Uno

That was the perfect gift for techies like me. I have wanted to tinker with Arduino for a while, but didn't get around to order one. So I am very excited about this gift.

Overall the organizers took really good care of the speakers, and I had a wonderful time. A big thumbs up to the everyone running the conference!

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 = {
  MediaStore.Images.Media._ID,
  MediaStore.Images.Media.DATA,
  MediaStore.Images.Media.ORIENTATION
};
Cursor cursor = mContext.getContentResolver().query(
  MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
  projection,
  MediaStore.Images.Media.DATA + " LIKE ?",
  new String[] { folder + "/%"},
  MediaStore.Images.Media.DATA);

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 : 
  Control.Intents.SCREEN_STATE_AUTO);

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:
https://play.google.com/store/apps/details?id=com.sqisland.smartwatch.slideshow

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: https://www.surveymonkey.com/s/VT9D25X. If you do, ping me. I'm curious what you'll do with the SmartWatch!