Monday, February 2, 2015

gce2retrofit, a gradle plugin

I am working on an Android app that uses Retrofit to talk to a Google Cloud Endpoints (GCE) server. Initially I was writing the Retrofit interfaces and models by hand, but then I found out that GCE servers serve a discovery document describing all its endpoints in JSON. So I started working on a tool to generate the Retrofit Java files automatically from the JSON document.

JavaWriter

I used JavaWriter (predecessor to JavaPoet) for code generation because I have this vague idea that if I do it in Java I can make it a gradle plugin. My first iteration is a jar file that can be run from the command-line, taking the discovery JSON file as an argument, and writes the Java files to the src/main/java directory to be checked in. This is a major time saver since I don't have to manually write all the Retrofit classes and interfaces every time the server updates.

Gradle plugin

To make it even easier, I want the code generation to be a part of the build process. I want the GCE discovery JSON file to be the source of truth, generating the Retrofit Java files when it gets updated. I have never written a Gradle plugin before, so I went through the tutorial to get a basic understanding.

I had difficulty getting Android Studio to recognize the generated files until I found another code generation gradle plugin that works with Android: Paraphrase.

Points to note

In the gradle task:

  • Use @InputDirectory to specify what triggers an incremental build.
  • Use @OutputDirectory to create the output directory on demand.

In the gradle plugin:

  • Use project.plugins.hasPlugin to determine if it is a Java project or an Android project.
  • Use variant.registerJavaGeneratingTask to make Android Studio recognize the generated files.

Push to Sonatype Nexus

Once my plugin is ready I need to publish it. I want to push a snapshot first, so I looked into Sonatype Nexus. There seems to be many different ways to do it, and I was at analysis paralysis for weeks. Finally I turned to Twitter and Google+ to ask for help, and pushed the snapshot with the maven and com.bmuschko.nexus plugins.

Try it

If you use Google Cloud Endpoint with Retrofit, please try the plugin and file issues if you encounter any. Instructions and source code here: https://github.com/chiuki/gce2retrofit.

Saturday, January 24, 2015

Moar technical articles!

You need to write more technical articles.

Yes, you.

I could guilt you into doing it. I could tell you that you have benefited from other people's sharing so much that it is time for you to give back.

But instead I will tell you what is in it for you: Job security.

Your real résumé

The classic first step to get a job is to prepare a résumé. It could lead you to an interview if you keyword-stuff it the right way. But nowadays employers are turning to the internet to cross check if you are who you claim. A repertoire of technical articles is a solid way to show that you know your stuff, and more importantly, you can communicate.

But what do I write?

I knew you would say that. While I have no guaranteed formula that works for everyone, I can share how I make writing technical articles a part of my normal workflow.

  1. Experiment on a clean slate
    Whenever I need to incorporate an unfamiliar technology into my work, I do not do it directly in my main project. Instead, I create a brand new project and implement the minimal setup to understand how it works.

  2. Build it up step by step
    Once I get the minimal setup working, I add extra logic to get to what I need. But instead of overwriting the minimal setup, I make a copy so that I can explain the steps later. For example in Android apps I make my MainActivity a list of activities, each corresponding to a step.
  3. Integrate into the main project
    When the sample project has all the functionalities I need, I put it into my main project. It may interact with my existing code in unexpected ways, so I tweak until it works.
  4. Write the article
    After the coding is done I write the article. This is the structure I use:
    1. A back story of what I was trying to do
    2. Outline the steps I took with code snippets and screenshots
    3. Explain the problems I encountered along the way, and how I solved them
    4. Link to source code on github
  5. Push the sample project to github
    I don't publish my source code until the article is written because sometimes I thought of a better way of doing things while explaining my method in writing. Once I am happy with the article, I push my repository to github, publish the article, then add a link from the README of the github repository back to the article.
  6. Share on social network
    After everything is published, I post a link to Twitter and Google+ with a screenshot as the teaser.

End result

Here is a sample article: Partial SlidingPaneLayout

And how I shared it:

Good for you, good for others

I stumbled upon this workflow by accident. I used to stick the new technology straight into my main project, failed to make it work, and unable to pinpoint why. With a brand new project I can focus on the new stuff, work out all the kinks before I introduce the extra complexity of my main project.

Once I figured out how it works, I could have just thrown away the sample project. But what a waste! Instead I push it to github, and suddenly I am an open source contributor. Sweet!

Writing the article is a bit of extra work, but so very worth it. It gives me credibility. It lets people know that I know my tech, and I know how to explain it. Icing on the cake? It helps other people too!

Friday, January 23, 2015

Rejection Therapy

I was intrigued by Rejection Therapy when I heard it on Invisibilia. You have to get rejected by someone at least once, every single day.

I was especially struck by this example given by the inventor:

It was harder to get rejected than I thought. And that was really amazing for me, that people were actually saying yes. I'd ask for a discount at a store, and they would go, yeah okay I would sell it to you for this, and I was like what? Really?

I had a similar experience:

When the lease for my apartment was up, I asked the manager for a discount. He readily offered me a free half-month if I signed a one-year lease. Which I did. The whole thing took 15 seconds.

That was so empowering. The manager's readiness made me realize that the discount was probably sitting there all this time, waiting for me to ask. I went on to ask for more things, and getting surprised by the yeses I got. Slowly it dawned on me: it is actually hard for people to say no!

Rejection Therapy is a great exercise. Getting comfortable with being rejected gives you the courage to ask for the things you want. And if you don't ask, they cannot say yes. So ask often, embrace the noes, and enjoy the yeses!

Tuesday, January 20, 2015

Redacted font on Android emulator

I wanted to show the Gmail app in my partial SlidingPaneLayout article. However, I do not want to show my actual emails. Normally I would just blur out the sensitive data, but I want to show the cross-fade animation in a video, so that doesn't really work.

The easy way to get around this is to create a throw-away email account and show the fake data, but I came up with something more fun: replace the system font to show squiggly lines instead of words! I will be using Redacted, a beautiful squiggly font. I don't want to muck with my actual device, so I went with modifying an emulator.

yaff2 vs ext4

At first I followed this tutorial: How to pack and unpack system.img and userdata.img from an Android factory image.

The first step should be a giveaway that it may not work:

git clone https://android.googlesource.com/platform/system/extras
cd extras
git checkout android-4.1.1_r1

The tools described in the tutorial has been removed in the recent releases, so you have to check out an older tag.

In any case, I proceeded to mount system.img, replace Roboto-Regular.ttf with redacted-script-regular.ttf, and repackage the image with make_ext4fs. To compile make_ext4fs I needed to checkout platforms/system/core as well, and add its includes:

gcc -o make_ext4fs -I../../core/include -DANDROID \
  make_ext4fs_main.c make_ext4fs.c ext4fixup.c \
  ext4_utils.c allocate.c backed_block.c output_file.c \
  contents.c extent.c indirect.c uuid.c sha1.c \
  sparse_crc32.c wipe.c -lz

Next I created a new AVD, and copied the modified system.img into its folder. Alas, it did not work.

ko:Invalid cache partition image type: ext4 (expected yaffs2)

Turns out the Android 5.0 filesystem its in ext4, but the tutorial produces a yaff2 image.

Mount and sync

This tutorial has steps to make an ext4 image: How to create EXT4 images

But instead of creating a new image, I modified a copy of system.img like this:

cp ${ANDROID_HOME}/system-images/android-21/google_apis/\
armeabi-v7a/system.img .
mkdir my_system
sudo mount -t ext4 -o loop system.img my_system
cp redacted-script-regular.ttf my_system/fonts/Roboto-Regular.ttf
sync

I copied the modified system.img into the AVD folder and booted. Yes, squiggly!

Squiggly

However I had two problems:

  1. How do I add a Gmail account in squiggly?
  2. Some strings were in squiggly but others were still in English

To solve the first problem, I moved the modified system.img out of the AVD folder and rebooted the emulator to configure Gmail in English. This works because the changes are stored in user data, which will not be overridden when I swap back in the modified system.img.

To solve the second problem, I overwrote more fonts.

Roboto-BlackItalic.ttf
Roboto-Black.ttf
Roboto-BoldItalic.ttf
Roboto-Bold.ttf
Roboto-Italic.ttf
Roboto-LightItalic.ttf
Roboto-Light.ttf
Roboto-MediumItalic.ttf
Roboto-Medium.ttf
Roboto-Regular.ttf
Roboto-ThinItalic.ttf
Roboto-Thin.ttf

Everything with Black or Bold got redacted-script-bold.ttf. Everything with Light or Thin got redacted-script-light.ttf. The rest got redacted-script-regular.ttf.

Ubuntu

In case you are wondering, I did everything on my ubuntu machine. To record the video, I used SimpleScreenRecord. I chose "Select window..." to pick the emulator window, and adjusted top and height to crop out the window title. The height is the height of my emulated device, and I add the delta from height to top to compensate.

With all that, I have Gmail in squiggly, recorded to share!

Monday, January 19, 2015

Partial SlidingPaneLayout

Gmail has an interesting UI on tablet:

The side pane is always visible, showing icons when collapsed, cross fading to more details when expanded. How is it implemented?

My first observation is that the main pane slides when the side pane expands, so I know it is not a NavigationDrawer. Let's try a SlidingPaneLayout.

SlidingPaneLayout

<android.support.v4.widget.SlidingPaneLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/sliding_pane_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
  <TextView
      android:layout_width="240dp"
      android:layout_height="match_parent"
      android:background="@color/blue"
      android:text="@string/pane_1"/>
  <TextView
      android:layout_width="400dp"
      android:layout_height="match_parent"
      android:layout_weight="1"
      android:background="@color/light_blue"
      android:text="@string/pane_2"/>
</android.support.v4.widget.SlidingPaneLayout>

Looks good, except the main pane turns gray. Fortunately we can change the fade color to transparent.

SlidingPaneLayout layout = (SlidingPaneLayout) 
    findViewById(R.id.sliding_pane_layout);
layout.setSliderFadeColor(Color.TRANSPARENT);

Partial side pane

Now I want to make the side pane partially visible when collapsed. Took me a while (plus a shower) to figure that out, but once I did it was really simple: add margin to the main pane.

<android.support.v4.widget.SlidingPaneLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/sliding_pane_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
  <TextView
      android:layout_width="240dp"
      android:layout_height="match_parent"
      android:background="@color/blue"
      android:text="@string/pane_1"/>
  <TextView
      android:layout_width="400dp"
      android:layout_height="match_parent"
      android:layout_weight="1"
      android:layout_marginLeft="64dp"
      android:background="@color/light_blue"
      android:text="@string/pane_2"/>
</android.support.v4.widget.SlidingPaneLayout>

With the margin, the side pane peeks from below when collapsed.

Cross fade

Finally, the cross fade. I replaced the side pane with FrameLayout, the bottom view being the full pane and the top view being the partial pane.

<com.sqisland.android.CrossFadeSlidingPaneLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/sliding_pane_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
  <FrameLayout
      android:layout_width="240dp"
      android:layout_height="match_parent"
      android:background="@color/purple">
    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="@string/full"/>
    <TextView
        android:layout_width="64dp"
        android:layout_height="match_parent"
        android:background="@color/blue"
        android:text="@string/partial"/>
  </FrameLayout>
  <TextView
      android:layout_width="400dp"
      android:layout_height="match_parent"
      android:layout_weight="1"
      android:layout_marginLeft="64dp"
      android:background="@color/light_blue"
      android:text="@string/pane_2"/>
</com.sqisland.android.CrossFadeSlidingPaneLayout>

Then I subclass SlidingPaneLayout to cross fade between the partial pane and the full pane on slide. To do that, I need to get the two panes.

@Override
protected void onFinishInflate() {
  super.onFinishInflate();

  if (getChildCount() < 1) {
    return;
  }

  View panel = getChildAt(0);
  if (!(panel instanceof ViewGroup)) {
    return;
  }

  ViewGroup viewGroup = (ViewGroup) panel;
  if (viewGroup.getChildCount() != 2) {
    return;
  }
  fullView = viewGroup.getChildAt(0);
  partialView = viewGroup.getChildAt(1);

  super.setPanelSlideListener(crossFadeListener);
}

Since SlidingPaneLayout already has the convention of specifying the side pane vs main pane by position, I also look for the partial pane and full pane by position. The first child of the SlidingPaneLayout is the side pane, its first child is the full pane, second child is the partial pane. I stash them in the fields fullView and partialView, which are used in the cross-fade listener.

private SimplePanelSlideListener crossFadeListener 
    = new SimplePanelSlideListener() {
  @Override
  public void onPanelSlide(View panel, float slideOffset) {
    super.onPanelSlide(panel, slideOffset);
    if (partialView == null || fullView == null) {
      return;
    }

    partialView.setVisibility(isOpen() ? View.GONE : VISIBLE);
    partialView.setAlpha(1 - slideOffset);
    fullView.setAlpha(slideOffset);
  }
};

Here, I change the alpha of the partial pane and the full pane depending on the slide offset. Since I don't want the partial pane to react to touch events when the layout is open, I set the partial pane to View.GONE. The same logic needs to be applied onLayout because devices with sufficient width (e.g. tablets) may start with the layout opened.

@Override
protected void onLayout(
    boolean changed, int l, int t, int r, int b) {
  super.onLayout(changed, l, t, r, b);

  if (partialView != null) {
    partialView.setVisibility(isOpen() ? View.GONE : VISIBLE);
  }
}

Here we go, a partially shown side pane that cross fades into a different view when expanded. Enjoy!

Source: https://github.com/chiuki/sliding-pane-layout

Friday, January 16, 2015

RailsConf hangout with Sarah Mei

The conference proposal process is a bit of a mystery to most of us. What makes a good topic? How do people get picked? So when Sarah Mei offered to do a hangout, I asked her if she'd like to do one for Technically Speaking. And she did!

This is the first time we hosted a public hangout, and we had no idea how many people would come. We ended up hitting the hangout limit so a lot of folks were turned away. So sorry! The saving grace is that I took notes.

Q & A

(A = Answer from Sarah)

Q: What is your favorite talk from last year?
A: Sandi Metz gave an extremely technical talk. Usually it was very hard to keep the audience's attention to a wall of code, but Sandi pulled it off beautifully. There was another talk on the growing talent track that was interesting as well.

Q: Something I struggle with is how short the abstract is! What are the main suggestions for creating a compelling abstract when you have so few characters?
A: Phrase it from the point of the view of the audience. Instead of tell them what you did, think about what they can learn.

Q: How do you advise to find topics to talk about? Finding the inspiration in everyday work is hard.
A: Whenever I encounter something interesting, I jot it down on an index card. Not all of the ideas will turn into talks, but over time themes will emerge. Another great thing to do is to look at the schedule for previous RailsConf, RubyConf and other conferences.

Q: Can you advise anything regarding finding topics to people relatively new to Ruby/Rails?
A: One interesting topic will be, how to integrate junior devs into the team? How to bring them up to speed?

Q: My level of expertise tends to come from soft skills and I worry about the interest and appropriateness of that as a topic. Any guidelines on what you should or shouldn't use as a talk topic?
A: RailsConf 2015 has a culture track and soft skills is definitely appropriate.

Q: Why do you think it's important to speak? Can you say anything specific about how speaking affects your career or your work?
A: After I took a year off on maternity leave, I had difficulty getting back to the work place. Then I went to a conference, looked at the speakers and thought, these people don't have problem getting jobs. Put it another way, there are two ways to get job security: Work at a big company, or be internet famous. So speaking at conferences is how I provide for my family.

Q: I'm scared to do a more technical talk because I'm more comfortable with soft skills but did a talk last year at a conference about mentorship and don't want to get pigeon-holed as a soft-skill speaker. I'm toying with two ideas... one softer and one more technical. I have more expertise in the soft skills one, but feel like I should push myself to do something technical. Any opinions on that?
A: I used to be scared to give a technical talk, because of the questions at the end. But then I realized that nobody said I had to take questions on stage! Now I take up all the time for my talk, and ask people to come find me afterwards if they want to discuss more.

Other comments

Other people chimed in as well:

I think the trick for technical talks is to lower your expectations for yourself. A mistake a lot of new people make is to assume they need to know everything about the topic to speak on it. If you've just learned it your insight is often more valuable to your audience, as they're likely very new to your topic as well.

Totally. The best teacher is someone who is slightly ahead of you, coz they remember the pain points.

More?

Our first hangout was jam-packed with information, and we want to host more! What would you like to discuss?

Android Hack Night

I hosted an Android Hack Night at GDG Boulder last night.

Android Hack Night is for everyone who's interested in Android programming. If you...

  • Have no Android experience
    Install Android Studio on your laptop and we'll walk through Hello World together.
  • Just got started but not sure what to do next
    Follow the Udacity course and bring your questions.
  • Tinker with Android as your side project
    Bring your project, share what's working and where the pain points are.
  • Live and breathe Android
    Come help others, or, if you are brave, there will be a coding challenge where you write an app on the spot and present your solution.

We will all be coding, so don't forget your laptops, chargers, Android devices and USB cables!

We had a pretty good turnout, almost 30 people. I asked everybody to introduce themselves and tell us about their Android experience. Since there were quite a few beginners, I gathered everyone who'd like to step through Hello World in a corner, and the rest of the people huddled in groups to work on their apps.


Couldn't get the projector to work so I held up my laptop.

Initially I was going to have every group share what they did at the end, but I lost track of time while helping people with their emulators and devices, so it was a bit of a bummer. But overall it was really fun, and I definitely want to do it again!