Applications permanentes et mode ambiant du système

Ce guide explique comment rendre votre application toujours active, comment réagir aux transitions d'état de l'alimentation et comment gérer le comportement de l'application pour offrir une bonne expérience utilisateur tout en économisant la batterie.

Rendre une application constamment visible a un impact significatif sur l'autonomie de la batterie. Tenez-en compte lorsque vous ajoutez cette fonctionnalité.

Concepts clés

Lorsqu'une application Wear OS s'affiche en plein écran, elle se trouve dans l'un des deux états d'alimentation suivants :

  • Interactif : état de forte puissance où l'écran est à pleine luminosité, ce qui permet une interaction complète de l'utilisateur.
  • Veille : état de faible consommation d'énergie dans lequel l'écran s'assombrit pour économiser de l'énergie. Dans cet état, l'UI de votre application occupe toujours tout l'écran, mais le système peut modifier son apparence en la floutant ou en superposant du contenu comme l'heure. Il s'agit également du mode Veille.

Le système d'exploitation contrôle la transition entre ces états.

Une application toujours activée est une application qui affiche du contenu à la fois en mode interactif et en mode Veille.

Lorsqu'une application always-on continue d'afficher sa propre UI alors que l'appareil est en mode Ambiant basse consommation, on dit qu'elle est en mode ambi-actif.

Transitions système et comportement par défaut

Lorsqu'une application est au premier plan, le système gère les transitions d'état d'alimentation en fonction de deux délais d'inactivité de l'utilisateur.

  • Délai d'inactivité 1 : état interactif à état Veille : après une période d'inactivité de l'utilisateur, l'appareil passe à l'état Veille.
  • Délai d'inactivité 2 : retour au cadran : après une nouvelle période d'inactivité, le système peut masquer l'application en cours et afficher le cadran.

Immédiatement après la première transition du système vers l'état Ambient, le comportement par défaut dépend de la version de Wear OS et de la configuration de votre application :

  • Sur Wear OS 5 et versions antérieures, le système affiche une capture d'écran floutée de votre application en pause, avec l'heure en superposition.
  • Sur Wear OS 6 et versions ultérieures, si une application cible le SDK 36 ou une version ultérieure, elle est considérée comme always-on. L'écran est assombri, mais l'application continue de s'exécuter et reste visible. (Les mises à jour peuvent être aussi rares qu'une fois par minute.)

Personnaliser le comportement pour l'état ambiant

Quel que soit le comportement par défaut du système, vous pouvez personnaliser l'apparence ou le comportement de votre application dans l'état Ambient sur toutes les versions de Wear OS en utilisant AmbientLifecycleObserver pour écouter les rappels sur les transitions d'état.

Utiliser AmbientLifecycleObserver

Pour réagir aux événements du mode Veille, utilisez la classe AmbientLifecycleObserver :

  1. Implémentez l'interface AmbientLifecycleObserver.AmbientLifecycleCallback. Utilisez la méthode onEnterAmbient() pour ajuster votre UI à l'état de faible consommation d'énergie et onExitAmbient() pour la restaurer en mode interactif complet.

    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. Créez un AmbientLifecycleObserver et enregistrez-le avec le cycle de vie de votre activité ou composable.

    private val ambientObserver = AmbientLifecycleObserver(activity, ambientCallback)
    
    override fun onCreate(savedInstanceState: Bundle) {
        super.onCreate(savedInstanceState)
        lifecycle.addObserver(ambientObserver)
    
        // ...
    }
    
  3. Appelez removeObserver() pour supprimer l'observateur dans onDestroy().

Pour les développeurs qui utilisent Jetpack Compose, la bibliothèque Horologist fournit un utilitaire utile, le composable AmbientAware, qui simplifie l'implémentation de ce modèle.

TimeText avec détection de mouvement

À titre d'exception à l'exigence d'un observateur personnalisé, le widget TimeText est sensible à la luminosité ambiante sur Wear OS 6. Il se met à jour automatiquement une fois par minute lorsque l'appareil est en état Ambient, sans aucun code supplémentaire.

Contrôler la durée d'activation de l'écran

Les sections suivantes décrivent comment gérer la durée d'affichage de votre application à l'écran.

Empêcher le retour au cadran avec une activité en cours

Après un certain temps passé à l'état Ambiant (délai d'inactivité 2), le système revient généralement au cadran de la montre. L'utilisateur peut configurer la durée du délai d'inactivité dans les paramètres système. Dans certains cas d'utilisation, comme lorsqu'un utilisateur suit un entraînement, une application peut avoir besoin de rester visible plus longtemps.

Sur Wear OS 5 et versions ultérieures, vous pouvez éviter cela en implémentant une activité en cours. Si votre application affiche des informations sur une tâche utilisateur en cours, comme une séance d'entraînement, vous pouvez utiliser l'API Ongoing Activity pour que votre application reste visible jusqu'à la fin de la tâche. Si un utilisateur revient manuellement au cadran, l'indicateur d'activité en cours lui permet de revenir à votre application en un seul geste.

Pour ce faire, l'intent tactile de la notification d'activité en cours doit pointer vers votre activité toujours activée, comme indiqué dans l'extrait de code suivant :

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

Garder l'écran allumé et empêcher l'état Veille

Dans de rares cas, vous devrez peut-être empêcher complètement l'appareil de passer en mode Veille. Autrement dit, pour éviter le délai d'inactivité n°1. Pour ce faire, vous pouvez utiliser l'indicateur de fenêtre FLAG_KEEP_SCREEN_ON. Cette fonction agit comme un verrouillage de réveil, en maintenant l'appareil dans l'état Interactive. Utilisez cette option avec une extrême prudence, car elle a un impact important sur l'autonomie de la batterie.

Recommandations pour le mode Veille

Pour offrir la meilleure expérience utilisateur possible et économiser de l'énergie en mode Veille, suivez ces consignes de conception. Ces recommandations privilégient une expérience utilisateur claire en évitant les informations trompeuses et en réduisant l'encombrement visuel, tout en optimisant la puissance d'affichage.

  • Réduisez l'encombrement visuel et la puissance d'affichage. Une UI épurée et minimaliste indique à l'utilisateur que l'application est en mode basse consommation et économise beaucoup de batterie en limitant les pixels lumineux.
    • Laissez au moins 85 % de l'écran noir.
    • N'affichez que les informations les plus importantes et déplacez les détails secondaires vers l'écran interactif.
    • Utilisez des contours pour les grandes icônes ou les grands boutons plutôt que des fonds pleins.
    • Évitez les grands blocs de couleur unie, ainsi que les images de marque ou d'arrière-plan non fonctionnelles.
  • Gérer les données dynamiques obsolètes
    • Le rappel onUpdateAmbient() n'est invoqué que périodiquement (généralement une fois par minute) pour économiser de l'énergie. En raison de cette limite, toutes les données qui changent fréquemment (comme un chronomètre, la fréquence cardiaque ou la distance d'entraînement) deviennent obsolètes entre les mises à jour. Pour éviter d'afficher des informations trompeuses et incorrectes, écoutez le rappel onEnterAmbient et remplacez ces valeurs en direct par du contenu d'espace réservé statique, tel que --.
  • Maintenir une mise en page cohérente
    • Pour une transition fluide, conservez les éléments à la même position dans les modes Interactif et Veille.
    • L'heure est toujours affichée.
  • Être conscient du contexte
    • Si l'utilisateur se trouvait sur un écran de paramètres ou de configuration lorsque l'appareil est passé en mode Veille, envisagez d'afficher un écran plus pertinent de votre application au lieu de la vue des paramètres.
  • Gérer les exigences spécifiques aux appareils
    • Dans l'objet AmbientDetails transmis à onEnterAmbient() :
      • Si deviceHasLowBitAmbient est défini sur true, désactivez l'anticrénelage dans la mesure du possible.
      • Si burnInProtectionRequired est défini sur true, déplacez légèrement les éléments d'interface utilisateur de manière périodique et évitez les zones blanches unies pour éviter le marquage de l'écran.

Débogage et test

Ces commandes adb peuvent être utiles lorsque vous développez ou testez le comportement de votre application lorsque l'appareil est en mode Veille :

# 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

Exemple : Application d'entraînement

Prenons l'exemple d'une application d'entraînement qui doit afficher des métriques à l'utilisateur pendant toute la durée de sa séance d'exercice. L'application doit rester visible lors des transitions d'état Ambient et ne pas être remplacée par la clock face.

Pour ce faire, le développeur doit procéder comme suit :

  1. Implémentez un AmbientLifecycleObserver pour gérer les modifications de l'UI entre les états Interactive et Ambient, comme la diminution de la luminosité de l'écran et la suppression des données non essentielles.
  2. Créez une mise en page basse consommation pour l'état Ambient en suivant les bonnes pratiques.
  3. Utilisez l'API Ongoing Activity pendant toute la durée de l'entraînement pour empêcher le système de revenir au cadran.

Pour obtenir une implémentation complète, consultez l'exemple d'exercice basé sur Compose sur GitHub. Cet exemple montre également comment utiliser le composable AmbientAware de la bibliothèque Horologist pour simplifier la gestion du mode Veille dans Compose.