En esta guía, se explica cómo hacer que tu app esté siempre activa, cómo reaccionar a las transiciones de estado de energía y cómo administrar el comportamiento de la aplicación para brindar una buena experiencia del usuario y, al mismo tiempo, conservar la batería.
Hacer que una app sea visible constantemente afecta significativamente la duración de la batería, por lo que debes tener en cuenta el impacto en el consumo de energía cuando agregues esta función.
Conceptos clave
Cuando una app de Wear OS se muestra en pantalla completa, se encuentra en uno de los siguientes dos estados de energía:
- Interactiva: Es un estado de alta potencia en el que la pantalla está con el brillo al máximo, lo que permite la interacción completa del usuario.
- Ambiente: Es un estado de bajo consumo en el que la pantalla se atenúa para ahorrar batería. En este estado, la IU de tu app sigue ocupando toda la pantalla, pero el sistema podría alterar su apariencia desenfocándola o superponiendo contenido, como la hora. Esto también se conoce como Modo ambiente.
El sistema operativo controla la transición entre estos estados.
Una app siempre activa es una aplicación que muestra contenido en los estados Interactivo y Ambiente.
Cuando una app siempre activa sigue mostrando su propia IU mientras el dispositivo está en el estado de ambiente de bajo consumo, se dice que está en modo ambiactivo.
Transiciones del sistema y comportamiento predeterminado
Cuando una app está en primer plano, el sistema administra las transiciones de estado de energía según dos tiempos de espera que se activan por la inactividad del usuario.
- Tiempo de espera 1: Estado interactivo a ambiente: Después de un período de inactividad del usuario, el dispositivo entra en el estado Ambiente.
- Tiempo de espera núm. 2: Regresar a la cara de reloj: Después de un período de inactividad adicional, es posible que el sistema oculte la app actual y muestre la cara de reloj.
Inmediatamente después de que el sistema pasa por la primera transición al estado Ambient, el comportamiento predeterminado depende de la versión de Wear OS y de la configuración de tu app:
- En Wear OS 5 y versiones anteriores, el sistema muestra una captura de pantalla desenfocada de la aplicación en pausa, con la hora superpuesta.
- En Wear OS 6 y versiones posteriores, si una app se segmenta para el SDK 36 o una versión posterior, se considera que está siempre activa. La pantalla se atenúa, pero la aplicación sigue ejecutándose y permanece visible. (Las actualizaciones pueden ser tan poco frecuentes como una vez por minuto).
Personaliza el comportamiento del estado Ambiente
Independientemente del comportamiento predeterminado del sistema, en todas las versiones de Wear OS, puedes personalizar la apariencia o el comportamiento de tu app mientras se encuentra en el estado Ambiental con AmbientLifecycleObserver
para detectar devoluciones de llamada en las transiciones de estado.
Usa AmbientLifecycleObserver
Para reaccionar a los eventos del modo ambiente, usa la clase AmbientLifecycleObserver
:
Implementa la interfaz
AmbientLifecycleObserver.AmbientLifecycleCallback
. Usa el métodoonEnterAmbient()
para ajustar la IU al estado de bajo consumo yonExitAmbient()
para restablecerla a la pantalla interactiva completa.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. } }
Crea un
AmbientLifecycleObserver
y regístralo en el ciclo de vida de tu actividad o elemento componible.private val ambientObserver = AmbientLifecycleObserver(activity, ambientCallback) override fun onCreate(savedInstanceState: Bundle) { super.onCreate(savedInstanceState) lifecycle.addObserver(ambientObserver) // ... }
Llama a
removeObserver()
para quitar el observador enonDestroy()
.
Para los desarrolladores que usan Jetpack Compose, la biblioteca de Horologist proporciona una utilidad útil, el elemento AmbientAware
componible, que simplifica la implementación de este patrón.
TimeText que tiene en cuenta el ambiente
Como excepción al requisito de un observador personalizado, en Wear OS 6, el widget TimeText
es compatible con el modo ambiente. Se actualiza automáticamente una vez por minuto cuando el dispositivo está en el estado Ambiental sin ningún código adicional.
Cómo controlar la duración de la pantalla encendida
En las siguientes secciones, se describe cómo administrar el tiempo que tu app permanece en la pantalla.
Cómo evitar volver a la cara de reloj con una actividad en curso
Después de un período en el estado Ambiental (Tiempo de espera núm. 2), el sistema suele volver a la cara de reloj. El usuario puede configurar la duración del tiempo de espera en la configuración del sistema. En algunos casos de uso, como cuando un usuario hace un seguimiento de su entrenamiento, es posible que una app deba permanecer visible durante más tiempo.
En Wear OS 5 y versiones posteriores, puedes evitar esto implementando una Ongoing Activity. Si tu app muestra información sobre una tarea del usuario en curso, como una sesión de entrenamiento, puedes usar la API de Ongoing Activity para mantener la app visible hasta que finalice la tarea. Si un usuario regresa manualmente a la cara de reloj, el indicador de actividad en curso le proporciona una forma de un solo toque para regresar a tu app.
Para implementar esto, el intent de toque de la notificación en curso debe apuntar a tu actividad siempre activa, como se muestra en el siguiente fragmento de código:
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() }
Mantener la pantalla encendida y evitar el estado de Modo ambiente
En casos excepcionales, es posible que debas evitar por completo que el dispositivo entre en el estado Ambient. Es decir, para evitar el tiempo de espera 1. Para ello, puedes usar la marca de ventana FLAG_KEEP_SCREEN_ON
. Esto funciona como un bloqueo de activación, lo que mantiene el dispositivo en el estado Interactivo. Usa esta opción con extrema precaución, ya que afecta gravemente la duración de la batería.
Recomendaciones para el Modo ambiente
Para proporcionar la mejor experiencia del usuario y ahorrar energía en el modo Ambiente, sigue estos lineamientos de diseño. Estas recomendaciones priorizan una experiencia del usuario clara, ya que evitan la información engañosa y reducen el desorden visual, a la vez que optimizan la potencia de visualización.
- Reduce el desorden visual y muestra la potencia. Una IU limpia y minimalista le indica al usuario que la app se encuentra en un estado de bajo consumo y ahorra una cantidad significativa de batería limitando los píxeles brillantes.
- Mantén al menos el 85% de la pantalla en negro.
- Muestra solo la información más importante y traslada los detalles secundarios a la pantalla interactiva.
- Usa contornos para los íconos o botones grandes en lugar de rellenos sólidos.
- Evita los bloques grandes de color sólido y las imágenes de marca o de fondo que no sean funcionales.
- Cómo controlar datos dinámicos obsoletos
- La devolución de llamada
onUpdateAmbient()
se invoca solo de forma periódica (por lo general, una vez por minuto) para ahorrar energía. Debido a esta limitación, cualquier dato que cambie con frecuencia (como un cronómetro, el ritmo cardíaco o la distancia de entrenamiento) se vuelve obsoleto entre las actualizaciones. Para evitar mostrar información incorrecta y engañosa, escucha la devolución de llamadaonEnterAmbient
y reemplaza estos valores en tiempo real por contenido de marcador de posición estático, como--
.
- La devolución de llamada
- Mantén un diseño coherente
- Mantén los elementos en la misma posición en los modos Interactivo y Ambiente para crear una transición fluida.
- Mostrar siempre la hora
- Ten en cuenta el contexto
- Si el usuario estaba en una pantalla de configuración cuando el dispositivo entró en el modo ambiente, considera mostrar una pantalla más relevante de tu app en lugar de la vista de configuración.
- Cómo controlar los requisitos específicos del dispositivo
- En el objeto
AmbientDetails
que se pasa aonEnterAmbient()
, haz lo siguiente:- Si
deviceHasLowBitAmbient
estrue
, inhabilita el suavizado de contorno siempre que sea posible. - Si
burnInProtectionRequired
estrue
, cambia periódicamente los elementos de la IU ligeramente y evita las áreas blancas sólidas para evitar la protección de pantalla.
- Si
- En el objeto
Depuración y pruebas
Estos comandos de adb
pueden ser útiles cuando desarrollas o pruebas el comportamiento de tu app cuando el dispositivo está en modo ambiente:
# 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
Ejemplo: App de entrenamiento
Considera una app de entrenamiento que necesita mostrar métricas al usuario durante toda la duración de su sesión de ejercicio. La app debe permanecer visible durante las transiciones de estado de Ambient y no debe reemplazarse por la cara de reloj.
Para lograrlo, el desarrollador debe hacer lo siguiente:
- Implementa un
AmbientLifecycleObserver
para controlar los cambios en la IU entre los estados Interactivo y Ambiental, como atenuar la pantalla y quitar los datos no esenciales. - Crea un nuevo diseño de bajo consumo para el estado Ambient que siga las prácticas recomendadas.
- Usa la API de Ongoing Activity durante el entrenamiento para evitar que el sistema vuelva a la cara de reloj.
Para ver una implementación completa, consulta la muestra de ejercicio basada en Compose en GitHub. Este ejemplo también muestra el uso del elemento AmbientAware
componible de la biblioteca Horologist para simplificar el control del modo ambiente en Compose.