Retrieve and launch app shortcuts on Android without being a launcher

Phạm Nguyễn Hoàng
4 min readJan 20, 2024
Google Maps’ app shortcuts

What are app shortcuts?

Introduced in Android 7.1 (API level 25), app shortcuts (not to be confused with the older “launcher shortcuts”) are a handy feature that lets users perform key actions in an app directly from the launcher. Along with them came a set of new APIs for developers to query and launch shortcuts:

  • LauncherApps.getShortcuts,
  • LauncherApps.getShortcutIconDrawable,
  • LauncherApps.startShortcut,
  • etc.

But there’s a catch! As their location suggests, they are only available if the calling app is the curent launcher or voice interaction service, in which case LauncherApp.hasShortcutHostPermission() returns true.

Is there any way for non-launcher apps to interact with shortcuts? That’s what I was wondering when developing App Search — a lightweight utility that lets you quickly search and launch apps. Having the ability to display and open shortcuts would make it even more convenient.

How to retrieve and launch app shortcuts

Checking if an app has shortcuts

Static shortcuts are defined in an XML resource file — let’s call it shortcuts.xml — which is in turn referenced in a <meta-data> element belonging to a launcher activity:

<meta-data 
android:name="android.app.shortcuts"
android:resource="@xml/shortcuts" />

To read this file, you’ll need to use the PackageManager.GET_META_DATA flag while querying activities from PackageManager. The below piece of code queries all launcher activities on the device, then check if each activity has any associated shortcuts:

// Narrows down results to launcher activities
val intent = Intent(Intent.ACTION_MAIN)
intent.addCategory(Intent.CATEGORY_LAUNCHERS)
val resolveInfoList = packageManager.queryIntentActivities(intent, PackageManager.GET_META_DATA)
for (resolveInfo in resolveInfoList) {
// Remember "android.app.shortcuts" from before?
val metadata = resolveInfo.activityInfo.loadXmlMetaData(packageManager, "android.app.shortcuts")
if (metadata != null) {
// Start retrieving shortcuts
}
}

Parsing shortcuts.xml

loadXmlMetaData() returns an XmlResourceParser object, which can be used to parse shortcuts.xml. The file looks similar to this:

<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
<shortcut
android:shortcutId="compose"
android:enabled="true"
android:icon="@drawable/compose_icon"
android:shortcutShortLabel="@string/compose_shortcut_short_label1"
android:shortcutLongLabel="@string/compose_shortcut_long_label1"
android:shortcutDisabledMessage="@string/compose_disabled_message1">
<intent
android:action="android.intent.action.VIEW"
android:targetPackage="com.example.myapplication"
android:targetClass="com.example.myapplication.ComposeActivity" />
<!-- If your shortcut is associated with multiple intents, include them
here. The last intent in the list determines what the user sees when
they launch this shortcut. -->
<categories android:name="android.shortcut.conversation" />
<capability-binding android:key="actions.intent.CREATE_MESSAGE" />
</shortcut>
<!-- Specify more shortcuts here. -->
</shortcuts>

Using XmlPullParser (which XmlResourceParser is a subclass of) is not difficult — just call getEventType(), which returns one in five event states; use get…() methods to extract data; and call next() when done. The official Android documentation for XmlPullParser has a good tutorial on it.

The shortcuts.xmlparsing code is a bit too long to be posted here, as there is more to do than just looping through XML events — for example, keeping track of the current shortcuts, fetching icons and labels, and populating shortcut intents. You can check it out here, or clone the whole demo app.

Some tips:

  • To retrieve string attributes, such as shortcutId, use getAttributeValue.
  • To retrieve resource ID attributes, such as shortcutShortLabel, use getAttributeResourceValue.
  • The two methods above requires passing in an XML namespace, which should be http://schemas.android.com/apk/res/android (the android: namespace).
  • However, some apps (most notably Chrome) do not use namespaces in their shortcuts.xml. If getting an attribute with the android:namespace doesn’t work, try again with null.
  • To get the actual strings and icon drawables, call PackageManager.getResourceForApplication, passing in the package name of the app that you’re getting shortcuts from. This gives you the app’s Resources, which you can call getString or getDrawable on.
  • Beside the targetClass attribute, some Google apps also use the undocumented targetActivity attribute. I had to replace $ (dollar signs) in its value with _ (underscores) for the activity to launch — don’t ask me why!

That’s it! Thanks to this workaround, your app can interact with shortcuts without being a launcher. Here’s my demo app in action:

But…

There are considerable limitations with this method:

  • Some shortcuts point to non-exported activities that can’t be launched by other apps. I was very disappointed to find out that this was the case with quite a few popular apps, such as Chrome or Gmail. Android will throw a SecurityException if you try to launch them.
A SecurityException is thrown when starting unexported activities.
  • Dynamic shortcuts are not declared in the manifest and therefore can’t be retrieved.
  • Lastly, shortcuts were added on Android 7.1, and some of the attributes used for declaring shortcuts were only introduced in that version of Android. They include shortcutId, shortcutShortLabel, shortcutLongLabel, and shortcutDisabledMessage. As a result, on older Android versions, you won’t be able to show a shortcut’s actual ID or label.

Conclusion

There is a workaround for getting another app’s static shortcut when you are not a launcher: parsing the shortcuts.xml file declared in the app’s manifest. However, many shortcuts target non-exported activites, which limits its usability.

In the end, I implemented the ability to show and pin shortcuts in my app. I hope it will prove useful, and I hope you’ve found this article helpful as well.

References

--

--