Постоянно включенные приложения и режим окружающей среды системы

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

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

Ключевые понятия

Когда приложение Wear OS отображается на весь экран, оно находится в одном из двух энергосберегающих состояний:

  • Интерактивный режим : состояние высокой мощности, при котором экран имеет максимальную яркость, что позволяет пользователю взаимодействовать с экраном в полном объеме.
  • Ambient : Режим пониженного энергопотребления, при котором дисплей затемняется для экономии энергии. В этом режиме пользовательский интерфейс приложения по-прежнему занимает весь экран, но система может изменять его внешний вид, размывая его или накладывая другой контент, например, время. Этот режим также называется режимом Ambient .

Переход между этими состояниями контролируется операционной системой.

Приложение с постоянной активацией (Always-On App) — это приложение, которое отображает контент как в интерактивном , так и в фоновом режимах.

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

Переходы в системе и поведение по умолчанию

Когда приложение находится на переднем плане, система управляет переходами между состояниями энергопотребления на основе двух тайм-аутов, срабатывающих при бездействии пользователя.

  • Тайм-аут №1: Переход из интерактивного состояния в фоновое: После периода бездействия пользователя устройство переходит в фоновое состояние.
  • Тайм-аут №2: Возврат к циферблату: После очередного периода бездействия система может скрыть текущее приложение и отобразить циферблат.

Сразу после первого перехода системы в состояние Ambient , поведение по умолчанию зависит от версии Wear OS и конфигурации вашего приложения:

  • В Wear OS 5 и более ранних версиях система отображает размытый снимок экрана приостановленного приложения с наложенным поверх него временем. Это состояние представлено узлом «AOD Lite» на следующей блок-схеме .
  • В Wear OS 6 и более поздних версиях , если приложение использует SDK 36 или более новую версию, оно считается постоянно включенным. Дисплей приглушается, но приложение продолжает работать и остается видимым. (Обновления могут происходить даже раз в минуту.) Это состояние представлено узлом «Global AOD» на следующей блок-схеме .

Настройка поведения для состояния окружающей среды

Независимо от поведения системы по умолчанию, во всех версиях Wear OS вы можете настроить внешний вид или поведение вашего приложения в режиме Ambient , используя AmbientLifecycleObserver для прослушивания обратных вызовов при переходах между состояниями. Это состояние представлено узлом «Ambiactive Mode» на следующей блок-схеме .

Используйте AmbientLifecycleObserver

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

  1. Реализуйте интерфейс AmbientLifecycleObserver.AmbientLifecycleCallback . Используйте метод onEnterAmbient() для настройки пользовательского интерфейса в режиме энергосбережения и onExitAmbient() для восстановления полноценного интерактивного отображения.

    val ambientCallback = object : AmbientLifecycleObserver.AmbientLifecycleCallback {
        override fun onEnterAmbient(ambientDetails: AmbientLifecycleObserver.AmbientDetails) {
            // ... Called when moving from interactive mode into ambient mode.
            // Adjust UI for low-power state: dim colors, hide non-essential elements.
        }
    
        override fun onExitAmbient() {
            // ... Called when leaving ambient mode, back into interactive mode.
            // Restore full UI.
        }
    
        override fun onUpdateAmbient() {
            // ... Called by the system periodically (typically once per minute)
            // to allow the app to update its display while in ambient mode.
        }
    }

  2. Создайте объект AmbientLifecycleObserver и зарегистрируйте его в жизненном цикле вашей активности или составного объекта.

    private val ambientObserver = AmbientLifecycleObserver(activity, ambientCallback)
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        lifecycle.addObserver(ambientObserver)
    
        // ...
    }

  3. Вызовите removeObserver() для удаления наблюдателя в onDestroy() .

    override fun onDestroy() {
        super.onDestroy()
        lifecycle.removeObserver(ambientObserver)
    
        // ...
    }

Для разработчиков, использующих Jetpack Compose, библиотека Horologist предоставляет полезную утилиту — AmbientAware composable, которая упрощает реализацию этого шаблона.

Ambient-ware TimeText

В качестве исключения из требования использования пользовательского наблюдателя, в Wear OS 6 виджет TimeText учитывает состояние окружающей среды. Он автоматически обновляется раз в минуту, когда устройство находится в режиме Ambient , без какого-либо дополнительного кода.

Блок-схема поведения окружающей среды

Следующая блок-схема иллюстрирует, как система определяет поведение в окружающей среде на основе версии Wear OS устройства, targetSdkVersion вашего приложения и того, реализует ли оно AmbientLifecycleCallback .

Блок-схема, иллюстрирующая логику принятия решений для режима Ambient Mode в Wear OS. Она показывает, как версия ОС устройства и конфигурация приложения определяют один из трех результатов: размытое наложение, глобальный AOD или управляемый приложением режим Ambiactive Mode.
Рисунок 1. Блок-схема, иллюстрирующая логику принятия решений для режима работы Wear OS в фоновом режиме.

Управление длительностью включения экрана

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

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

После некоторого времени пребывания в фоновом режиме (тайм-аут №2) система обычно возвращается к циферблату часов. Пользователь может настроить продолжительность тайм-аута в системных настройках. В некоторых случаях, например, когда пользователь отслеживает тренировку, приложению может потребоваться оставаться видимым дольше.

В Wear OS 5 и более поздних версиях это можно предотвратить, реализовав индикатор текущей активности . Если ваше приложение отображает информацию о текущей задаче пользователя, например, о тренировке, вы можете использовать API индикатора текущей активности , чтобы приложение оставалось видимым до завершения задачи. Если пользователь вручную возвращается к циферблату, индикатор текущей активности предоставляет ему возможность одним касанием вернуться в ваше приложение.

В качестве альтернативы, на Wear OS 7 и более поздних версиях вы можете использовать Live Update вместо Ongoing Activity. Для обеспечения обратной совместимости продолжайте поддерживать Ongoing Activity на устройствах под управлением Wear OS 6 или более ранних версий.

Для реализации этого, намерение касания текущего уведомления должно указывать на вашу постоянно активную активность, как показано в следующем фрагменте кода:

val activityIntent =
    Intent(this, AlwaysOnActivity::class.java).apply {
        flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
    }

val pendingIntent =
    PendingIntent.getActivity(
        this,
        0,
        activityIntent,
        PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
    )

val notificationBuilder =
    NotificationCompat.Builder(this, CHANNEL_ID)
        // ...
        // ...
        .setOngoing(true)

// ...

val ongoingActivity =
    OngoingActivity.Builder(applicationContext, NOTIFICATION_ID, notificationBuilder)
        // ...
        // ...
        .setTouchIntent(pendingIntent)
        .build()

ongoingActivity.apply(applicationContext)

val notification = notificationBuilder.build()

Оставьте экран включенным и предотвратите появление состояния Ambient State.

В редких случаях может потребоваться полностью предотвратить переход устройства в состояние Ambient . То есть, чтобы избежать таймаута №1. Для этого можно использовать оконный флаг FLAG_KEEP_SCREEN_ON . Он работает как блокировка пробуждения, удерживая устройство в интерактивном состоянии. Используйте его с особой осторожностью, так как он серьезно влияет на время работы от батареи.

Рекомендации по использованию режима Ambient

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

  • Уменьшите визуальный беспорядок и сэкономьте энергию дисплея. Чистый, минималистичный пользовательский интерфейс сигнализирует пользователю о том, что приложение находится в режиме низкого энергопотребления, и значительно экономит заряд батареи за счет ограничения количества ярких пикселей.
    • Не менее 85% экрана должно быть черным.
    • Отображайте только самую важную информацию, вынося второстепенные детали на интерактивный экран.
    • Для больших значков или кнопок используйте контуры, а не сплошную заливку.
    • Избегайте больших блоков сплошного цвета, а также нефункциональной фирменной символики или фоновых изображений.
  • Обработка устаревших динамических данных
    • Функция обратного вызова onUpdateAmbient() вызывается лишь периодически — обычно раз в минуту — для экономии энергии. Из-за этого ограничения любые часто изменяющиеся данные — такие как показания секундомера, частота сердечных сокращений или пройденное расстояние — устаревают между обновлениями. Чтобы избежать отображения вводящей в заблуждение и некорректной информации, отслеживайте вызов функции обратного вызова onEnterAmbient и заменяйте эти текущие значения статическим содержимым-заполнителем, например -- .
  • Поддерживайте единообразную планировку.
    • Для обеспечения плавного перехода сохраняйте элементы в одном и том же положении как в интерактивном , так и в фоновом режимах.
    • Всегда указывайте время.
  • Учитывайте контекст.
    • Если пользователь находился на экране настроек или конфигурации, когда устройство перешло в режим ожидания, рассмотрите возможность показа более релевантного экрана из вашего приложения вместо окна настроек.
  • Обработка требований, специфичных для конкретного устройства.
    • В объекте AmbientDetails , передаваемом в onEnterAmbient() :
      • Если deviceHasLowBitAmbient имеет true , отключите сглаживание, где это возможно.
      • Если burnInProtectionRequired имеет значение true , периодически слегка смещайте элементы пользовательского интерфейса и избегайте сплошных белых областей, чтобы предотвратить выгорание экрана.

Отладка и тестирование

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

# put device in ambient mode if the always on display is enabled in settings
# (and not disabled by other settings, such as theatre mode)
$ adb shell input keyevent KEYCODE_SLEEP

# put device in interactive mode
$ adb shell input keyevent KEYCODE_WAKEUP

Пример: приложение для тренировок

Рассмотрим приложение для тренировок, которому необходимо отображать показатели пользователю на протяжении всей тренировки. Приложение должно оставаться видимым при переходах между состояниями Ambient State и не заменяться циферблатом часов.

Для достижения этой цели разработчику следует выполнить следующие действия:

  1. Реализуйте AmbientLifecycleObserver для обработки изменений пользовательского интерфейса между интерактивным и окружающим состояниями, таких как затемнение экрана и удаление несущественных данных.
  2. Создайте новый, энергоэффективный макет для состояния Ambient , соответствующий передовым практикам.
  3. Используйте API текущей активности (или обновления в реальном времени в Wear OS 7 и выше) на протяжении всей тренировки, чтобы предотвратить возврат системы к циферблату часов.

Полную реализацию см. в примере упражнения на основе Compose на GitHub. Этот пример также демонстрирует использование компонента AmbientAware из библиотеки Horologist для упрощения обработки окружающего режима в Compose.