An Android Love Affair

25 November 2016

The last developer preview of Android 7.1 has started shipping, which means APIs are (based on past experience) more or less stable. There is a very good write up on developer.android.com on how to get started with supporting these new features. I set about trying it out, and here's what happened.

Update SDK version

Let's start with the (supposedly) easy bit -- updating target SDK to 25. It should be as easy as updating your build.gradle file, but just like when the API 24 sources became available, I found that I have to edit the SDK path before Studio starts to recognise the new APIs.

Go to Preferences > Appearance and Behavior > System Settings > Android SDK, click Edit beside Android SDK Location and just keep on clicking Next until you exit the wizard.

Circular Launcher Icons

The next (supposedly) easier bit is having a circular launcher icon. First stop is to generate the new icon. Android Studio has a built-in icon generator, in fact the documentation encourages you to use just that. So launch Asset Studio (CMD+SHIT+A then Asset Studio, or, right click in Project Pane > New > Image Asset).

Choose Image as the Asset Type, then click the three dots to choose your existing icon. Now I know that pointing it to the current icon in mipmap/ or drawable/ should work but it didn' t for me. I had to copy the asset to somewhere else (in my case Desktop) and point the tool to there.

Next choose Circle as the shape and voila, you have your asset. Not.

So it looks like the tool does not trim the icon to be a circle. It tries to, you can see the shape. I filed a bug for it, but I don't think it has been triaged yet.

In the meantime, you can use Roman Nurik's excellent online Asset Studio to generate your icons.

App Shortcuts

One of the biggest features on this version is app shortcuts (please stop calling it force touch, that's not ours).

The Developer site is quite verbose on how to implement app shortcuts. For an overview on what app shortcuts are and the different types (static and dynamic), head on over to the developer site right now and read the intro.

But in a nutshell, and I'm quoting here:

Android 7.1 allows you to define shortcuts to specific actions in your app. These shortcuts can be displayed in a supported launcher, such as the one provided with Nexus and Pixel devices. Shortcuts let your users quickly start common or recommended tasks within your app.

Static shortcuts

Static shortcuts are simple enough. It is so simple in fact, that I doubted myself.

Basically copy-paste stuff from the dev guide, tweak it to use your own app's Intents and it just works. To try it out, I re-used existing PNG icons from our app. But of course, we want to do it right, right?

So first up is to make the icons follow the design guidelines.
• icons should be 24dp x 24 dp
• should be centred in a circle that's 44dp wide
• circle should be in material grey #F5F5F5
• there should be a 2dp padding all around the circle

I needed to convert the icons to vectors, and I found this SVG to vector converter to work best for my purposes. And by best I mean it doesn't mangle the circle, it doesn't lose any of the holes, and is as close as possible to the SVG input.

As always, I tried to define strings in strings.xml. This works well enough for the shortcut labels, but it strangely does not for the shortcutId attribute. And by "does not work" I mean your shortcuts will not appear at all.

Dynamic Shortcuts

App shortcuts can also be dynamic. For my use case, I wanted to expose a few more shortcuts for users who are logged in.

Again, the Developer site has a very good guide on how to get started with including these shortcuts in your app. I highly recommend reading the ShortcutManager Javadoc, as there are more details there than is available in the guide.

A bunch of people have already written about how to implement shortcuts in your app, but I think what most don't mention is that if you need to build up a backstack for the Activity triggered by your shortcut, the docs recommend to use TaskStackBuilder.

This applies to my use case, which is opening the user's Shortlist. In normal circumstances, users get to their shortlist from our main (search) screen. This means that once they are on their shortlist, pressing the back button will take them back to the main screen. We want to replicate this behaviour when they go through the shortcut, and TaskStackBuilder helps us do just that.

Sure you can just manually write out your own Intents[], but remember to set the correct flags for the first Activity in your stack. It's something that's easy to forget, but if we create our Intents via TaskStackBuilder, it will automatically add the correct flags for us.

Reporting Usage

The docs exhort you to report using shortcuts regardless of how the users get there. The API name may be a bit misleading -- reportShortcutUsed -- but think of it as just adding analytics tracking for the screen. So please report usage of both static and dynamic shortcuts!

What's next?

There is so much more stuff to explore with app shortcuts. What happens when the users restore your app from a backup? What if they pinned a shortcut you removed in a new version? How do you re-arrange shortcuts?

I have yet to explore these, so if you have, what are the other quirkiness you encountered?

27 October 2016

Yesterday, my bank pushed a notification asking if I'm going to travel. Yes! I am! I filled up the form they asked me to fill up, and tapped Submit.

And then I got this:

I try again, same error. I try again, the app crashes.

I know some of the devs for this app, so I messaged one of them. As any proper developer does, he asks me "What version are you on?"

I went to the app's settings, and I can't find it there. Hmmm. Opened their navigation drawer. Not there. Where is it?! Apparently, it's nowhere in the app. The dev told me to go to my device's settings, look for the app, and look for the version number.

I thought that was weird. I asked the dev why they don't have it, and he said "I can find you in our crash logs using your user ID. I can find your device, version number, user name. Users do not need the version number."

I have always put in the version number somewhere in all the apps I have worked on ever. Am I doing it wrong? And so I shouted on Twitter:

Hey, devs! Question: Do you have your app's version number visible anywhere inside your app? Why or why not?

A bunch of people replied to me, all with some variation of "Yes". I needed the internet on my side today, thank you for heeding the call, Twitter friends.

I really don't understand why you would not want to put the version number in your app.

It's just one TextView. Use the tiniest, thinnest font you have. You don't even need to think about the code. It's all there, in BuildConfig.

If users care enough about your app to report issues, then that means they are really comfortable using it. Why would you kick them out of their comfort zone just to ask them for this piece of information that should be plastered on the wall? Not all users are as tech savvy as you, 23-year-old developer.

I can just imagine someone calling support:

Customer: The application keeps on stopping.Support: What version of the app are you on?Customer: Where do I find that?Support: Open your device's settings....Customer: What? How?Support: What phone are you using?Customer: I don't know, my children gave this to me.

Remember, going to Settings > Applications may be different for each manufacturer. (I'm looking at you, Samsung). And it may not be as easy as telling someone "Go to device settings".

Having the version number is a good excuse to put in an easter egg. Why pass up this chance?

I guess what I want to make out of this is that something as inconsequential as this for you as a developer may be important to end users, or to other people who help you make your app better. It is bad enough that users encounter bugs so severe they feel like it's worth reporting. And surely we do not want to lose those users because simply by the act of reporting issues, they show us that they care about our product. The least we can do as developers is to make it as easy as possible for them to help us.

I tried re-running pidcat, but it's still not displaying anything. Which probably means we really don't have the latest code off the repo. The --force switch has been deprecated, so to force an update we would have to call reinstall instead:

23 August 2016

Today was the day a thing I worked on was open sourced. It's done. It's out there. My code. For everyone to see. It is like baring my soul to the world. It is baring my soul to the world.

You can't possibly imagine how I feel. I am frightened. I am elated.

Will people find it useful? Will they use it? Will they look at my code and laugh at how stupid I am? Will they finally know for sure that I am a ruse? Will they whinge about how I am Not Doing Things Right?

And you know what? I don't care. I've done my part and did the best I could.

(I lied. I do care. But I promise I'll try to get over it as soon as I can.)

Be afraid. But still do it.

Massive thanks to the Domain Android team for the support, code reviews, suggestions, feedback, and putting up with my baking frenzies; and to Mike for the code contributions, and for helping me through the very arduous publishing process.

14 August 2016

They say all relationships go through a rough patch at two, seven, then ten years. I don't remember who said it, but someone told me that many years ago.

Next week will be my moving-to-Sydney second anniversary, so I figured this is a good time to write this post.
During I/O last May, I met one of the coolest ladies ever, Yasmine. She asked me how I got into Android development, and when I was done telling her she said I should blog about it. So here it is, Yasmine. Better late than never. ;)

In the beginning...

If there's one thing you should know about me, it's that I find it very hard to make decisions. Who's your best friend? What's your favourite food? What should you name your stuffed panda? I don't know the answer to these things. So imagine 16-year-old me, about to graduate high school, and I had Zero Idea what I wanted to major in. The first university I applied to? I wrote down what major I was applying for right in front of the registrar, literally before handing her my application form (Business Economics).

I ended up going to another school, majoring in Electronics and Communications Engineering. I had one computer programming subject in freshman year. And I hated it. I hated it with a passion. I couldn't figure how anything works, and I swore I would never do that again.

My first job after uni was with Intel as a product engineer. I worked there for two years. Lived in the middle of nowhere, worked long hours. But I thought that's par for the course; part of being an adult is working hard, right? And then the semiconductor industry in the Philippines started flipping out. A lot of other factories closed down, some of the products we used to look after were being transferred to other sites. I decided I'd rather look for another job now, than being retrenched and not knowing how long I will be jobless for.

What Now?

I wanted a job back in the city, and I kinda don't want to stay in a sinking industry. But then again, there is nothing else I know how to do. Yeah, I am a licensed engineer, so technically I could work for a telco, or a TV station even! But at that time, if you want to for a telco, you'd have a better chance of getting hired if you interned with them right out of uni. And I didn't, so that's out. There were a lot of job postings for software developers though. But I hated programming! I don't know how to do it!

And this is when my first lucky break came. I am so fortunate that I met a manager who trusted in me. I was upfront with her, I don't know shit. I would have to learn on the job, so it would be a slow start. Needless to say, I learned a lot in that job. I worked on some pretty cool stuff (we made apps installed in SIM cards), and met a lot of really nice people. But more importantly, it kickstarted my foray into software development.

I eventually worked on more enterprise-y stuff (boring). Until the time we ran out of projects. I mean I'm all for coming in puttering around the office doing nothing and getting paid for it. But after two days it turns out it kinda sucks. It was 2009 and I keep on hearing about this new OS from Google called Android and that the SDK is Out Now! and that You Should Try It Out. So I installed all the things and started Android-ing.

Things Get Interesting

So now that I have built a shiny, new Hello World app that runs on an emulator, I took that as a sign that I have the creds to apply for an Android development job. I joined a start up, and again, I was upfront about it -- I don't know how to do this, I have just been playing around, but if you want to pay me to play around, then we can be friends. And so I was met with another lucky break.

It was an encouraging time to be a dev. The Android Dev StackOverflow community was much smaller, we are all learning at the same time, and honestly, I think everyone was kinder and more forgiving+.

I eventually worked for a company whose mobile team is distributed across offices in Manila, Sydney, and New York. I was the first Android developer in the Manila office, but by then I was so used to it that I didn't mind.

It was there that I met the guy who would eventually refer me to Domain, and for that I am forever grateful to him. Domain has done so much for me, both personally and professionally. I work with a really talented team, and I have never seen a company love a product so much. Domain made my dream of attending IO a reality, and through my work with them I got to work on a lot of pretty sweet features that I never dreamed of++. Another lucky break, and I mean to make the most out of it.

And?

I guess what I mean to say is that I have just been winging it all these years. But at least I'm honest about it, right? It there's anything I have learned, it is that there is nothing wrong with saying "I don't know". There are times when we need to pretend to know things, but there are a lot more times when we need to accept that we do not know things.

Do not be afraid to try something new, no matter how scared it makes you feel. Easier said than done, I know. But sometimes it really helps to take a deep breath, close your eyes, and just jump+++. Lundagin mo, baby!

+ I was looking at my old StackOverflow questions, and I seriously think that if I asked them today, I would have a lot of "What are you, stupid?" comments. Or maybe I'm just old and cynical. I don't know. The point is, we have all been there, so be kind to another, okay?++ This merits a post all on its own+++ I distinctly remember that's how I applied to my first Android job. I wrote my cover letter, proof read it, hovered my mouse over the Send button, took a deep breath, then clicked it before I could change my mind.

04 August 2016

One of the things we are taught in Android is that we should gracefully handle different layouts based on screen sizes. With more and more things being not just screen size-specific but also OS version-specific, this is one thing I think a lot more devs need to pay attention to. Today was my turn to do just that.

I wanted to create a dialog with an image that resizes itself while maintaining aspect ratio but being constrained by the container. Okay.

The image does not fit, you cannot see "NOT NOW". It doesn't work. Then I tried it on a tablet:

OMG how could it possibly get worse. There is so much space and it's not filling it up properly! I am pretty sure the root layout has layout_height="wrap_content". Weird.

After some furious Googling, I found out that I have to set the LayoutParams on the dialog after it has been shown. Why do I have to do that? I said wrap it and you refuse and now you want me to do it myself? Why?

I gave in and did the thing that StackOverflow tells me to. But it didn't work. It still looked the same. I tried moving stuff around; calling setLayout instead of setAttributes. It still looked the same. :sad_panda:

And so the time has come to look for another approach. A couple of things need to happen:
a. We shouldn't make the user scroll just to click the buttons on a landscape phone
b. Make the image take up more space on tablets

To make (a) happen, we either: create a new layout for this orientation or just set the image to be invisible. The second option is more appealing for me. There really is not much difference in the two orientations aside from the image after all. We can set the visibility of the image at runtime, but then maybe we can afford to show the image if there is enough space for it.

This means that to make (b) happen, maybe we need to tweak our layout a little. And this is where the beauty of Android's alternative resources come in. To be as less repetitive as possible, I chose to harness styles too, since I only need to override one attribute -- the visibility.

First I created a style in the default /res/values folder that I would apply to the image:

Running the sample though, you'll see that the colour is not set and a dark grey-ish lilac-ish colour is used instead:

Unacceptable indeed!

So how do we make the Earl of Lemongrab happy? There are two ways of solving this problem.

A. Use a BindingAdapter

A BindingAdapter lets you manipulate and do a bit more involved logic on your data before applying it to the View. To use a BindingAdapter, first create a static method in your code that is bound to either a standard Android attribute or a custom one.

Do not forget to add the app namespace! Android Studio can add this for you. Just type in appNs and it will autocomplete.

This solution works, but is a bit too involved. And you said data binding is easy!!!

Well it kinda is, because, it turns out that we can:B. Set the value in XML

But it didn't work! Well it turns out the problem is because we are passing a resource ID into an attribute that expects a resolved color value. So the only thing we need to do is to move all that code in our BindingAdapter into XML.

Very convenient! And it works! Note that you do not have to import anything to use context, there is some data binding magic going on that automatically injects it for you if you call it. Think of it as The Beckoning of the Context.

The Earl is now happy.

PS: If you need to set a drawable from a resource ID as an ImageView src, I found that you would also need to do this trick, aka: