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

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

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

Ключевые концепции

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

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

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

Always-On App — это приложение, которое отображает контент как в интерактивном , так и в фоновом режиме.

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

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

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

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

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

  • На Wear OS 5 и ниже система отображает размытый снимок экрана приостановленного приложения с наложенным поверх временем.
  • На Wear OS 6 и более поздних версиях , если приложение использует SDK 36 или более новую версию, оно считается постоянно активным. Дисплей затемняется, но приложение продолжает работать и остаётся видимым. (Обновления могут происходить не чаще раза в минуту.)

Настройте поведение для состояния Ambient

Независимо от поведения системы по умолчанию, во всех версиях Wear OS вы можете настроить внешний вид или поведение своего приложения в состоянии Ambient , используя 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() .

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

TimeText с учетом окружающей среды

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

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

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

Запретить возврат к циферблату с текущей активностью

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

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

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

private fun createNotification(): Notification {
    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)

    return notificationBuilder.build()
}

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

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

Рекомендации для режима Ambient

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

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

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

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

# 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. Реализуйте AmbientLifecycleObserver для обработки изменений пользовательского интерфейса между интерактивным и фоновым состояниями, таких как затемнение экрана и удаление ненужных данных.
  2. Создайте новый маломощный макет для состояния Ambient , соответствующий лучшим практикам.
  3. Используйте API текущей активности на протяжении всей тренировки, чтобы предотвратить возврат системы к циферблату часов.

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