Shortcuts to Shortcuts

08 Jun 2019 | tags: android

It has been a few years since I last looked at implementing app shortcuts, and lately I have been looking at them again. I remember implementing them the first time they were released for Android N, but as with life, things have changed a bit.

In this post I want to share some interesting learnings I had whilst implementing static app shortcuts. (Note: This post assumes the reader is familiar with the basic requirements of implementing app shortcuts). As always, all the code for this post is in my Sandbox App.

Android Studio support is… limited.

:point_up: For this exercise, I am using Android Studio 3.5 Beta 4.

And by limited I mean resource references do not autocomplete at all. This becomes tricky as some of the required attributes can use resource references. The table below shows what those are:

Allowed Not allowed
shortcutShortLabel shortcutId
shortcutLongLabel Intent attributes (targetPackage, targetClass)
icon  

:warning: My non-scientific test shows that on Pixel launchers, shortcutShortLabel is used unless shortcutLongLabel is 17 characters or less.

There are changes in design guidelines between APIs 25 and 26

Android O introduced adaptive icons and if we take into consideration that users can pin shortcuts onto their homescreens, we need to support this format if we want to provide the best possible user experience.

Nick Butcher talked about this in detail in his post on implementing adaptive icons.

:warning: Using a VectorDrawable for the icon foreground as Nick showed, it didn’t seem to like using theme references for the fill colour. Using a resource reference is okay.

Read this post to learn more about designing adaptive icons, and this document for designing app shortcut icons.

Mo’ package names, mo’ files

For each app shortcut we want to support, there needs to be an Intent associated with it; with each Intent needing a targetPackage and a targetClass. To wit:

<intent
    android:action="android.intent.action.VIEW"
    android:targetPackage="com.zdominguez.sdksandbox"
    android:targetClass="com.zdominguez.sdksandbox.MainActivity" />

This might present a problem if your app have different package names for each build type. Someone has written a function to support variables but unfortunately it does not work on more modern versions of the Gradle Plugin.

This means that we would need multiple shortcuts.xml files for all build variants that we want to support:

Passing intent extras is possible, but sometimes a trampoline activity comes in handy

If we need to pass some extras to the Activity we are targetting, we can do so via the XML file:

<intent ...>
    <extra android:name="target_tab" android:value="settings" />
</intent>

One important statement that is a bit buried in the documentation for app shortcuts is:

[W]hen the app is already running, all the existing activities in your app are destroyed when a static shortcut is launched.

This is when a trampoline Activity becomes useful so we can prepare our back stack or even get a handle on some requirements the receiving activity would need (for example, a value to be read from the database). You can read more about trampoline activities here. :curly_loop:

Handling up navigation

Similar to how we should handle navigation when starting an Activity from a notification, we need to consider how the app would behave when the user navigates away from our target Activity.

Thus, we need to provide multiple Intents to the shortcut and the last one in the list would be what the user initially sees.

<shortcut ...>
    <intent
        android:action="android.intent.action.VIEW"
        android:targetPackage="com.zdominguez.sdksandbox"
        android:targetClass="com.zdominguez.sdksandbox.MainActivity">
        <extra android:name="target_tab" android:value="settings" />
    </intent>
    <intent
        android:action="android.intent.action.VIEW"
        android:targetPackage="com.zdominguez.sdksandbox"
        android:targetClass="com.zdominguez.sdksandbox.DemoActivity">
    </intent>
</shortcut>

It took me a bit longer than I liked to figure out all of this :weary:, so I’m writing it down to have a reference for future me.

:dancers: Make sure to read the rest of the best practices document to learn more.


Moving On Up (Or Down)

03 Apr 2019 | tags: android

I tweeted that this morning after rediscovering CMD+SHIFT+UP. I have known about this shortcut for a while, but I guess I haven’t had reason to use it until lately.

And then I remembered a variation of this shortcut that would literally “move things don’t care”.

This being IntelliJ, these actions work on more than just properties. They work for basically anything in your code that has a scope – individual lines, functions, classes even!

There are two main movement actions, move statement and move line. Depending on what you want to do, each of these have their own benefits.

Moving a statement means that the IDE will respect the context within which that particular element exists. In the case of a single line of code, this means maintaining its current scope. What this means exactly is – if a line is inside a function, using CMD+SHIFT+UP/DOWN will never move it out of the function.

To wit:


But this line wants to move to another function! It says so itself! This is when CMD+ALT+UP/DOWN comes in handy:


This shortcut is really handy when reordering or refactoring functions:


I leave it as an exercise for the reader to see how the shortcut behaves with classes. Get moving and stop copy-pasting! :dancers:


Bug Reports: A Story

20 Feb 2019 | tags: android

“This tool sucks :facepunch::rage::rage::rage::rage:

“Ugh WTF this is so stupid :poop::fire::fire::fire:

These are just some of the things I have heard developers say over the years whilst working on Android. Sometimes they are trying to figure something out and there is not enough documentation, other times they are trying to make their tools do something and it just won’t work. Oftentimes, it is because they are expecting something to happen and it does not.

I know it can be frustrating, I have been a dev for more than a 12 years now after all. But then again, no matter how much or how loudly you curse the Android gods, where does that get you? Does it fix the issue? Does it improve the situation? I do not think so.

So what should we do then?

My answer has, and always will be, “have you filed a bug for it?”

Android has its own dedicated issue tracker organised into several components. This page enumerates those components and have some very good tips on how and when to file a bug.

It is important to select the correct component because (1) it makes the relevant team aware of your issue, and (2) each component may have a template for reporting issues.


Android Studio component with template

When writing up a bug report, think of what you, as a developer, would find useful if it were you receiving the issue. “This app sucks” is way less helpful than “I have trouble logging in with my credentials that work on my computer”.

I try to be as helpful to the developer as much as I can because we both have the same end-goal: to fix a verified issue.

It might be time-consuming, but when the bug you reported gets fixed and other devs start to notice and benefit from it, it is very rewarding. :green_heart:

I might be totally wrong but I’d like to think my bug report helped nudge this along :sweat_smile:


Sometimes, my feature requests actually get built too! :dancer:

Do you want to get your issues fixed too?

The trick is to make the bug report as great as possible. Let’s take this bug for example. I encountered an issue whilst running “Remove Unused Resources” from Android Studio and wanted to report it.

To gather the relevant information about my machine, I opened Android Studio and then chose “About Android Studio”. I then clicked on the copy icon in the dialogue box that appeared; that copies into my clipboard almost all the information asked for by the template!


Click that!

I then provided a short overview of the reproduction steps; I try to follow the given-when-then pattern. Set the scene first to give the developers some context of what we are trying to achieve. In this case, I have provided a zip file of sample project that reproduces the error.

Next, I reported what I was trying to do and what happened as a result. If there are multiple steps that need to happen first, I enumerate them all in order. In this bug, I have also provided a screenshot so that the team can quickly verify that they have reproduced my error exactly as I see it.

And finally, I tried to provide as much as detail as possible. Were there scenarios where this does not happen?

It looks like Studio detects layouts inflated through the generated binding file’s .inflate() method as unused. Views inflated using DataBindingUtil are not removed. (From here)

If it is a feature request, what would I like to see as the end result? What are the benefits of fixing this issue?

If I am beginning data binding, “user defined types” might be unfamiliar. (From here)

Are there any workarounds that I am aware of?

Found another way to fix it. Remove the spaces before and after the arrow, as such: android:onClick="@{()->handlers.onSendNotificationClick()}" (From here)

When I found a use case that was missed by the initial fix, I provided another sample project.

Really, a sample project is like a picture. It paints a thousand words.

EDIT (20190221): I cannot believe I need to say this but I will say it anyway: BE NICE. No one wants to talk to a jerk.

A good bug report is like a good story.

It presents the characters, introduces conflict, and finally, a denouement.

Be a good storyteller and hold your audience captive. After all, it’s the stories that make this world much more fun and interesting. :relaxed: