Shortcuts to Shortcuts

08 Jun 2019

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.