public class ShortcutManager extends Object
ShortcutInfo
class contains information about each of the
shortcuts themselves.
There are two ways to publish shortcuts: static shortcuts and dynamic shortcuts.
AndroidManifest.xml
file.
Static shortcuts are published when an app is installed,
and the details of these shortcuts change when an app is upgraded with an updated XML
file.
Static shortcuts are immutable, and their
definitions, such as icons and labels, cannot be changed dynamically without upgrading the
publisher app.
Only main activities—activities that handle the MAIN
action and the
LAUNCHER
category—can have shortcuts.
If an app has multiple main activities, these activities have different sets
of shortcuts.
Static shortcuts and dynamic shortcuts are shown in the currently active launcher when the user long-presses on an app's launcher icon.
Note: The actual gesture may be different depending on the launcher app.
Each launcher icon can have at most getMaxShortcutCountPerActivity()
number of
static and dynamic shortcuts combined.
Launcher apps allow users to pin shortcuts so they're easier to access. Both static and dynamic shortcuts can be pinned. Pinned shortcuts cannot be removed by publisher apps; they're removed only when the user removes them, when the publisher app is uninstalled, or when the user performs the clear data action on the publisher app from the device's Settings app.
However, the publisher app can disable pinned shortcuts so they cannot be started. See the following sections for details.
When a dynamic shortcut is pinned, even when the publisher removes it as a dynamic shortcut,
the pinned shortcut will still be visible and launchable. This allows an app to have
more than getMaxShortcutCountPerActivity()
number of shortcuts.
For example, suppose getMaxShortcutCountPerActivity()
is 5:
updateShortcuts(List)
to update any of the existing
8 shortcuts, when, for example, the chat peers' icons have changed.
The addDynamicShortcuts(List)
and setDynamicShortcuts(List)
methods
can also be used
to update existing shortcuts with the same IDs, but they cannot be used
for updating non-dynamic, pinned shortcuts because these two methods try to convert the given
lists of shortcuts to dynamic shortcuts.
If the deprecated shortcut is pinned, then the pinned shortcut will remain on the launcher, but it will be disabled automatically. Note that, in this case, the pinned shortcut is no longer a static shortcut, but it's still immutable. Therefore, it cannot be updated using this class's APIs.
disableShortcuts(List)
, which removes the specified dynamic
shortcuts and also makes any specified pinned shortcuts un-launchable.
The disableShortcuts(List, CharSequence)
method can also be used to disabled shortcuts
and show users a custom error message when they attempt to launch the disabled shortcuts.
In order to add static shortcuts to your app, first add
<meta-data android:name="android.app.shortcuts" />
to your main activity in
AndroidManifest.xml:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.myapplication"> <application ... > <activity android:name="Main"> <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> </application> </manifest>Then, define your app's static shortcuts in the
res/xml/shortcuts.xml
file:
<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 is what the user sees when they launch this shortcut. --> <categories android:name="android.shortcut.conversation" /> </shortcut> <!-- Specify more shortcuts here. --> </shortcuts>The following list includes descriptions for the different attributes within a static shortcut:
android:shortcutId
This must be a string literal.
A resource string, such as @string/foo
, cannot be used.
android:enabled
true
. Can be set to false
in order
to disable a static shortcut that was published in a previous version and set a custom
disabled message. If a custom disabled message is not needed, then a static shortcut can
be simply removed from the XML file rather than keeping it with enabled="false"
.android:icon
android:shortcutShortLabel
ShortcutInfo.Builder.setShortLabel(CharSequence)
.
This must be a resource string, such as @string/shortcut_label
.
android:shortcutLongLabel
ShortcutInfo.Builder.setLongLabel(CharSequence)
.
This must be a resource string, such as @string/shortcut_long_label
.
android:shortcutDisabledMessage
android:enabled
is set to
false
, this attribute is used to display a custom disabled message.
This must be a resource string, such as @string/shortcut_disabled_message
.
intent
android:action
is mandatory.
See Using intents for the
other supported tags.
You can provide multiple intents for a single shortcut so that the last defined activity is launched
with the other activities in the back stack.
See TaskStackBuilder
for details.
categories
ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION
is defined in the framework.
Apps can publish dynamic shortcuts with setDynamicShortcuts(List)
or addDynamicShortcuts(List)
. The updateShortcuts(List)
method can also be
used to update existing, mutable shortcuts.
Use removeDynamicShortcuts(List)
or removeAllDynamicShortcuts()
to remove
dynamic shortcuts.
The following code snippet shows how to create a single dynamic shortcut:
ShortcutManager shortcutManager = getSystemService(ShortcutManager.class); ShortcutInfo shortcut = new ShortcutInfo.Builder(this, "id1") .setShortLabel("Web site") .setLongLabel("Open the web site") .setIcon(Icon.createWithResource(context, R.drawable.icon_website)) .setIntent(new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.mysite.example.com/"))) .build(); shortcutManager.setDynamicShortcuts(Arrays.asList(shortcut));
Dynamic shortcuts can be published with any set of Intent
flags.
Typically, Intent.FLAG_ACTIVITY_CLEAR_TASK
is specified, possibly along with other
flags; otherwise, if the app is already running, the app is simply brought to
the foreground, and the target activity may not appear.
The ShortcutInfo.Builder.setIntents(Intent[])
method can be used instead of
ShortcutInfo.Builder.setIntent(Intent)
with TaskStackBuilder
in order to launch an activity with other activities in the back stack.
When the user selects a shortcut to load an activity with a back stack,
then presses the back key, a parent activity from the same app will be shown
instead of the user being navigated back to the launcher.
Static shortcuts can also have multiple intents to achieve the same effect.
In order to associate multiple Intent
objects with a shortcut, simply list multiple
<intent>
elements within a single <shortcut>
element.
The last intent specifies what the user sees when they launch a shortcut.
Static shortcuts cannot have custom intent flags.
The first intent of a static shortcut will always have Intent.FLAG_ACTIVITY_NEW_TASK
and Intent.FLAG_ACTIVITY_CLEAR_TASK
set.
This means, when the app is already running, all the existing activities will be
destroyed when a static shortcut is launched.
If this behavior is not desirable, you can use a trampoline activity,
or an invisible activity that starts another activity in Activity.onCreate(android.os.Bundle)
,
then calls Activity.finish()
.
The first activity should include an attribute setting
of android:taskAffinity=""
in the app's AndroidManifest.xml
file, and the intent within the static shortcut should point at this first activity.
In order to avoid confusion, you should not use updateShortcuts(List)
to update
a shortcut so that it contains conceptually different information.
For example, a phone app may publish the most frequently called contact as a dynamic
shortcut. Over time, this contact may change. When it does, the app should
represent the changed contact with a new shortcut that contains a different ID, using either
setDynamicShortcuts(List)
or addDynamicShortcuts(List)
, rather than updating
the existing shortcut with updateShortcuts(List)
.
This is because when the shortcut is pinned, changing
it to reference a different contact will likely confuse the user.
On the other hand, when the
contact's information has changed, such as the name or picture, the app should
use updateShortcuts(List)
so that the pinned shortcut is updated too.
ShortcutInfo.isDeclaredInManifest()
is true
),
and then show dynamic shortcuts (if ShortcutInfo.isDynamic()
is true
).
ShortcutInfo.getRank()
.
Shortcut ranks are non-negative, sequential integers
that determine the order in which shortcuts appear, assuming that the shortcuts are all in
the same category.
Ranks of existing shortcuts can be updated with
updateShortcuts(List)
. You can also use addDynamicShortcuts(List)
and
setDynamicShortcuts(List)
.
Ranks are auto-adjusted so that they're unique for each target activity in each category (static or dynamic). For example, if there are 3 dynamic shortcuts with ranks 0, 1 and 2, adding another dynamic shortcut with a rank of 1 represents a request to place this shortcut at the second position. In response, the third and fourth shortcuts move closer to the bottom of the shortcut list, with their ranks changing to 2 and 3, respectively.
Calls to setDynamicShortcuts(List)
, addDynamicShortcuts(List)
, and
updateShortcuts(List)
may be rate-limited when called by background apps, or
apps with no foreground activity or service. When you attempt to call these methods
from a background app after exceeding the rate limit, these APIs return false
.
Apps with a foreground activity or service are not rate-limited.
Rate-limiting is reset upon certain events, so that even background apps can call these APIs until the rate limit is reached again. These events include the following:
When rate-limiting is active, isRateLimitingActive()
returns true
.
If your app is rate-limited during development or testing, you can use the
Reset ShortcutManager rate-limiting development option or
the following adb
command to reset it:
$ adb shell cmd shortcut reset-throttling [ --user USER-ID ]
Apps should update dynamic and pinned shortcuts when the system locale changes
using the Intent.ACTION_LOCALE_CHANGED
broadcast.
When the system locale changes, rate-limiting is reset, so even background apps can add and update dynamic shortcuts until the rate limit is reached again.
When an app has the android:allowBackup="true"
attribute assignment included
in its AndroidManifest.xml
file, pinned shortcuts are
backed up automatically and are restored when the user sets up a new device.
updateShortcuts(List)
to re-publish icons.
AndroidManifest.xml
file.
Because dynamic shortcuts are not restored, it is recommended that apps check
currently-published dynamic shortcuts using getDynamicShortcuts()
each time they are launched, and they should re-publish
dynamic shortcuts when necessary.
public class MainActivity extends Activity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ShortcutManager shortcutManager = getSystemService(ShortcutManager.class); if (shortcutManager.getDynamicShortcuts().size() == 0) { // Application restored. Need to re-publish dynamic shortcuts. if (shortcutManager.getPinnedShortcuts().size() > 0) { // Pinned shortcuts have been restored. Use // updateShortcuts() to make sure they contain // up-to-date information. } } } // ... }
Because pinned shortcuts are backed up and restored on new devices, shortcut IDs should contain either stable, constant strings or server-side identifiers, rather than identifiers generated locally that might not make sense on other devices.
Launcher apps may be capable of predicting which shortcuts will most likely be used at a given time by examining the shortcut usage history data.
In order to provide launchers with such data, publisher apps should
report the shortcuts that are used with reportShortcutUsed(String)
when a shortcut is selected,
or when an action equivalent to a shortcut is taken by the user even if it wasn't started
with the shortcut.
For example, suppose a navigation app supports "navigate to work" as a shortcut. It should then report when the user selects this shortcut and when the user chooses to navigate to work within the app itself. This helps the launcher app learn that the user wants to navigate to work at a certain time every weekday, and it can then show this shortcut in a suggestion list at the right time.
LauncherApps
class provides APIs for launcher apps to access shortcuts.
Constructor and Description |
---|
ShortcutManager(Context context) |
ShortcutManager(Context context,
IShortcutService service) |
Modifier and Type | Method and Description |
---|---|
boolean |
addDynamicShortcuts(List<ShortcutInfo> shortcutInfoList)
Publish the list of dynamic shortcuts.
|
void |
disableShortcuts(List<String> shortcutIds)
Disable pinned shortcuts.
|
void |
disableShortcuts(List<String> shortcutIds,
CharSequence disabledMessage)
Disable pinned shortcuts, showing the user a custom error message when they try to select
the disabled shortcuts.
|
void |
disableShortcuts(List<String> shortcutIds,
int disabledMessageResId) |
void |
disableShortcuts(List<String> shortcutIds,
String disabledMessage) |
void |
enableShortcuts(List<String> shortcutIds)
Re-enable pinned shortcuts that were previously disabled.
|
List<ShortcutInfo> |
getDynamicShortcuts()
Return all dynamic shortcuts from the caller app.
|
int |
getIconMaxHeight()
Return the max height for icons, in pixels.
|
int |
getIconMaxWidth()
Return the max width for icons, in pixels.
|
List<ShortcutInfo> |
getManifestShortcuts()
Return all static (manifest) shortcuts from the caller app.
|
int |
getMaxShortcutCountForActivity() |
int |
getMaxShortcutCountPerActivity()
Return the maximum number of static and dynamic shortcuts that each launcher icon
can have at a time.
|
List<ShortcutInfo> |
getPinnedShortcuts()
Return all pinned shortcuts from the caller app.
|
long |
getRateLimitResetTime()
Return when the rate limit count will be reset next time, in milliseconds since the epoch.
|
int |
getRemainingCallCount()
Return the number of times the caller app can call the rate-limited APIs
before the rate limit counter is reset.
|
protected int |
injectMyUserId() |
boolean |
isRateLimitingActive()
Return
true when rate-limiting is active for the caller app. |
void |
onApplicationActive(String packageName,
int userId)
Called internally when an app is considered to have come to the foreground
even when technically it's not.
|
void |
removeAllDynamicShortcuts()
Delete all dynamic shortcuts from the caller app.
|
void |
removeDynamicShortcuts(List<String> shortcutIds)
Delete dynamic shortcuts by ID.
|
void |
reportShortcutUsed(String shortcutId)
Apps that publish shortcuts should call this method whenever the user
selects the shortcut containing the given ID or when the user completes
an action in the app that is equivalent to selecting the shortcut.
|
boolean |
setDynamicShortcuts(List<ShortcutInfo> shortcutInfoList)
Publish the list of shortcuts.
|
boolean |
updateShortcuts(List<ShortcutInfo> shortcutInfoList)
Update all existing shortcuts with the same IDs.
|
public ShortcutManager(Context context, IShortcutService service)
public ShortcutManager(Context context)
public boolean setDynamicShortcuts(List<ShortcutInfo> shortcutInfoList)
This API will be rate-limited.
true
if the call has succeeded. false
if the call is rate-limited.IllegalArgumentException
- if getMaxShortcutCountPerActivity()
is exceeded,
or when trying to update immutable shortcuts.IllegalStateException
- when the user is locked.public List<ShortcutInfo> getDynamicShortcuts()
IllegalStateException
- when the user is locked.public List<ShortcutInfo> getManifestShortcuts()
IllegalStateException
- when the user is locked.public boolean addDynamicShortcuts(List<ShortcutInfo> shortcutInfoList)
This API will be rate-limited.
true
if the call has succeeded. false
if the call is rate-limited.IllegalArgumentException
- if getMaxShortcutCountPerActivity()
is exceeded,
or when trying to update immutable shortcuts.IllegalStateException
- when the user is locked.public void removeDynamicShortcuts(List<String> shortcutIds)
IllegalStateException
- when the user is locked.public void removeAllDynamicShortcuts()
IllegalStateException
- when the user is locked.public List<ShortcutInfo> getPinnedShortcuts()
IllegalStateException
- when the user is locked.public boolean updateShortcuts(List<ShortcutInfo> shortcutInfoList)
This API will be rate-limited.
true
if the call has succeeded. false
if the call is rate-limited.IllegalArgumentException
- If trying to update immutable shortcuts.IllegalStateException
- when the user is locked.public void disableShortcuts(List<String> shortcutIds)
ShortcutManager
class.IllegalArgumentException
- If trying to disable immutable shortcuts.IllegalStateException
- when the user is locked.public void disableShortcuts(List<String> shortcutIds, CharSequence disabledMessage)
ShortcutManager
class.IllegalArgumentException
- If trying to disable immutable shortcuts.IllegalStateException
- when the user is locked.public void enableShortcuts(List<String> shortcutIds)
IllegalArgumentException
- If trying to enable immutable shortcuts.IllegalStateException
- when the user is locked.public int getMaxShortcutCountForActivity()
public int getMaxShortcutCountPerActivity()
public int getRemainingCallCount()
getRateLimitResetTime()
public long getRateLimitResetTime()
public boolean isRateLimitingActive()
true
when rate-limiting is active for the caller app.
See the class level javadoc for details.
IllegalStateException
- when the user is locked.public int getIconMaxWidth()
public int getIconMaxHeight()
public void reportShortcutUsed(String shortcutId)
ShortcutManager
class
The information is accessible via UsageStatsManager.queryEvents(long, long)
Typically, launcher apps use this information to build a prediction model
so that they can promote the shortcuts that are likely to be used at the moment.
IllegalStateException
- when the user is locked.public void onApplicationActive(String packageName, int userId)
protected int injectMyUserId()