Создайте собственные плитки быстрых настроек для своего приложения.

Быстрые настройки — это плитки, отображаемые на панели быстрых настроек и представляющие собой действия, которые пользователи могут быстро выполнять, нажимая на них. Ваше приложение может предоставлять пользователям пользовательские плитки через класс TileService и использовать объект Tile для отслеживания состояния плитки. Например, вы можете создать плитку, которая позволяет пользователям включать или выключать VPN, предоставляемый вашим приложением.

Панель быстрых настроек с включенным и выключенным VPN-модулем
Рисунок 1. Панель быстрых настроек с включенным и выключенным элементом VPN.

Решите, когда создавать плитку.

Мы рекомендуем создавать плитки для конкретных функций, к которым, как ожидается, пользователи будут часто обращаться или к которым им нужен быстрый доступ (или и то, и другое). Наиболее эффективными являются плитки, которые соответствуют обоим этим качествам, обеспечивая быстрый доступ к часто выполняемым действиям.

Например, вы могли бы создать для фитнес-приложения плитку, которая позволяла бы пользователям быстро начать тренировку. Однако мы бы не рекомендовали создавать для того же приложения плитку, которая позволяла бы пользователям просматривать всю историю своих тренировок.

Примеры использования плиток в фитнес-приложениях
Рисунок 2. Примеры рекомендуемых и нерекомендуемых элементов интерфейса для фитнес-приложения.

Для повышения удобства и доступности вашей плитки мы рекомендуем избегать некоторых действий:

  • Избегайте использования плиток для запуска приложений. Вместо этого используйте ярлыки приложений или стандартные панели запуска.

  • Избегайте использования плиток для одноразовых действий пользователя. Вместо этого используйте ярлык приложения или уведомление .

  • Избегайте создания слишком большого количества плиток. Мы рекомендуем максимум две на одно приложение. Вместо этого используйте ярлык приложения.

  • Избегайте использования плиток, отображающих информацию, но не являющихся интерактивными для пользователей. Вместо этого используйте уведомления или виджеты .

Создайте свою плитку

Для создания плитки необходимо сначала создать подходящую иконку для плитки, а затем создать и объявить TileService в файле манифеста вашего приложения.

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

Создайте свою собственную иконку

Вам потребуется указать пользовательскую иконку, которая будет отображаться на плитке в панели быстрых настроек. (Вы добавите эту иконку при объявлении TileService , как описано в следующем разделе.) Иконка должна быть сплошного белого цвета с прозрачным фоном, размером 24 x 24dp и иметь вид VectorDrawable .

Пример векторного изображения
Рисунок 3. Пример векторного изображения.

Создайте иконку, которая визуально намекает на назначение вашего элемента. Это поможет пользователям легко определить, соответствует ли ваш элемент их потребностям. Например, вы можете создать иконку секундомера для элемента в фитнес-приложении, который позволяет пользователям начать тренировку.

Создайте и объявите свой TileService.

Создайте для своего тайла сервис, расширяющий класс TileService .

Котлин

class MyQSTileService: TileService() {

  // Called when the user adds your tile.
  override fun onTileAdded() {
    super.onTileAdded()
  }
  // Called when your app can update your tile.
  override fun onStartListening() {
    super.onStartListening()
  }

  // Called when your app can no longer update your tile.
  override fun onStopListening() {
    super.onStopListening()
  }

  // Called when the user taps on your tile in an active or inactive state.
  override fun onClick() {
    super.onClick()
  }
  // Called when the user removes your tile.
  override fun onTileRemoved() {
    super.onTileRemoved()
  }
}

Java

public class MyQSTileService extends TileService {

  // Called when the user adds your tile.
  @Override
  public void onTileAdded() {
    super.onTileAdded();
  }

  // Called when your app can update your tile.
  @Override
  public void onStartListening() {
    super.onStartListening();
  }

  // Called when your app can no longer update your tile.
  @Override
  public void onStopListening() {
    super.onStopListening();
  }

  // Called when the user taps on your tile in an active or inactive state.
  @Override
  public void onClick() {
    super.onClick();
  }

  // Called when the user removes your tile.
  @Override
  public void onTileRemoved() {
    super.onTileRemoved();
  }
}

Объявите свой TileService в файле манифеста вашего приложения. Добавьте имя и метку вашего TileService , созданную вами в предыдущем разделе пользовательскую иконку и соответствующие разрешения.

 <service
     android:name=".MyQSTileService"
     android:exported="true"
     android:label="@string/my_default_tile_label"  // 18-character limit.
     android:icon="@drawable/my_default_icon_label"
     android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
     <intent-filter>
         <action android:name="android.service.quicksettings.action.QS_TILE" />
     </intent-filter>
 </service>

Управление службой TileService

После того как вы создали и объявили свой TileService в манифесте приложения, вам необходимо управлять его состоянием.

TileService — это привязанный сервис . Ваш TileService привязывается по запросу вашего приложения или когда системе необходимо с ним взаимодействовать. Типичный жизненный цикл привязанного сервиса включает следующие четыре метода обратного вызова: onCreate() , onBind() , onUnbind() и onDestroy() . Эти методы вызываются системой каждый раз, когда сервис переходит в новую фазу жизненного цикла.

Обзор жизненного цикла TileService

Помимо коллбэков, управляющих жизненным циклом связанного сервиса, необходимо реализовать и другие методы, специфичные для жизненного цикла TileService . Эти методы могут вызываться вне методов onCreate() и onDestroy() поскольку методы жизненного цикла Service и методы жизненного цикла TileService вызываются в двух отдельных асинхронных потоках.

Жизненный цикл TileService включает следующие методы, которые вызываются системой каждый раз, когда ваш TileService переходит в новую фазу жизненного цикла:

  • onTileAdded() : Этот метод вызывается только при первом добавлении пользователем вашего тайла, а также при удалении и повторном добавлении. Это оптимальное время для однократной инициализации. Однако этого может быть недостаточно для выполнения всех необходимых инициализаций.

  • Методы onStartListening() и onStopListening() вызываются всякий раз, когда ваше приложение обновляет плитку, и вызываются часто. TileService остается связанным между onStartListening() и onStopListening() , что позволяет вашему приложению изменять плитку и отправлять обновления.

  • onTileRemoved() : Этот метод вызывается только в том случае, если пользователь удаляет вашу плитку.

Выберите режим прослушивания

Ваш TileService может работать в активном или неактивном режиме. Мы рекомендуем использовать активный режим, который необходимо указать в манифесте приложения. В противном случае TileService работает в стандартном режиме и не требует указания режима.

Не следует предполагать, что ваш TileService будет находиться вне пар методов onStartListening() и onStopListening() .

Используйте активный режим для TileService , который отслеживает и контролирует свое состояние в собственном процессе. TileService в активном режиме привязан к событиям onTileAdded() , onTileRemoved() , событиям касания, а также к событиям, запрашиваемым процессом приложения.

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

Статический метод TileService.requestListeningState() можно вызвать для запроса начала прослушивания и получения обратного вызова для onStartListening() .

Для объявления активного режима добавьте параметр META_DATA_ACTIVE_TILE в файл манифеста вашего приложения.

<service ...>
    <meta-data android:name="android.service.quicksettings.ACTIVE_TILE"
         android:value="true" />
    ...
</service>

Неактивный режим

Неактивный режим — это стандартный режим. TileService находится в неактивном режиме, если он привязан всякий раз, когда ваш тайл виден пользователю. Это означает, что ваш TileService может быть создан и привязан снова в моменты, не зависящие от него. Он также может быть отвязан и уничтожен, когда пользователь не просматривает тайл.

Ваше приложение получает обратный вызов метода onStartListening() после того, как пользователь откроет панель быстрых настроек. Вы можете обновлять объект Tile столько раз, сколько хотите, в промежутке между onStartListening() и onStopListening() .

Нет необходимости объявлять неактивный режим — просто не добавляйте META_DATA_ACTIVE_TILE в файл манифеста вашего приложения.

Обзор состояний плиток

После добавления пользователем вашего элемента, он всегда находится в одном из следующих состояний.

  • STATE_ACTIVE : Указывает на состояние «включено» или «активно». Пользователь может взаимодействовать с вашим элементом, находясь в этом состоянии.

    Например, для элемента фитнес-приложения, позволяющего пользователям запускать тренировку с таймером, STATE_ACTIVE будет означать, что пользователь запустил тренировку и таймер запущен.

  • STATE_INACTIVE : Указывает на выключенное или приостановленное состояние. Пользователь может взаимодействовать с вашим элементом, находясь в этом состоянии.

    Возвращаясь к примеру с плиткой в ​​фитнес-приложении, плитка в STATE_INACTIVE означает, что пользователь еще не начал тренировку, но может сделать это при желании.

  • STATE_UNAVAILABLE : Указывает на временно недоступное состояние. В этом состоянии пользователь не может взаимодействовать с вашим элементом.

    Например, если значение параметра STATE_UNAVAILABLE равно STATE_UNAVAILABLE это означает, что данный момент плитка недоступна пользователю по какой-либо причине.

Система устанавливает только начальное состояние объекта Tile . Вы сами устанавливаете состояние объекта Tile на протяжении всего его жизненного цикла.

Система может изменять цвет значка плитки и фона в соответствии с состоянием объекта Tile . Объекты Tile со статусом STATE_ACTIVE имеют самый темный цвет, а объекты со STATE_INACTIVE и STATE_UNAVAILABLE становятся все светлее. Точный оттенок зависит от производителя и версии.

Плитка VPN окрашена в соответствии с состоянием объекта.
Рисунок 4. Примеры плитки, окрашенной в соответствии с ее состоянием (активное, неактивное и недоступное состояния соответственно).

Обновите свою плитку

Вы можете обновить свой тайл после получения обратного вызова функции onStartListening() . В зависимости от режима работы тайла, его можно обновить как минимум один раз до получения обратного вызова функции onStopListening() .

В активном режиме вы можете обновить плитку ровно один раз, прежде чем получить обратный вызов функции onStopListening() . В неактивном режиме вы можете обновлять плитку столько раз, сколько хотите, в промежутке между onStartListening() и onStopListening() .

Получить объект Tile можно, вызвав метод getQsTile() . Для обновления определенных полей объекта Tile вызовите следующие методы:

После установки правильных значений в поля объекта Tile необходимо вызвать updateTile() для обновления плитки. Это позволит системе обработать обновленные данные плитки и обновить пользовательский интерфейс.

Котлин

data class StateModel(val enabled: Boolean, val label: String, val icon: Icon)

override fun onStartListening() {
  super.onStartListening()
  val state = getStateFromService()
  qsTile.label = state.label
  qsTile.contentDescription = tile.label
  qsTile.state = if (state.enabled) Tile.STATE_ACTIVE else Tile.STATE_INACTIVE
  qsTile.icon = state.icon
  qsTile.updateTile()
}

Java

public class StateModel {
  final boolean enabled;
  final String label;
  final Icon icon;

  public StateModel(boolean e, String l, Icon i) {
    enabled = e;
    label = l;
    icon = i;
  }
}

@Override
public void onStartListening() {
  super.onStartListening();
  StateModel state = getStateFromService();
  Tile tile = getQsTile();
  tile.setLabel(state.label);
  tile.setContentDescription(state.label);
  tile.setState(state.enabled ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE);
  tile.setIcon(state.icon);
  tile.updateTile();
}

Рукоятки кранов

Пользователи могут нажать на ваш кулон, чтобы запустить действие, если кулон находится в состоянии STATE_ACTIVE или STATE_INACTIVE . Затем система вызывает функцию обратного вызова onClick() вашего приложения.

После получения вызова метода onClick() ваше приложение может запустить диалоговое окно или активность, инициировать фоновые процессы или изменить состояние вашего элемента интерфейса.

Котлин

var clicks = 0
override fun onClick() {
  super.onClick()
  counter++
  qsTile.state = if (counter % 2 == 0) Tile.STATE_ACTIVE else Tile.STATE_INACTIVE
  qsTile.label = "Clicked $counter times"
  qsTile.contentDescription = qsTile.label
  qsTile.updateTile()
}

Java

int clicks = 0;

@Override
public void onClick() {
  super.onClick();
  counter++;
  Tile tile = getQsTile();
  tile.setState((counter % 2 == 0) ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE);
  tile.setLabel("Clicked " + counter + " times");
  tile.setContentDescription(tile.getLabel());
  tile.updateTile();
}

Запустить диалог

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

Запустить действие

startActivityAndCollapse() запускает активность при сворачивании панели. Активности полезны, если необходимо отобразить более подробную информацию, чем в диалоговом окне, или если ваше действие является интерактивным.

Если ваше приложение требует значительного взаимодействия с пользователем, запуск активности следует осуществлять только в крайнем случае. Вместо этого рассмотрите возможность использования диалогового окна или переключателя.

Длительное нажатие на плитку отображает пользователю экран с информацией о приложении . Чтобы изменить это поведение и вместо этого запустить активность для настройки параметров, добавьте <intent-filter> в одну из ваших активностей с параметром ACTION_QS_TILE_PREFERENCES .

Начиная с Android API 28, объект PendingIntent должен иметь флаг Intent.FLAG_ACTIVITY_NEW_TASK :

if (Build.VERSION.SDK_INT >= 28) {
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}

В качестве альтернативы вы можете добавить этот флаг в файл AndroidManifest.xml в разделе, посвященном конкретному Activity .

Отметьте свой элемент как переключаемый.

Мы рекомендуем помечать ваш элемент как переключаемый, если он функционирует преимущественно как двухпозиционный переключатель (что является наиболее распространенным поведением элементов). Это помогает предоставить операционной системе информацию о поведении элемента и улучшить общую доступность.

Установите метаданные TOGGLEABLE_TILE в true , чтобы пометить ваш плиточный элемент как переключаемый.

<service ...>
  <meta-data android:name="android.service.quicksettings.TOGGLEABLE_TILE"
    android:value="true" />
</service>

Выполняйте только безопасные действия на надежно заблокированных устройствах.

На заблокированных устройствах ваш кулон может отображаться поверх экрана блокировки. Если кулон содержит конфиденциальную информацию, проверьте значение isSecure() чтобы определить, находится ли устройство в безопасном состоянии, и ваш TileService должен соответствующим образом изменить свое поведение.

Если выполнение действия с плиткой безопасно при заблокированном экране, используйте startActivity() для запуска активности поверх экрана блокировки.

Если действие с плиткой небезопасно, используйте unlockAndRun() , чтобы предложить пользователю разблокировать устройство. В случае успеха система выполнит объект Runnable , переданный в этот метод.

Классифицируйте свою плитку.

Для улучшения пользовательского опыта в быстрых настройках вы можете классифицировать свои плитки. Система организует плитки по категориям, таким как «Подключение», «Дисплей» и «Конфиденциальность». Система использует эти категории для сортировки и группировки плиток в режиме редактирования быстрых настроек, что упрощает их поиск и управление пользователями.

Выполнение

Чтобы указать категорию для вашего TileService , добавьте поле метаданных в объявление сервиса в файле AndroidManifest.xml :

  • В файле AndroidManifest.xml , внутри элемента <service> для вашего TileService , добавьте элемент <meta-data> .
  • android:name : Установите это значение равным android.service.quicksettings.TILE_CATEGORY .
  • android:value : Присвойте одну из предопределенных констант категории, например android.service.quicksettings.CATEGORY_CONNECTIVITY или android.service.quicksettings.CATEGORY_DISPLAY .

Как показано в следующем примере:

<service
    android:name=".MyConnectivityTileService"
    [...]
    >
    <meta-data android:name="android.service.quicksettings.TILE_CATEGORY"
        android:value="android.service.quicksettings.CATEGORY_CONNECTIVITY" />
</service>

API предоставляет набор предопределенных категорий на выбор. Эти категории определены как строковые константы в классе TileService .

Если категория не указана, система автоматически присваивает категорию по умолчанию:

  • Из системных приложений: Для плиток, являющихся частью системного приложения.
  • Из установленных вами приложений: Для плиток из приложений, установленных пользователем.

Хотя в быстрых настройках устройств Google Pixel используются категории, производители могут либо использовать, либо игнорировать эту информацию о категориях в своих системных интерфейсах.

Предложите пользователю добавить вашу плитку.

Для добавления плитки вручную пользователям необходимо выполнить несколько шагов:

  1. Проведите пальцем вниз, чтобы открыть панель быстрых настроек.
  2. Нажмите кнопку редактирования.
  3. Пролистайте все плитки на их устройстве, пока они не найдут вашу плитку.
  4. Удерживая плитку нажатой, перетащите её в список активных плиток.

Пользователь также может в любой момент переместить или удалить свой фрагмент текста.

Начиная с Android 13, вы можете использовать метод requestAddTileService() , чтобы значительно упростить добавление вашего плитки на устройство. Этот метод запрашивает у пользователей запрос на быстрое добавление вашей плитки непосредственно в панель быстрых настроек. Запрос включает название приложения, предоставленную метку и значок.

Запрос API быстрого размещения настроек
Рисунок 5. Подсказка API для быстрых настроек размещения.
public void requestAddTileService (
  ComponentName tileServiceComponentName,
  CharSequence tileLabel,
  Icon icon,
  Executor resultExecutor,
  Consumer<Integer> resultCallback
)

Функция обратного вызова содержит информацию о том, был ли добавлен фрагмент кода, не был ли он добавлен, существовал ли он уже на объекте или произошла ли какая-либо ошибка.

Принимая решение о том, когда и как часто запрашивать у пользователей подтверждение, руководствуйтесь собственным усмотрением. Мы рекомендуем вызывать requestAddTileService() только в контексте — например, когда пользователь впервые взаимодействует с функцией, которую обеспечивает ваш плиточный модуль.

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