Я — Денис, Android-разработчик в «Лайв Тайпинге». В этой статье я расскажу о том как добавить поддержку App shortcuts в Android. Я поделюсь опытом создания шорткатов на примере мобильного приложения — мессенджера.
Введение
Мобильные приложения дарят возможность совершать почти любые действия на ходу. Назначить встречу в поликлинике всего в несколько касаний, заказать обед, проложить маршрут — все эти функции доступны прямо с мобильного устройства. Но бывают моменты, когда скорость становится критически важной — требуется мгновенное реагирование. Как быть приложению в таких случаях?
С шорткатами пользователь может мгновенно получить доступ к практически любому действию в приложении. Например, в Shazam шорткаты можно использовать для быстрого распознавания музыки — достаточно лишь двух касаний, чтобы узнать, что играет. Это значительно упрощает и ускоряет процесс использования приложения для пользователя.
Обычный сценарий: открываете приложение → ждёте, пока оно загрузится → находите нужное приложение → песня уже закончилась.
Сценарий с шорткатом: вы зажимаете иконку приложения и нажимаете нужное действие — приложение мгновенно распознаёт музыку.

Зачем нужны шорткаты
Для шорткатов предусмотрено много разнообразных возможностей. Вот лишь небольшой обзор того, как пользователи обычно взаимодействуют с ними:
в мессенджерах: быстрый доступ к последнему активному чату, набор номера для звонка, создание новых чатов или групповых бесед.
в приложениях доставки: оплата товаров в корзине, просмотр истории заказов, вызов курьера для доставки.
в коммерческих приложениях: проверка баланса бонусного счета, повторение предыдущего заказа, поиск ближайшего пункта самовывоза.
Как настроить шорткаты
Виды шорткатов
Существуют три вида шорткатов: статичные, динамические, закреплённые.
cтатичные шорткаты — заранее определяются и остаются неизменными. Изменить их можно лишь путем внесения изменений в код приложения;
динамические шорткаты — позволяют изменяться на лету в любое время;
закреплённые шорткаты — находятся выше остальных шорткатов в списке.
?? Пользователи могут создать закреплённые шорткаты, скопировав статические или динамические шорткаты на рабочем столе.
Статичные
Лучше всего подходят для приложений, которым нужно только перейти к опрелённому экрану или действию. Статические шорткаты полезны для выполнения рутинных задач, например, если пользователь хочет просмотреть свой календарь или электронную почту.
Чтобы добавить статический шорткат создайте в res/xml файл shortcuts.xml:
<?xml version="1.0" encoding="utf-8"?>
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
<shortcut
android:enabled="true"
android:icon="@drawable/ic_call"
android:shortcutDisabledMessage="@string/disabled_message"
android:shortcutId="static"
android:shortcutLongLabel="@string/static_long_label"
android:shortcutShortLabel="@string/static_short_label">
<intent
android:action="android.intent.action.VIEW"
android:targetClass="ru.popkov.appshortcut.MainActivity"
android:targetPackage="ru.popkov.appshortcut">
<extra
android:name="shortcut_id"
android:value="static" />
</intent>
</shortcut>
</shortcuts>Android studio не автокомплитит код для шорткатов, поэтому вы можете скопировать и переписать код под своё приложение. Ниже расскажу про параметры из кода выше.
enabled— определяет доступен шорткат или нет;shortcutDisabledMessage— сообщение, которое видет пользователь, если шорткат отключен;shortcutShortLabel— короткий текст, который видет пользователь в списке шорткатов (до 10 символов);shortcutLongLabel— длинный текст, который видет пользователь в списке шорткатов (до 25 символов);targetClass— активити, которая будет открыта по клику на шорткат;icon— адаптивная иконка или bitmap, которая отображается возле текста в списке шорткатов.
Динамические
Нужны в контекстно-чувствительных приложениях. Контекстно-зависимые шорткаты адаптированы к действиям, которые пользователи выполняют в приложении. Например, если вы разрабатываете мессенджер, вы можете динамически обновлять список шорткатов, которые направляют пользователя на часто используемые диалоги.
Чтобы добавить динамический шорткат напишите следующий код:
private val shortcutManager = getSystemService(applicationContext, ShortcutManager::class.java)
fun addDynamicShortcut(
applicationContext: Context,
shortcut: ShortcutModel,
shortcutIntent: Intent,
shortcutId: String,
) {
val shortcutInfo = ShortcutInfoCompat.Builder(applicationContext, shortcutId)
.setShortLabel(shortcut.shortLabel)
.setLongLabel(shortcut.longLabel)
.setIcon(IconCompat.createFromIcon(applicationContext, shortcut.shortcutIcon))
.setIntent(shortcutIntent)
.build()
ShortcutManagerCompat.pushDynamicShortcut(applicationContext, shortcutInfo)
}В репозитории реализована идея описанная выше: в списке шорткатов появляются часто используемые диалоги, по клику вы попадаете сразу в нужный чат.

Закреплённые
Используются для конкретных действий. Например, когда пользователь хочет прикрепить определённый веб-сайт к лаунчеру. Это удобно, потому что позволяет выполнить действие ещё на один шаг быстрее, чем использование шорткаты.
Поведение со статическим или динамическим шорткатом: зажал иконку приложения → выбрал шорткат → перешёл к нужному действию.
Поведение с закреплённым шорткатом: нажал на шорткат → перешёл к нужному действию.
Создание закреплённого шортката похоже на создание динамического шортката, но требует дополнительного разрешения от пользователя. Сейчас покажу как это сделать:
private val shortcutManager = getSystemService(applicationContext, ShortcutManager::class.java)
fun addPinnedShortcut(
applicationContext: Context,
shortcut: ShortcutModel,
shortcutIntent: Intent,
shortcutId: String,
) {
if (shortcutManager!!.isRequestPinShortcutSupported) {
val shortcutInfo = ShortcutInfo.Builder(applicationContext, shortcutId)
.setShortLabel(shortcut.shortLabel)
.setLongLabel(shortcut.longLabel)
.setIcon(shortcut.shortcutIcon)
.setIntent(shortcutIntent)
.build()
pinShortcut(applicationContext, shortcutInfo)
}
}
private fun pinShortcut(
applicationContext: Context,
shortcut: ShortcutInfo,
) {
val callbackIntent = shortcutManager!!.createShortcutResultIntent(shortcut)
val successPendingIntent = PendingIntent.getBroadcast(
applicationContext,
0,
callbackIntent,
PendingIntent.FLAG_IMMUTABLE
)
shortcutManager.requestPinShortcut(shortcut, successPendingIntent.intentSender)
}Контроль действий
Если вы хотите написать логику, которая должна выполниться после нажатия определённого шортката, то вы можете поместить в MainActivity такой код:
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
handleIntent(intent)
}
private fun handleIntent(intent: Intent?) {
intent?.let {
val shortcutType = when (intent.getStringExtra(SHORTCUT_ID)) {
"static" -> ShortcutType.STATIC
"dynamic" -> ShortcutType.DYNAMIC
else -> ShortcutType.PINNED
}
}
}Ограничения
Большинство лаунчеров отображают до четырёх шорткатов для одного приложения. Чтобы узнать количество шорткатов, которые поддерживает устройство — напишите:
getMaxShortcutCountPerActivity()Также нет лимита для количества закреплённых шорткатов, которые может создать пользователь. Ваше приложение не может напрямую удалить такие шорткаты, но их можно отключить.
При очистки кэша приложения динамические и закреплённые шорткаты будут удалены.
Расположение в списке
Шорткаты отображаются в лаунчере в следующем порядке:
статические шорткаты;
динамические шорткаты.
Также шорткаты могут сортироваться в зависимости от rank (ранга). Это положительное число. Для динамических шорткатов вы можете выставить это значение самостоятельно, когда вызываете метод updateShortcuts, addDynamicShortcuts, pushDynamicShortcut или setDynamicShortuts.
Большинство лаунчеров отображают максимум четыре шортката. Для любой комбинации статических и динамических шорткатов, которые определены, лаунчер отобразит максимум два статических и два динамических шортката. Например, если вы создадите четыре статических шортката и программно создадите ещё три динамических шортката, лаунчер отобразит два первых статических шортката и два наиболее высоко ранговых динамических шортката.
Особенности работы
Android сам никак не регулирует количество добавляемых шорткатов. Если вы по какой-то причине захотите добавить шестой шорткат, приложение просто упадет, выбросив IllegalArgumentException: Max number of dynamic shortcuts exceeded.
Если не задать хотя бы одному интенту шортката атрибут action, то в случае динамического шортката приложение упадет при создании этого шортката, а в случае статического — шорткат просто не будет создан.
Если попытаться вызвать getSystemService(ShortcutManager.class) на устройстве ниже 23 API, то приложение упадет с ClassNotFoundException.
Заключение
Спасибо, что прочитали статью! Ниже я собрал для вас полезные ссылки и дополнительные материалы по этой теме.
Если вы нашли неточности/ошибки в статье или просто хотите дополнить её своим мнением — то прошу в комментарии!
