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

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

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

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

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

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

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

Приложение с постоянно включенным экраном – это приложение, которое показывает контент в интерактивном и фоновом режимах.

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

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

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

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

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

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

Как настроить поведение устройства в режиме ожидания

Независимо от поведения системы по умолчанию, во всех версиях Wear OS можно настроить внешний вид или поведение приложения в фоновом режиме, используя LocalAmbientModeManager для отслеживания переходов между состояниями в Jetpack Compose на Wear OS. Этот режим представлен узлом "Ambiactive Mode" на приведенной ниже блок-схеме.

Наблюдение за состоянием окружающей среды

Чтобы реагировать на события спящего режима в Jetpack Compose на Wear OS:

  1. Используйте LocalAmbientModeManager, чтобы отслеживать изменения статуса режима Ambient Mode и соответствующим образом корректировать композиции. Настройте компонент так, чтобы в состоянии AmbientMode.Ambient он переходил в режим пониженного энергопотребления, а в состоянии AmbientMode.Interactive – возвращался к полнофункциональному интерактивному дисплею.

    // In a production application, the AmbientModeManager should be instantiated and provided at
    // the highest level of the Compose hierarchy (typically in the host Activity's setContent
    // block) using a CompositionLocalProvider. This ensures proper lifecycle management and
    // broad accessibility.
    
    // For this self-contained demo, AmbientModeManager is created and provided locally:
    val activityAmbientModeManager = rememberAmbientModeManager()
    CompositionLocalProvider(LocalAmbientModeManager provides activityAmbientModeManager) {
        val ambientModeManager = LocalAmbientModeManager.current
        val ambientMode = ambientModeManager?.currentAmbientMode
    
        if (ambientModeManager != null) {
            ambientModeManager.AmbientTickEffect {
                // While device is in ambient mode, update properties every minute or so
                // ...
            }
        }
    
        // ...
            val ambientModeName =
                when (ambientMode) {
                    is AmbientMode.Interactive -> "Interactive"
                    is AmbientMode.Ambient -> "Ambient"
                    else -> "Unknown"
                }
    
            Text(text = "$ambientModeName Mode")
            // ...
    }

Блок-схема работы функции "Окружающий звук"

На следующей схеме показано, как система определяет поведение в режиме ожидания в зависимости от версии Wear OS, значения targetSdkVersion в приложении и того, отслеживает ли приложение переходы в режим ожидания (с помощью LocalAmbientModeManager или AmbientLifecycleCallback).

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

Как управлять временем включения экрана

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

Как запретить возвращаться к циферблату с помощью функции "Текущая активность" или "Динамическое обновление"

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

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

В Wear OS 7 и более поздних версий вместо Ongoing Activity можно использовать Live Update. Для обеспечения обратной совместимости продолжайте поддерживать 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()

Как не выключать экран и не переходить в режим ожидания

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

Рекомендации для спящего режима

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

  • Уменьшите визуальный беспорядок и энергопотребление. Простой и минималистичный интерфейс сообщает пользователю, что приложение работает в режиме энергосбережения, и позволяет значительно экономить заряд батареи за счет ограничения ярких пикселей.
    • Не менее 85% экрана должно быть черным (соотношение включенных пикселей – 15% или меньше).
    • Показывайте только самую важную информацию, а второстепенные сведения размещайте на интерактивном дисплее.
    • Используйте контуры для больших значков или кнопок вместо сплошной заливки, регулируя контрастность контура, чтобы элементы оставались разборчивыми, не вызывая мерцания экрана или выгорания.
    • Не используйте большие блоки сплошного цвета, а также нефункциональные изображения бренда или фона.
  • Как обрабатывать устаревшие динамические данные и анимацию
    • Обратный вызов AmbientTickEffect вызывается только периодически, обычно раз в минуту, чтобы экономить заряд батареи. Из-за этого ограничения данные, которые часто меняются, например показания секундомера, пульс или расстояние, пройденное во время тренировки, устаревают между обновлениями. Чтобы избежать показа вводящей в заблуждение и неверной информации, замените эти динамические значения статическим контентом, например --.
    • Удалите индикаторы прогресса, которые требуют частого обновления, чтобы быть полезными в текущем контексте или ситуации пользователя, например круги прогресса для коротких сеансов воспроизведения медиаконтента. Индикаторы прогресса можно оставить, если обновления раз в минуту по-прежнему полезны, например для длинных таймеров.
    • Приостановите или удалите все анимации в фоновом режиме, в том числе анимированные индикаторы выполнения и бегущую строку.
  • Сохраняйте единообразие макета
    • Чтобы переход между режимами Интерактивный и Фоновый был плавным, элементы должны находиться в одном и том же положении. Интерактивные элементы интерфейса, например кнопки, могут оставаться видимыми, поскольку современные устройства Wear OS поддерживают сенсорный ввод в режиме ожидания.
    • Всегда показывать текущее время. В Jetpack Compose для Wear OS компонент TimeText (по умолчанию включен в ScreenScaffold) учитывает окружающую обстановку и автоматически обновляется раз в минуту в режиме ожидания.
  • Учитывайте контекст
    • Если пользователь находился на экране настроек или конфигурации, когда устройство перешло в режим ожидания, вместо экрана настроек покажите более подходящий экран из вашего приложения.
  • Как обрабатывать требования к определенным устройствам
    • Когда функция currentAmbientMode возвращает значение AmbientMode.Ambient:
      • Если параметр isLowBitAmbientSupported true, дисплей поддерживает меньше бит на цвет в режиме заставки. Отключите сглаживание (например, в пользовательских операциях рисования Canvas или Paint) и по возможности избегайте цветовых градиентов.
      • Если значение isBurnInProtectionRequired – true, система периодически сдвигает контент на экране, чтобы предотвратить выгорание пикселей. Не перемещайте элементы интерфейса вручную. Вместо этого избегайте белых областей и размещайте важные элементы интерфейса на расстоянии не менее 10 пикселей от края экрана, чтобы они не обрезались при смещении дисплея. (Стандартные макеты Wear Compose, например ScreenScaffold, уже обеспечивают достаточное внутреннее поле.)

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

При разработке или тестировании приложения, которое работает в режиме ожидания, могут быть полезны следующие команды: 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 и не должно заменяться циферблатом.

Чтобы этого избежать, разработчику необходимо:

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

Полную реализацию можно найти в примере приложения для тренировок на GitHub. В этом примере также показано, как использовать компонуемый элемент LocalAmbientModeManager, чтобы упростить обработку режима Ambient в Compose.