App sempre attive e modalità Ambient di sistema

Questa guida spiega come rendere la tua app sempre attiva, come reagire alle transizioni dello stato di alimentazione e come gestire il comportamento dell'applicazione per offrire un'esperienza utente ottimale risparmiando la batteria.

La visibilità costante di un'app influisce notevolmente sulla durata della batteria, quindi prendi in considerazione l'impatto sull'alimentazione quando aggiungi questa funzionalità.

Concetti principali

Quando un'app Wear OS viene visualizzata a schermo intero, è in uno dei due stati di riattivazione:

  • Interattivo: uno stato ad alta potenza in cui lo schermo è a luminosità massima, consentendo un'interazione completa dell'utente.
  • Ambient: uno stato a basso consumo in cui il display si attenua per risparmiare energia. In questo stato, l'interfaccia utente dell'app occupa ancora lo schermo intero, ma il sistema potrebbe alterarne l'aspetto sfocandola o sovrapponendo contenuti come l'ora. Questa modalità è nota anche come modalità Ambient.

Il sistema operativo controlla la transizione tra questi stati.

Un'app sempre attiva è un'applicazione che mostra i contenuti sia nello stato Interattivo sia in quello Ambient.

Quando un'app sempre attiva continua a mostrare la propria UI mentre il dispositivo è in stato Ambient a basso consumo, si dice che è in modalità ambiattiva.

Transizioni di sistema e comportamento predefinito

Quando un'app è in primo piano, il sistema gestisce le transizioni dello stato di alimentazione in base a due timeout attivati dall'inattività dell'utente.

  • Timeout 1: stato Interattivo a stato Ambient:dopo un periodo di inattività dell'utente, il dispositivo entra nello stato Ambient.
  • Timeout 2: ritorno al quadrante:dopo un ulteriore periodo di inattività, il sistema potrebbe nascondere l'app corrente e mostrare il quadrante.

Immediatamente dopo che il sistema ha eseguito la prima transizione allo stato Ambient, il comportamento predefinito dipende dalla versione di Wear OS e dalla configurazione dell'app:

  • Su Wear OS 5 e versioni precedenti, il sistema mostra uno screenshot sfocato della tua applicazione messa in pausa, con l'ora sovrapposta.
  • Su Wear OS 6 e versioni successive, se un'app ha come target SDK 36 o versioni successive, è considerata sempre attiva. Il display è attenuato, ma l'applicazione continua a funzionare e rimane visibile. Gli aggiornamenti possono essere anche una volta al minuto.

Personalizzare il comportamento per lo stato Ambient

Indipendentemente dal comportamento predefinito del sistema, su tutte le versioni di Wear OS puoi personalizzare l'aspetto o il comportamento della tua app nello stato Ambient utilizzando AmbientLifecycleObserver per ascoltare i callback nelle transizioni di stato.

Utilizzare AmbientLifecycleObserver

Per reagire agli eventi della modalità Ambient, utilizza la classe AmbientLifecycleObserver:

  1. Implementa l'interfaccia AmbientLifecycleObserver.AmbientLifecycleCallback. Utilizza il metodo onEnterAmbient() per regolare l'interfaccia utente per lo stato di basso consumo e onExitAmbient() per ripristinare il display completamente interattivo.

    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. Crea un AmbientLifecycleObserver e registralo con il ciclo di vita della tua attività o del tuo composable.

    private val ambientObserver = AmbientLifecycleObserver(activity, ambientCallback)
    
    override fun onCreate(savedInstanceState: Bundle) {
        super.onCreate(savedInstanceState)
        lifecycle.addObserver(ambientObserver)
    
        // ...
    }
    
  3. Chiama removeObserver() per rimuovere l'osservatore in onDestroy().

Per gli sviluppatori che utilizzano Jetpack Compose, la libreria Horologist fornisce un'utilità utile, il composable AmbientAware, che semplifica l'implementazione di questo pattern.

TimeText sensibile all'ambiente

Come eccezione al requisito di un osservatore personalizzato, su Wear OS 6 il widget TimeText è sensibile all'ambiente. Si aggiorna automaticamente una volta al minuto quando il dispositivo è in stato Ambient senza alcun codice aggiuntivo.

Controllare la durata dello schermo attivo

Le sezioni seguenti descrivono come gestire il tempo di permanenza dell'app sullo schermo.

Impedire il ritorno al quadrante con un'attività in corso

Dopo un determinato periodo di tempo nello stato Ambient (timeout 2), in genere il sistema torna al quadrante. L'utente può configurare la durata del timeout nelle impostazioni di sistema. Per determinati casi d'uso, ad esempio un utente che monitora un'attività fisica, un'app potrebbe dover rimanere visibile più a lungo.

Su Wear OS 5 e versioni successive, puoi evitarlo implementando un'attività in corso. Se la tua app mostra informazioni su un'attività in corso dell'utente, come una sessione di allenamento, puoi utilizzare l'API Attività in corso per mantenere visibile la tua app fino al termine dell'attività. Se un utente torna manualmente al quadrante, l'indicatore dell'attività in corso consente di tornare alla tua app con un solo tocco.

Per implementare questa funzionalità, l'intent tocco della notifica in corso deve puntare all'attività sempre attiva, come mostrato nel seguente snippet di codice:

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()
}

Mantenere lo schermo attivo ed evitare lo stato Ambient

In rari casi, potrebbe essere necessario impedire completamente al dispositivo di attivare lo stato Ambient. In altre parole, per evitare il timeout 1. Per farlo, puoi utilizzare il flag della finestra FLAG_KEEP_SCREEN_ON. Funziona come un blocco di attivazione, mantenendo il dispositivo nello stato Interattivo. Utilizzala con estrema cautela in quanto influisce notevolmente sulla durata della batteria.

Suggerimenti per la modalità Ambient

Per offrire la migliore esperienza utente e risparmiare energia in modalità Ambient, segui queste linee guida per il design.

  • Utilizzare un display minimalista a basso consumo
    • Mantieni almeno l'85% dello schermo nero.
    • Utilizza contorni per icone o pulsanti di grandi dimensioni anziché riempimenti solidi.
    • Mostra solo le informazioni più importanti, spostando i dettagli secondari nella visualizzazione interattiva.
    • Evita grandi blocchi di colore a tinta unita e branding o immagini di sfondo non funzionali.
  • Assicurati che i contenuti vengano aggiornati in modo appropriato
    • Per i dati che cambiano di frequente, come un cronometro, la distanza dell'allenamento o il tempo, mostra contenuti segnaposto come -- per evitare di dare l'impressione che i contenuti siano aggiornati.
    • Rimuovi gli indicatori di avanzamento che si aggiornano continuamente, ad esempio per gli annunci con conto alla rovescia e le sessioni multimediali.
    • Il callback onUpdateAmbient() deve essere utilizzato solo per aggiornamenti essenziali, in genere una volta al minuto.
  • Mantieni un layout coerente
    • Mantieni gli elementi nella stessa posizione nelle modalità Interattiva e Ambiente per creare una transizione fluida.
    • Mostra sempre l'ora.
  • Sii sensibile al contesto
    • Se l'utente si trovava in una schermata di impostazioni o configurazione quando il dispositivo è passato alla modalità Ambient, valuta la possibilità di mostrare una schermata più pertinente della tua app anziché la visualizzazione delle impostazioni.
  • Gestire i requisiti specifici del dispositivo
    • Nell'oggetto AmbientDetails passato a onEnterAmbient():
      • Se deviceHasLowBitAmbient è true, disattiva l'anti-aliasing ove possibile.
      • Se burnInProtectionRequired è true, sposta periodicamente leggermente gli elementi dell'interfaccia utente ed evita aree bianche solide per evitare il burn-in dello schermo.

Debug e test

Questi comandi adb possono essere utili per lo sviluppo o il test del comportamento della tua app quando il dispositivo è in modalità Ambient:

# 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

Esempio: app di allenamento

Prendiamo ad esempio un'app di allenamento che deve mostrare le metriche all'utente per l'intera durata della sessione di allenamento. L'app deve rimanere visibile durante le transizioni di stato Ambient ed evitare di essere sostituita dal quadrante.

Per farlo, lo sviluppatore deve:

  1. Implementa un AmbientLifecycleObserver per gestire le modifiche dell'interfaccia utente tra gli stati Interattivo e Ambiente, ad esempio attenuare lo schermo e rimuovere i dati non essenziali.
  2. Crea un nuovo layout a basso consumo per lo stato Ambient che segua le best practice.
  3. Utilizza l'API Attività in corso per tutta la durata dell'allenamento per impedire al sistema di tornare al quadrante.

Per un'implementazione completa, consulta l'esempio di esercizio basato su compose su GitHub. Questo esempio mostra anche l'utilizzo del composable AmbientAware della libreria Horologist per semplificare la gestione della modalità Ambient in Compose.