Shortcuts практическое использование

Начиная с версии 7.1 Nougat (API level 25) в андроиде появился долгожданный и удобный функционал под названием Android Shortcuts. Шорткаты необходимы для более простого доступа к некоторым функциями приложения в один клик. В пользовательском интерфейсе они могут выглядеть как всплывающее меню над иконкой приложения, или как отдельные иконки на экране лаунчера. Нажатие на них приводит пользователя в ту точку приложения которая написана на шорткате. Например, шорткат «Написать Васе» сразу же будет открывать активити написания сообщения для этого пользователя.

Разные лаунчеры показывают шорткаты по разному

Виды шорткатов ⭐

С точки зрения разработчика, шорткаты бывают: статические, динамические и прикреплённые . Статические в отличии от динамических, нельзя изменять и удалять программно. Они определены в конфигурационном xml файле и могут быть показаны даже до первого запуска приложения. С пользовательской точки зрения разницы между статическими и динамическими вообще никакой нет, выглядят и ведут себя они одинаково. Пользователи различают шорткаты в виде меню (как на картинке выше) и pinned (прикреплённые) шорткаты, которые представляют собой отдельную иконку на экране лаунчера (пользователь перетаскивает туда любой пункт всплывающего меню и она появляется). Прикреплённый шорткат может быть как динамическим так и статическим, а так же он может быть создан программно (если лаунчер поддерживает + версия андроид 8 и выше).

Static shortcuts

Статические шорткаты очень легко добавить в приложение, для этого потребуется сделать две вещи: создать xml файл описывающий шорткат (в директории res/xml) и обновить манифест приложения, прописав туда путь к этому файлу. Простейший конфигурационный файл шортката выглядит так:

<?xml version="1.0" encoding="utf-8"?>
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
    <shortcut
        android:shortcutId="id1"
        android:enabled="true"
        android:icon="@drawable/ic_stars_black_24dp"
        android:shortcutShortLabel="@string/shortcut_1_label"
        android:shortcutLongLabel="@string/shortcut_1_label_long"
        android:shortcutDisabledMessage="@string/shortcut_1_label_disabled">
        <intent
            android:action="android.intent.action.VIEW"
            android:targetPackage="ru.angryrobot.shortcuts"
            android:targetClass="ru.angryrobot.shortcuts.MainActivity" >
        </intent>
    <categories android:name="android.shortcut.conversation" />
    </shortcut>
</shortcuts>

Корневой элемент файла — shortcuts содержит внутри себя элементы shortcut, каждый из которых является отдельным шорткатом отображаемым в меню и имеет следующие атрибуты:

  • shortcutId — уникальный идентификатор шортката
  • enabled — определяет, включен шорткат или нет. По-умолчанию — true
  • icon — иконка которая отображается в шорткате. У гугла есть ряд рекомендаций к ним
  • shortcutShortLabel — короткое название для шортката. Именно оно отображается для pinned шорткатов. Рекомендуется не превышать лимит в 10 символов
  • shortcutLongLabel — длинное описание шортката, оно будет отображаться вместо короткого если на экране достаточно места. Рекомендуется не превышать лимит в 25 символов
  • shortcutDisabledMessage — сообщение которое показывается пользователю при попытке нажатия на шорткат который был выключен или удалён. Такое может произойти если пользователь вынес шорткат из меню на экран лаунчера (pinned шорткат), а в более новой версии приложения, этот шорткат выключили.
  • intent — Интент который будет запускать активити после клика по шорткату. Их может быть несколько, тогда клик создаст целый стек из активити. Самой верхней будет активити из последнего интента.
  • categories — определяет принадлежность шортката к определённой категории. Пока доступна только одна категория «conversation». Можно не указывать.

Теперь нужно связать этот конфигурационный файл с активити для которой шорткаты будут показываться. Для этого используется элемент meta-data с соответствующим именем:

<activity
    android:name=".MainActivity"
    android:label="@string/app_name"
    android:theme="@style/AppTheme.NoActionBar">

    <!-- Указываем ссылку на наш конфиг файл -->
    <meta-data
        android:name="android.app.shortcuts"
        android:resource="@xml/shortcuts"/>

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

Ничего больше делать не надо, сразу после установки приложения, шорткат из примера будет доступен и после нажатия будет открывать MainActivity.

Dynamic shortcuts 💪

Если приложение сложное, то одних только статических шорткатов может оказаться мало. Допустим в приложении есть возможность отправлять сообщения, и каким-то пользователям эти сообщения отправляются чаще чем другим. Сделать шорткат позволяющий в один клик написать сообщение — хорошая идея. И вот здесь то нам и понадобятся динамические шорткаты. Добавлением и удалением заведует специальный класс ShortcutManager который содержит все необходимые для этого методы.

//Получаем шорткат менеджер (проверка версии андроида пропущена для простоты)
ShortcutManager shortcutManager = (ShortcutManager) getSystemService(SHORTCUT_SERVICE);

// Билдер поможет создать шорткат
ShortcutInfo.Builder shortcutBuilder =
     //Задаём айди шортката
     new ShortcutInfo.Builder(getApplicationContext(), "id10")
     //Короткая надпись
     .setShortLabel("Избранное")
      //длинное описание шортката
     .setLongLabel("Открыть избранное")
      //Для какой активити этот шорткат
     .setActivity(getComponentName())
     // Чем меньше ранг - тем выше шорткат в списке
     .setRank(0)
     //Иконка шортката
     .setIcon(Icon.createWithResource(getApplicationContext(), R.drawable.ic_stars_black_24dp))
     // Интент запускающий активити
     .setIntent(new Intent(getApplicationContext(), MainActivity.class)
             .setAction(Intent.ACTION_MAIN));
// Список добавляемых шорткатов
List<ShortcutInfo> shortcuts = new LinkedList<>();
shortcuts.add(shortcutBuilder.build());
// Создаем шорткат
shortcutManager.addDynamicShortcuts(shortcuts);

Сразу после выполнения этого кода, шорткат создастся. Если он станет не нужен, его всегда можно удалить (или просто временно выключить) зная идентификатор:

LinkedList<String> ids = new LinkedList<>();
ids.add("id10");
// Навсегда удаляем
shortcutManager.removeDynamicShortcuts(ids);
//Или временно выключаем
shortcutManager.disableShortcuts(ids, "Более недоступно 🙁 ");

Если попытаться удалить шорткат с несуществующим id — ничего не произойдёт. Если попытаться создать шорткат с уже существующим id — он будет заменён. Метод setRank() позволяет задать ранг шортката, он определяет его положение относительно других. Чем меньше это число, тем выше к иконке приложения он будет располагаться. Но даже если установить его в ноль (минимальный ранг), то сверху всегда будут располагаться статические шорткаты если они есть. Гугл рекомендует вызывать метод reportShortcutUsed(String id) всякий раз когда шорткат был использован, (или пользователь сделал то же самое действие без использования шортката). Информация об использовании накапливается и может быть использована лаунчером чтоб предлагать пользователю запустить шорткат в той или иной ситуации.

Ограничения ⏳

Нельзя удалять, изменять или отключать статические шорткаты, попытка сделать это приведёт к выбрасыванию исключения. Так же существуют лимиты на максимальное количество шорткатов для одной активити, его можно получить при помощи метода getMaxShortcutCountPerActivity() обычно оно равно пяти, но почему-то на экране не бывает более 4-х шорткатов одновременно. При добавлении 6-го шортката — выбрасывается исключение, поэтому за количеством нужно следить. Еще очень важно помнить, что методы добавляющие, изменяющие шорткаты нельзя часто вызывать когда приложение работает в фоне. Если вызовы происходят слишком часто, методы setDynamicShortcuts(), addDynamicShortcuts() и updateShortcuts() начнут возвращать false и перестанут работать. Перед их вызовом можно проверить, есть ли в данный момент ограничения при помощи метода isRateLimitingActive() который вернёт true если их вызов ничего не даст. Ограничения действуют только пока приложение в фоне, сразу после разворачивания активити — лимитов нет.

Прикреплённые (pinned) шорткаты 📌

Pinned shortcuts

Как уже писалось выше, пользователь может самостоятельно прикрепить шорткат в любом свободном месте лаунчера, просто перетащив его из открывшегося контекстного меню. Но есть и программный способ их создания (начиная с Android 8.0). Количество таких шорткатов ограничено только свободным местом на экране устройства. Единственное условие — используемый лаунчер должен поддерживать это. Перед попыткой создания нужно проверять это при помощи метода isRequestPinShortcutSupported() который вернёт true если создание возможно. Ниже пример создания одного pinned шортката:

//Сначала проверяем поддерживается ли фунционал
if (shortcutManager.isRequestPinShortcutSupported()) {
   //Создаем шорткат как обычно
   ShortcutInfo.Builder shortcutBuilder =
        new ShortcutInfo.Builder(getApplicationContext(), "id0")
          .setShortLabel("Shortcut 1").setLongLabel("Shortcut 1")
          .setActivity(getComponentName())
          .setIcon(Icon.createWithResource(context, R.drawable.ic_star))
          .setIntent(new Intent(getApplicationContext(), MainActivity.class)
                  .setAction(Intent.ACTION_MAIN));
   Intent intent = shortcutManager.createShortcutResultIntent(shortcutBuilder.build());
   PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
   //Запрашиваем создание шортката. Пользователю покажется диалог где он должен согласиться
   shortcutManager.requestPinShortcut(shortcutBuilder.build(), pendingIntent.getIntentSender());
}

Оформление 🎨

Использование SpannableString

Методы задающие текст шортката принимают в качестве аргумента параметр CharSequence, а это значит, есть возможность задать не просто текст, а еще и отформатировать его. Для этого потребуется класс SpannableString который реализует этот интерфейс. С его помощью можно создать что-то подобное как на картине справа. Главное не злоупотреблять этим и пользоваться с осторожностью. Цвета отличные от чёрного зачастую могут странно смотреться. Весь код который формирует подобные форматированные строки, представлен ниже:

Spannable text1 = new SpannableString("Цвет, фон ");
ForegroundColorSpan color = new ForegroundColorSpan(Color.RED);
BackgroundColorSpan backgroundColor = new BackgroundColorSpan(Color.BLUE);
text1.setSpan(color, 0, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
text1.setSpan(backgroundColor, 6, 9, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

Spannable text2 = new SpannableString("Черта, жирный");
text2.setSpan(new UnderlineSpan(), 0, 5, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
text2.setSpan(new StyleSpan(Typeface.BOLD), 7, 13, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

Если цветов и начертаний недостаточно, можно легко добавить эмодзи в название шортката.

Вместо заключения 🏁

Приложения должны идти в ногу со временем, и по максимуму использовать новые возможности новых версий андроида. Использование Android Shortcuts позволит упростить жизнь пользователям и сделает приложение более современным и удобным. Как и в любом другом случае, важно помнить об уместности того или иного функционала и не пытаться добавлять его туда, где он совершенно не нужен.