App Shortcuts

Your app probably has a single activity that appears in the user’s home screen launcher. It is the activity that has the <intent-filter> for the MAIN action and the LAUNCHER category.

For years, many home screens for Android have allowed the user to make “shortcuts” to that activity, typically by long-pressing the icon in the launcher, then dragging it to the desired spot on the home screen. This is reminiscent of similar capabilities in many desktop operating systems.

However, some desktops have gone beyond that. For example, with the Unity desktop in Linux, right-clicking a launcher icon in the Unity dock may bring up specific ways to get into the app identified by that icon. For example, an email client might offer “Compose New Message” from the icon’s context menu, so whereas a simple click on the icon would bring up the inbox, right-clicking and choosing “Compose New Message” would bring up a message composer.

Android 7.1 adds the awkwardly-named “app shortcuts” to mimic this sort of feature. There are two ways of adding these shortcuts: via a resource tied into the manifest, and via Java code. The former approach has no particular ties to Android 7.1, and third-party home screen implementations are already adopting it.

In this chapter, we will explore what app shortcuts are, how to add them to the manifest, and how to offer “dynamic” app shortcuts from Java.

Prerequisites

Understanding this chapter requires that you have read the chapter on Intent filters.

Enabling Deep Dives

Google has been steadily increasing the ways in which users can drive directly into specific portions of your app, as opposed to always getting into it via a home screen launcher or perhaps the overview screen, such as:

All of these are designed to make it a bit easier for power users to get where they want to go quickly, saving some taps, swipes, or other forms of input.

The app shortcuts added by Android 7.1 work much the same way.

App Shortcuts, from the User’s POV

It will help to understand what you are supposed to be adding to your app if you see what the user experience is for apps with app shortcuts.

However, technically, a home screen can do whatever it wants with app shortcuts, from a presentation standpoint. So, let’s focus on the Pixel Launcher first, which is the launcher that Google shipped with their 2016 Pixel phones and offers app shortcut support. The Android 7.1 emulator has a similar launcher.

Ad-Hoc Requests

The user can long-press on an app icon and pull up a list of available app shortcuts:

App Shortcuts for Settings App
Figure 889: App Shortcuts for Settings App

If the icon does not support app shortcuts, the long-press simply does whatever it ordinarily would have done prior to app shortcuts. For example, long-pressing an icon in the launcher would allow the user to drag it to be a shortcut on the main home screen. To do those sorts of things with an icon that does support app shortcuts, you not only need to long-press but also start dragging the icon somewhere.

Pinning

Each of those app shortcuts has a small “grab handle” (looks like =). The user can drag that and use it to create a shortcut on the home screen for that particular app shortcut:

Pinned Battery App Shortcut from Settings App
Figure 890: Pinned Battery App Shortcut from Settings App

Tapping that icon directly launches whatever the app shortcut has specified.

Alternatives

However, developers are limited only by their imaginations in terms of presentation of app shortcuts. Home screens have easy access to app shortcut information via the LauncherApps utility class, and there is little stopping other apps from doing the same. So, you can imagine:

And so on.

Offering Manifest App Shortcuts

The “low-hanging fruit” of app shortcuts is to offer some static options via the manifest. This takes very little time to implement, including no mandatory Java code changes. Furthermore, while the Android 7.1 APIs for working with app shortcuts may not exist on older devices, home screens and other apps could still support manifest app shortcuts with a bit of additional code. Hence, the app shortcuts that you offer via the manifest will become available to the users of many popular alternative home screen implementations, in addition to users of Android 7.1+ devices.

The AppShortcuts/WeakBrowser sample project demonstrates the use of both manifest and dynamic app shortcuts. This app implements a silly little Web browser, allowing the user to visit a handful of hard-coded sites.

Identify the Destinations

First, you need to decide where these app shortcuts should send users.

From a navigation flow standpoint, an app shortcut:

For example, suppose that a common bit of existing navigation in your app is:

Adding an app shortcut to that same destination is easy but not that useful, as it will be no faster — and perhaps slower — to activate the app shortcut than it would to be to just go into the activity and tap on the desired tab.

But, the navigation might be more complex, where getting to the destination:

Now offering rapid access to the destination via an app shortcut may be useful, as it may be faster than the ordinary navigation options.

Of course, WeakBrowser, being weak, has one manifest app shortcut: to allow the user to visit a search engine. This same page is available by tapping a “search” action bar item. This is not an especially effective use of manifest app shortcuts, but it helps to simplify the example.

Ensure the Destination is “Evergreen”

App shortcuts offered via the manifest are static. You cannot modify them at runtime, the way that you can with dynamic app shortcuts. Hence, they cannot really be personalized.

Also, if the user pins one of these app shortcuts, and some future version of your app eliminates the app shortcut, the pinned app shortcut may remain on the user’s home screen. Tapping it would display some sort of “you cannot do this anymore” message. From a user experience standpoint, this will not be popular.

So, try to have your manifest app shortcuts be “evergreen”, ones that are unlikely to need to be changed or removed in the future.

Add Entry Points for Destination in Manifest

An app shortcut triggers a call to startActivity() on some Intent. With manifest app shortcuts, you describe the Intent in XML, and some other process creates that Intent and passes it to startActivity().

This means that any destination that you want to offer needs to be able to be reached by some startActivity() call, with an Intent that can be built out of some combination of the following:

Notably, it appears that you cannot use extras or categories to distinguish this Intent from any other that starts up the same activity.

Also, this activity will need to be exported, as third-party apps will need to be able to start up the activity. If the activity has an <intent-filter>, it will be exported. Otherwise, you will need to add android:exported="true" to the <activity> in the manifest.

Write the XML

The manifest app shortcuts are defined via an XML resource, usually residing in res/xml/ within your module’s main/ sourceset. This will contain a root <shortcuts> element, which itself contains one or more <shortcut> elements:

<?xml version="1.0" encoding="utf-8"?>
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
  <shortcut
    android:icon="@drawable/ic_search_black_24dp"
    android:shortcutId="search"
    android:shortcutLongLabel="@string/search_long_desc"
    android:shortcutShortLabel="@string/search">
    <intent
      android:action="android.intent.action.SEARCH"
      android:targetClass="com.commonsware.android.appshortcuts.MainActivity"
      android:targetPackage="com.commonsware.android.appshortcuts" />
  </shortcut>
</shortcuts>

(from AppShortcuts/WeakBrowser/app/src/main/res/xml/shortcuts.xml)

Here, we have a single app shortcut. The required attributes are:

android:shortcutLongLabel is optional. In theory, it will be used in places where a longer description of the app shortcut may be useful. In practice, it is unclear where this would be used.

The other required piece of a shortcut definition is the nested <intent> element. This describes what Intent should be used with startActivity() to take the user to where this app shortcut advertises as its destination. Typically, you will use the three attributes shown in the above sample:

A shortcut can have several <intent> elements, which will cause Android to create a fake back stack for the user (i.e., pressing BACK from the last <intent> will take the user to whatever activity was identified in the preceding <intent>). And, a <shortcuts> element can have one or several <shortcut> elements. However, bear in mind that a launcher may not use many app shortcuts — for example, the Pixel Launcher seems to cap the presentation at three app shortcuts. These results will vary by launcher (or other app shortcuts client) but you should assume that you only have so many app shortcut “slots” to display to the user.

Add to the Manifest

Then, you need to add a <meta-data> element to your <activity> element for your launcher activity in the manifest, pointing Android to your XML resource:

    <activity android:name=".MainActivity">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
      <meta-data
        android:name="android.app.shortcuts"
        android:resource="@xml/shortcuts" />
    </activity>

(from AppShortcuts/WeakBrowser/app/src/main/AndroidManifest.xml)

Only app shortcuts declared on launcher activities will be honored. If you try putting this <meta-data> element on other activities, it will be ignored. If your app is one of the few with multiple launcher icons, each could have its own app shortcuts. Or, you might take this opportunity to consolidate those launcher icons into a single one, with the secondary launcher icons turning into app shortcuts.

The fact that this is just a <meta-data> element and an XML resource is why existing home screens could adopt manifest app shortcuts. All of this information is available via PackageManager, going back to the earliest Android versions.

Results

If you install this app on a device with a compatible home screen implementation, the manifest app shortcut should be available, such as what you get on the Pixel:

Manifest App Shortcut for WeakBrowser
Figure 891: Manifest App Shortcut for WeakBrowser

The biggest problem comes with the icons. There are no instructions at all as what these icons should look like, or what size they should be. The author’s assumption is that they should be launcher-style icons is made in part by the behavior of when you use other types of icons, such as the simple action bar-style search icon:

Pinned Manifest App Shortcut for WeakBrowser
Figure 892: Pinned Manifest App Shortcut for WeakBrowser

Here, the app shortcut was pinned to the home screen, and the icon looks… unpleasant.

Also note that while the Pixel Launcher superimposes your app’s icon over the app shortcut icon, it is unclear if that is something that is required by the framework or merely a Pixel Launcher convention.

Disabling Manifest App Shortcuts

So, you ship an update to your app, where you declare some manifest app shortcuts. Some time later, you revamp the UI of your app, and one of those app shortcuts no longer makes sense. It might not even work anymore — for example, you might have removed support for the activity that the app shortcut pointed to.

You might think that whatever shortcut XML you use in the new app version is what the device will use, once the user upgrades to the new app version. That is true, with one noteworthy exception: pinned app shortcuts. Google does not want these to vanish into thin air based on an app update, as that might confuse the user.

You have two main options for how to handle this gracefully:

  1. Your revised shortcut XML might repurpose the existing app shortcut. Have a <shortcut> with the same android:shortcutId attribute, but give it whatever icon, labels, and <intent> are appropriate. Users who upgrade your app will have their pinned shortcut updated to reflect the new settings. This works well in cases where the app shortcut has changed a bit but still closely resembles its original role.
  2. Your revised shortcut XML might disable the existing app shortcut. This would happen if your shortcut XML lacked any <shortcut> for the old ID. Preferably, though, you have a <shortcut> for the to-be-disabled ID. On that element, you can have android:enabled="false" to indicate that the app shortcut is now disabled, and you can have android:shortcutDisabledMessage pointing to a string resource where you explain why that app shortcut has been disabled. If the user taps on the app shortcut, this message should appear.

Offering Dynamic App Shortcuts

Truly personalized app shortcuts usually cannot be specified in the manifest. For example, you may want to allow the user to have an app shortcut to their favorite “friend” in your social network client. The identity of that friend varies by user and time. While you could offer a manifest-registered app shortcut for “Favorite Friend”, the user will not know necessarily who that friend is. With dynamic shortcuts, you can craft one that uses the name and avatar of that specific friend.

Offering dynamic app shortcuts is more powerful and correspondingly more complex.

Our WeakBrowser sample app, on initial install, only has the one manifest app shortcut. However, the user can visit a Settings activity within the app and elect to enable “bookmarks”:

WeakBrowser Settings Activity
Figure 893: WeakBrowser Settings Activity

The user can choose which bookmarks to use from a multi-selection preference:

WeakBrowser Settings Activity, Showing Bookmarks
Figure 894: WeakBrowser Settings Activity, Showing Bookmarks

If the user checks some bookmarks, they get added as dynamic app shortcuts, to go along with the existing manifest app shortcut:

WeakBrowser Manifest and Dynamic App Shortcuts
Figure 895: WeakBrowser Manifest and Dynamic App Shortcuts

Grok the Adjectives

An app shortcut is “pinned” if, by one means or another, the user has indicated that they want long-term direct access to whatever that app shortcut represents. In the Pixel Launcher, an app shortcut is pinned if the user grabs the grab handle and drags it as a shortcut onto the home screens. Different home screens will have different visual metaphors for “pinned”.

An app shortcut is “immutable” if it cannot be changed by the app that provided (“published”) the app shortcut. Manifest app shortcuts are immutable. Conversely, an app shortcut is “mutable” if its contents can be changed. Dynamic app shortcuts are mutable.

Ponder the IDs

Each app shortcut has a unique ID. For manifest app shortcuts, that is set via the android:shortcutId attribute in the <shortcut> element. Dynamic app shortcuts have an equivalent means of establishing their ID.

As you manipulate dynamic app shortcuts, what happens depends upon:

Identify the Destinations

As with manifest app shortcuts, you need to know where you are going to send the user within your app when the user chooses one of your dynamic app shortcuts. However, in this case, you will be able to provide a full Intent associated with the app shortcut. In principle, you could use things like extras, whereas that is not documented to be supported for manifest app shortcuts.

Craft the Intent

As with manifest app shortcuts, the destination for your dynamic app shortcuts needs to be identifiable by an Intent that will be used with startActivity() to take the user to that destination. However, unlike with manifest app shortcuts, you have full control over the setup of the Intent that is used for dynamic app shortcuts.

In particular, you may want to consider what Intent flags to use. A manifest app shortcut will have FLAG_ACTIVITY_NEW_TASK and FLAG_ACTIVITY_CLEAR_TASK added to the Intent constructed from the shortcut XML. This will send the user to your destination, wiping out the back stack from that task. You might elect to use other flags, or control things using <activity> manifest attributes like android:taskAffinity, to get the flow that you want.

For the bookmarks, our sample app has a model class, named Bookmark, much to nobody’s surprise:

package com.commonsware.android.appshortcuts;

import java.util.HashMap;

class Bookmark implements Comparable<Bookmark> {
  static final HashMap<String, Bookmark> MODEL=new HashMap<>();
  final String url;
  final String title;
  final String id;

  static {
    add(new Bookmark("Android Developer Home",
        "https://developer.android.com",
        "687a9ea6-f0c0-448c-9cc9-a4aa6e10a1af"));
    add(new Bookmark("Android Open Source Project",
      "https://source.android.com",
      "0ee37e25-2dac-4602-8aa2-3709ac4037c8"));
    add(new Bookmark("AOSP Source Search",
      "http://xref.opersys.com/",
      "405ba533-337e-40be-abe0-fb86cd04bf7d"));
    add(new Bookmark("Stack Overflow Android Questions",
      "https://stackoverflow.com/questions/tagged/android",
      "c9599794-cb9f-46a1-ad61-971ff2a8a172"));
    add(new Bookmark("The CommonsBlog",
      "https://commonsware.com/blog/",
      "948fe25a-44d4-49d0-a23f-2783f786040d"));
    add(new Bookmark("CWAC Community",
      "https://community.commonsware.com/c/cwac",
      "4c7fac0f-fc86-4c68-8ad8-99198fc3d433"));
  }

  private static void add(Bookmark b) {
    MODEL.put(b.id, b);
  }

  Bookmark(String title, String url, String id) {
    this.url=url;
    this.title=title;
    this.id=id;
  }

  @Override
  public int compareTo(Bookmark bookmark) {
    return(title.compareTo(bookmark.title));
  }
}

(from AppShortcuts/WeakBrowser/app/src/main/java/com/commonsware/android/appshortcuts/Bookmark.java)

Here, to keep the example simple, the “database” of bookmarks is merely hardcoded roster, stored in a HashMap, keyed by a UUID serving as a unique identifier. In addition to its id, each Bookmark has a title and a url.

When it comes time to build an Intent for a given Bookmark, we use that url as the “data” facet of the Intent, to deliver it to our MainActivity:

  private Intent buildIntent(Bookmark item) {
    return(new Intent(getActivity(), MainActivity.class)
      .setAction("i.can.haz.reason.why.this.is.REQUIRED")
      .setData(Uri.parse(item.url)))
      .putExtra(MainActivity.EXTRA_BOOKMARK_ID, item.id)
      .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK);
  }

(from AppShortcuts/WeakBrowser/app/src/main/java/com/commonsware/android/appshortcuts/SettingsFragment.java)

We also:

Define the Shortcuts

Android 7.1’s SDK offers a ShortcutInfo.Builder, which lets you create ShortcutInfo objects, each of which represents one dynamic app shortcut.

Given a Set of Bookmark IDs, we can craft the corresponding ShortcutInfo objects via builders:

  private List<ShortcutInfo> buildShortcuts(Set<String> ids) {
    List<Bookmark> items=new ArrayList<>();

    for (String id: ids) {
      items.add(Bookmark.MODEL.get(id));
    }

    if (items.size()>0) Collections.sort(items);

    List<ShortcutInfo> shortcuts=new ArrayList<>();

    for (Bookmark item : items) {
      shortcuts.add(new ShortcutInfo.Builder(getActivity(), item.id)
        .setShortLabel(item.title)
        .setIcon(buildIcon(item))
        .setIntent(buildIntent(item))
        .build());
    }

    return(shortcuts);
  }

(from AppShortcuts/WeakBrowser/app/src/main/java/com/commonsware/android/appshortcuts/SettingsFragment.java)

The ShortcutInfo.Builder constructor takes a Context for resource resolution, plus a unique ID of the app shortcut. In our case, we just use the unique ID of the Bookmark, since a Bookmark corresponds 1:1 with our dynamic app shortcuts.

The builder methods that we use here mirror the XML that we used in the manifest app widget:

Manifest App Widget XML Builder Method
android:shortcutShortLabel setShortLabel()
android:icon setIcon()
<intent> setIntent()

Our setIntent() call uses the buildIntent() method shown in the preceding section. In theory, our corresponding buildIcon() method would craft an icon for each bookmark, perhaps using the favicon of the site. Here, we just use a simple resource image, the same one for each bookmark:

  private Icon buildIcon(Bookmark item) {
    return(Icon.createWithResource(getActivity(),
      R.drawable.ic_bookmark_border_black_24dp));
  }

(from AppShortcuts/WeakBrowser/app/src/main/java/com/commonsware/android/appshortcuts/SettingsFragment.java)

This buildShortcuts() method simply creates the ShortcutInfo objects. To apply them, we need to get our hands on a ShortcutManager, via getSystemService():

    shortcuts=getActivity().getSystemService(ShortcutManager.class);

(from AppShortcuts/WeakBrowser/app/src/main/java/com/commonsware/android/appshortcuts/SettingsFragment.java)

Then, we can call setDynamicShortcuts() on the ShortcutManager, supplying our list of ShortcutInfo objects, to specify that this list of ShortcutInfo objects represents the current roster of dynamic app shortcuts to offer to the user:

  private void showBookmarks() {
    updateBookmarks(bookmarks.getValues());
  }

  private void updateBookmarks(Set<String> ids) {
    shortcuts.setDynamicShortcuts(buildShortcuts(ids));
  }

(from AppShortcuts/WeakBrowser/app/src/main/java/com/commonsware/android/appshortcuts/SettingsFragment.java)

All of this code is appearing in a SettingsFragment that shows the SwitchPreference and MultiSelectListPreference for manipulating the bookmarks. showBookmarks() is called if the user toggles on the SwitchPreference, and updateBookmarks() is called when the user changes which items are checked in the MultiSelectListPreference (held in the bookmarks field).

Remove the Shortcuts

It is possible that you will want to remove some existing dynamic app shortcuts. In the case of the sample app, there are two possibilities:

  1. The user changes the mix of bookmarks to be something other than it was before, including perhaps unchecking some previously-checked bookmarks
  2. The user turns off the SwitchPreference, meaning that no dynamic app shortcuts should be offered

Scenario #1 is handled just by calling setDynamicShortcuts() on the ShortcutManager, as this removes any existing app shortcuts.

Scenario #2 is handled by removeAllDynamicShortcuts() on the ShortcutManager, which does pretty much what the method name suggests and removes all dynamic app shortcuts:

  private void hideBookmarks() {
    shortcuts.removeAllDynamicShortcuts();
  }

(from AppShortcuts/WeakBrowser/app/src/main/java/com/commonsware/android/appshortcuts/SettingsFragment.java)

Another option is the removeShortcuts() method on the ShortcutManager. This takes a List of ID values and removes those app shortcuts.

However, bear in mind that:

What these methods are doing is changing the roster of dynamic app shortcuts that are available to the user from the launcher icon. They do not affect any existing pinned app shortcuts.

Grok the Other Verbs

ShortcutManager has a handful of other methods that allow you to manipulate the roster of dynamic app shortcuts that your app produces.

addDynamicShortcuts() will update any dynamic app shortcuts with the same IDs as the ones that you supply, and any new dynamic app shortcuts will be added. Where setDynamicShortcuts() says “replace the existing roster with this one”, addDynamicShortcuts() says “update or augment the existing roster with these, and leave everything else alone”.

updateShortcuts() is like addDynamicShortcuts(), except that it will only update existing dynamic app shortcuts, not add new ones.

reportShortcutUsed() should be called, with the ID of a shortcut, whenever that app shortcut gets used by the app. In theory, this information might help Android optimize the presentation of app shortcuts to the user, though it is unclear if this is being used at the moment. This is why we put the bookmark ID in the EXTRA_BOOKMARK_ID extra: so MainActivity can report the usage of this app shortcut.

      if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.N_MR1) {
        String id=i.getStringExtra(EXTRA_BOOKMARK_ID);

        if (id!=null) {
          getSystemService(ShortcutManager.class)
            .reportShortcutUsed(id);
        }
      }

(from AppShortcuts/WeakBrowser/app/src/main/java/com/commonsware/android/appshortcuts/MainActivity.java)

(here, i is the Intent used to display this activity)

disableShortcuts() — where you supply it with a list of dynamic app shortcut IDs — allows you to stop pinned dynamic app shortcuts from working. While setDynamicShortcuts(), removeDynamicShortcuts(), and removeAllDynamicShortcuts() affect the roster of available dynamic app shortcuts, they do not affect any pinned dynamic app shortcuts. Those will still work. If the reason why you are removing some dynamic app shortcuts is that the user is no longer eligible for those things (e.g., the user failed to renew a subscription), disableShortcuts() allows you to block those dynamic app shortcuts from working. The user will be shown a message instead of having the pinned dynamic app shortcut launch an activity, and you can tailor that message if desired.

Contemplate Update vs. Replace

setDynamicShortcuts(), updateShortcuts(), and addDynamicShortcuts() all do the same thing if there is an existing dynamic app shortcut with the same ID: update its contents to reflect whatever you passed in to those methods. This includes “updating” it to have the same information as it already has, if you have not changed anything. These not only update the roster that will be shown to the user, but they also update any pinned editions of those dynamic app shortcuts.

This introduces a potential area of confusion for the user.

For example, a Web browser that is more sophisticated than is WeakBrowser could keep track of which sites the user visits most often. Then, the browser could offer a dynamic app shortcut to visit that specific site. Let’s pretend for a moment that, at some point in time, the user’s most-visited site is the CommonsWare site. The browser would have a dynamic app shortcut for “CommonsWare”, which the user could pin.

Some time later, as the user continues using the browser, the browser realizes that some new site has supplanted the CommonsWare site as the one that the user has visited the most. So, the browser will want its dynamic app shortcut roster to reflect this change.

There are two ways of going about this:

  1. The browser could reuse the existing app shortcut ID (e.g., mostPopular) and change its label and Intent to reflect the new most-popular site. However, this will not only change what the user sees when looking at the available app shortcuts, but it also changes the pinned app shortcut. Now that home screen shortcut would bring up some other site, while the user had pinned the CommonsWare site.
  2. The browser could use app shortcut IDs that are unique not for the role, but for the site (e.g., use the actual URL). If the browser uses setDynamicShortcuts() now, the list of shortcuts would contain one with the new app shortcut ID and would not contain one with the old app shortcut ID. The list of dynamic app shortcuts that the user sees will reflect this change. But, since the browser did not change any data with the old app shortcut ID, the pinned one remains as it was, still pointing to the CommonsWare site.

If this sort of thing sounds like it might be plausible for your planned use of dynamic app shortcuts, you probably want to consider the app shortcut ID to be tied to the content (e.g., the site URL), not the role the content is being applied to (e.g., the most-popular site URL).

Get the Existing Shortcuts

There are three getter methods that allow you to find out what app shortcuts are outstanding and are related to your app:

The latter, as the name suggests, lets you know which app shortcuts (manifest or dynamic) have been pinned by the user.

Note that the ShortcutInfo objects that you get back from these methods may not have all of their details filled in (e.g., may be missing the icon). Mostly, you will be looking for the shortcut IDs, so you can make determinations of how to manipulate the app shortcut roster (e.g., do you need to disable anything?).

Deal with Reality

Unfortunately, there are some aspects of dynamic app shortcuts which will require additional work.

If your labels for the dynamic app shortcuts have translations for other languages, you need to replace the dynamic app shortcuts when the user switches the device to a different locale. For example, you could have a manifest-registered BroadcastReceiver, listening for the ACTION_LOCALE_CHANGED system broadcast. There, you could call setDynamicShortcuts() or updateShortcuts() to reflect the new labels.

However, outside of locale changes, you need to be careful about updating dynamic app shortcut information when your app is in the background. There is a “rate limit” established that will prevent you from changing those shortcuts too frequently. addDynamicShortcuts(), setDynamicShortcuts(), and updateShortcuts() each return a boolean; false indicates that your request failed due to rate-limiting. You can also call isRateLimitingActive() on the ShortcutManager to find out in advance whether your app is being rate-limited and would not be able to affect dynamic app shortcut changes.

Also, there is a limit of how many app shortcuts you can have at any one time. geMaxShortcutCountPerActivity() on ShortcutManager reports this limit. Attempting to go past that will result in an exception. Android 7.1 appears to have a limit of five; if you try enabling all six bookmarks in the sample app, you will crash.

Privacy, Security, and App Shortcuts

Bear in mind that information contained in app shortcuts will be visible to home screens and anything else using LauncherApps to get at the possible app shortcuts. As such, please be careful to avoid putting sensitive information in app shortcuts (e.g., labels).