image


Привет Хабр! Предлагаю вашему вниманию свободный перевод статьи «Exploring Android Nougat 7.1 App Shortcuts» от Andrei Catinean.


Google выпустил Android Nougat с версией 7.1 (API 25). Появились некоторые интересные функции под капотом. Одна из этих дополнительных функций — app shortcuts. Эта статья расскажет, что они собой представляют, как они работают, и как вы можете их реализовать.


Конечный результат выглядит так:


image


Что такое App Shortcuts и зачем они нужны?


Ярлыки приложений — это средство изображения общих действий или задач вашего приложения на главном экране пользователя. Ваши пользователи могут раскрыть ярлыки при долгом нажатии запуска программы. С технической точки зрения, ярлыки приложений — это простой и быстрый способ использования intents.


Они бывают двух типов:


  • Static: это ярлыки, которые статически определяются в файле ресурсов. Они не могут быть изменены, если вы не измените файл и не развернете приложение
  • Динамический: это ярлыки, которые публикуются во время выполнения. Они могут быть обновлены без необходимости повторного развертывания приложения

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


Добавление ярлыков


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


STATIC SHORTCUTS


В этом примере предполагается, что у вас уже создан проект в Android Studio.
Перейдите в AndroidManifest.xml и добавьте следующий тег метаданных в свою основную активность:


<?xml version="1.0" encoding="utf-8"?>  
<manifest xmlns:android="http://schemas.android.com/apk/res/android"  
  package="com.catinean.appshortcutsdemo">

  <application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity android:name=".MainActivity">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
        <category android:name="android.intent.category.DEFAULT" />
      </intent-filter>

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

</manifest>  

В теге meta-data ключ android:resource соответствует ресурсу, определенному в файле res / xml / shortcuts.xml. Здесь вам нужно определить все ваши статические ярлыки. Давайте добавим тот, который откроет определенную активность из вашего приложения.


В приведенном ниже примере я создал фиктивный StaticShortcutActivity:


<?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_static_shortcut"
    android:shortcutDisabledMessage="@string/static_shortcut_disabled_message"
    android:shortcutId="static"
    android:shortcutLongLabel="@string/static_shortcut_long_label"
    android:shortcutShortLabel="@string/static_shortcut_short_label">
    <intent
      android:action="android.intent.action.VIEW"
      android:targetClass="com.catinean.appshortcutsdemo.StaticShortcutActivity"
      android:targetPackage="com.catinean.appshortcutsdemo" />
  </shortcut>
</shortcuts> 

Вы можете видеть, что корневой тег этого файла <shortcuts>, который может содержать несколько блоков <shortcut>. Каждый из них, как вы могли догадаться, представляет собой статический ярлык. Здесь для одного ярлыка можно задать следующие свойства:


  • enabled: Как указано в названии, включен ли ярлык или нет. Если вы решите отключить статический ярлык, вы можете либо установить значение false, либо просто удалить его из набора <shortcuts>.
  • icon: значок, который будет показан в левой части ярлыка.
  • shortcutDisabledMessage: это строка, которая будет показана пользователю, если он попытается запустить отключенный ярлык. Прим. переводчика: Такое может произойти, когда ярлык не убрали с главного экрана устройства.
  • shortcutLongLabel: это более длинный вариант описания ярлыка, который отображается, когда имеется достаточно места.
  • shortcutShortLabel: краткое описание ярлыка. Данное поле является обязательным. Это текст ярлыка, который пользователь увидит на своем главном экране.
  • intent: здесь вы определяете свое намерение (или несколько намерений), которое ваш ярлык откроется при нажатии.

Вот как этот ярлык будет отображаться пользователю вашего приложения:


image




Просто и понятно, но если вы это реализуете, то вы можете заметить, что при нажатии на кнопку "назад" пользователь возвращается на домашний экран.


Как насчет того, чтобы вместо этого перейти в приложение? Для этого мы можем добавить несколько intent-тэгов в ярлыках, которые мы ранее создали:


<?xml version="1.0" encoding="utf-8"?>  
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">  
  <shortcut
  ...>
    <intent
      android:action="android.intent.action.MAIN"
      android:targetClass="com.catinean.appshortcutsdemo.MainActivity"
      android:targetPackage="com.catinean.appshortcutsdemo" />
    <intent
      android:action="android.intent.action.VIEW"
      android:targetClass="com.catinean.appshortcutsdemo.StaticShortcutActivity"
      android:targetPackage="com.catinean.appshortcutsdemo" />
  </shortcut>
</shortcuts>  

Обратите внимание, как мы добавили дополнительный <intent> до того, что у нас было, указав на MainActivity. Это создаст back stack намерений, последний из которых будет открыт ярлыком. В нашем случае back stack выглядит как MainActivity > Static ShortcutActivity, поэтому при нажатии назад пользователь переходит в MainActivity:


image


Добавление статических ярлыков довольно просто. Давайте перейдем к динамическим.


DYNAMIC SHORTCUTS


Как следует из их названия, динамические ярлыки могут быть изменены во время выполнения без необходимости повторного открытия вашего приложения. Как вы могли догадаться, они не определяются через статический ресурс (shortcuts.xml), как статические, но создаются в коде.


Давайте добавим наш первый динамический ярлык! Для этого вам нужно будет использовать ShortcutManager и ShortcutInfo.Builder. Я буду создавать первый динамический ярлык в моей MainActivity.#OnCreate ():


@Override
protected void onCreate(Bundle savedInstanceState) {

    ...

    ShortcutManager shortcutManager = getSystemService(ShortcutManager.class);

    ShortcutInfo webShortcut = new ShortcutInfo.Builder(this, "shortcut_web")
            .setShortLabel("novoda.com")
            .setLongLabel("Open novoda.com web site")
            .setIcon(Icon.createWithResource(this, R.drawable.ic_dynamic_shortcut))
            .setIntent(new Intent(Intent.ACTION_VIEW, Uri.parse("https://novoda.com")))
            .build();

    shortcutManager.setDynamicShortcuts(Collections.singletonList(webShortcut));
}

В приведенном выше примере мы используем shortcutManager и создаем ShortcutInfo. Используя ShortcutInfo.Builder, мы можем установить различные свойства для ярлыка, который мы хотим создать. Все методы, которые мы используем выше, соответствуют тем же, что и для статического ярлыка. Однако одно свойство, которое является немного неясным, является идентификатором ярлыка, который определен в конструкторе StaticInfo.Builder как второй параметр — shortcut_web. В приведенном выше примере я определил намерение, которое откроет мой сайт. Наконец, я установил динамический ярлык в ShortcutManager. Посмотрим теперь, как выглядят наши ярлыки:


image




Круто! Теперь у нас есть 2 быстрых ярлыка в нашем приложении — один статический и одий динамический.


Давайте добавим еще один, который укажет на активность внутри приложения и посмотрим, как мы можем создать для него back stack:


@Override
protected void onCreate(Bundle savedInstanceState) {

    ...

    ShortcutInfo dynamicShortcut = new ShortcutInfo.Builder(this, "shortcut_dynamic")
            .setShortLabel("Dynamic")
            .setLongLabel("Open dynamic shortcut")
            .setIcon(Icon.createWithResource(this, R.drawable.ic_dynamic_shortcut_2))
            .setIntents(
                    new Intent[]{
                            new Intent(Intent.ACTION_MAIN, Uri.EMPTY, this, MainActivity.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK),
                            new Intent(DynamicShortcutActivity.ACTION)
                    })
            .build();

    shortcutManager.setDynamicShortcuts(Arrays.asList(webShortcut, dynamicShortcut));
}

Вы можете видеть, что теперь мы создаем setIntents () в builder'е, чтобы построить back stack:


  • Первое намерение соответствует MainActivity. Мы определяем флаг FLAG_ACTIVITY_CLEAR_TASK, чтобы очистить все существующие задачи, связанные с приложением, и сделать MainActivity текущим корневым действием


  • Второе соответствует DynamicShortcutActivity (пустая активность, которую я создал). Чтобы сделать это, нам нужно предоставить Intent конкретное действие, которое определяется как статическая String в DynamicShortcutActivity и соответствует имени действия intent-filter, определенному в AndroidManifest.xml для той же активности:

<activity  
      android:name=".DynamicShortcutActivity"
      android:label="Dynamic shortcut activity">
      <intent-filter>
        <action android:name="com.catinean.appshortcutsdemo.OPEN_DYNAMIC_SHORTCUT" />
        <category android:name="android.intent.category.DEFAULT" />
      </intent-filter>
</activity>  

Объявив массив намерений в этом порядке, то гарантируем, что когда пользователь вернется после открытия DynamicShortcutActivity, то MainActivity будет открыт.


Как это выглядит:


image




SHORTCUT ORDERING


Теперь, когда у нас есть 1 статический ярлык и 2 динамических, как мы можем задать для них порядок? Если мы более подробно рассмотрим методы ShortcutInfo.Builder, в частности: setRank (int). Установив настраиваемый ранг в динамический ярлык, мы можем контролировать порядок, в котором они появляются: чем выше ранг, тем выше ярлык.


В качестве примера, скажем, мы хотим, чтобы ярлык №2 (novoda.com) находился на вершине. Мы можем динамически изменять ряды уже добавленных динамических ярлыков. Давайте сделаем это, нажав кнопку в MainActivity:


findViewById(R.id.main_rank_button).setOnClickListener(new View.OnClickListener() {

      @Override
      public void onClick(View view) {
          ShortcutInfo webShortcut = new ShortcutInfo.Builder(MainActivity.this, "shortcut_web")
                  .setRank(1)
                  .build();

          ShortcutInfo dynamicShortcut = new ShortcutInfo.Builder(MainActivity.this, "shortcut_dynamic")
                  .setRank(0)
                  .build();

          shortcutManager.updateShortcuts(Arrays.asList(webShortcut, dynamicShortcut));
      }
});

В слушателе кнопки мы создаем новый ShortcutInfo для каждого ярлыка, которые мы ранее добавляли с теми же идентификаторами, но теперь мы устанавливаем более высокий ранг в shortcut_web, а нижний — для shortcut_dynamic. Наконец, мы используем метод updateShortcuts (List <ShortcutInfo>) для обновления ярлыков:


image


Вы можете видеть из приведенной выше гифки, что статический ярлык находится внизу списка. Важное замечание: вы не можете изменить ранг статического ярлыка. Они будут показаны в том порядке, в котором они определены в файле shortcuts.xml. Поскольку у нас есть только один статический ярлык, он имеет ранг по умолчанию 0, который нельзя изменить.


EXTRA BITS


Если мы более подробно рассмотрим метод setShortLabel (CharSequence), мы увидим, что он принимает CharSequence в качестве параметра. Что это значит? Ну, это означает, что мы можем немного поиграть с ним :-)


Предположим, мы хотим изменить цвет на красный, нажав на созданную выше кнопку. Мы можем создать SpannableStringBuilder и установить для него ForegroundColorSpan требуемый цвет, а затем передать spannableStringBuilder как shortLabel (поскольку SpannableStringBuilder реализует CharSequence):


findViewById(R.id.main_rank_button).setOnClickListener(new View.OnClickListener() {

    @Override
    public void onClick(View view) {

        ForegroundColorSpan colorSpan = new ForegroundColorSpan(getResources().getColor(android.R.color.holo_red_dark, getTheme()));
        String label = "novoda.com";
        SpannableStringBuilder colouredLabel = new SpannableStringBuilder(label);
        colouredLabel.setSpan(colorSpan, 0, label.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);

        ShortcutInfo webShortcut = new ShortcutInfo.Builder(MainActivity.this, "shortcut_web")
                .setShortLabel(colouredLabel)
                .setRank(1)
                .build();

        ...
    }
});

image


Итог


  • Ярлыки приложений отлично подходят для просмотра активностей вашего приложения и привлечения пользователей
  • Они могут быть статическими или динамическими
  • Статические ярлыки устанавливаются и вы можете обновить их только при повторном развертывании приложения
  • Динамические ярлыки можно менять на лету
  • Вы можете создать back stack активностей для каждого ярлыка
  • Ярлыки могут быть переупорядочены, но только в соответствующем типе
  • Статические ярлыки всегда будут внизу, так как они добавляются первыми
  • Описания ярлыков принимают CharSequence, поэтому вы можете манипулировать ими

Исходники приложения из статьи

Комментарии (2)