Principes de base des applications

Faire évoluer les cartes vers des widgets Wear à l'aide de Jetpack Glance et de Remote Compose

Haut-parleurs

  • François Deschênes

    Responsable produit

  • Yuri Schimke

    Ingénieur logiciel

À propos

À propos de l'intervention

Dans cette session, nous allons faire une présentation détaillée de l'évolution technique des cartes plein écran vers les widgets Wear dynamiques à hauteur partielle.

Nous explorerons les avantages techniques de l'API Jetpack Glance unifiée et du nouveau moteur Remote Compose, en expliquant comment ils s'alignent parfaitement avec le développement Android moderne.

Enfin, nous partagerons les dernières consignes concernant l'expérience utilisateur et nous vous présenterons des stratégies de migration pratiques pour vous aider à créer la prochaine génération d'UI d'aperçu.

Résumé

  • Wear OS fait évoluer les cartes plein écran en widgets Wear créés avec Jetpack Glance et optimisés par Remote Compose.
  • Remote Compose partage le même compilateur et le même environnement d'exécution que l'UI Jetpack Compose, tout en dissociant de manière sécurisée votre UI du processus de l'application.
  • Les tailles de conteneur S et L s'alignent sur les formats de grille mobile, tandis que les API Jetpack Glance Wear maintiennent la rétrocompatibilité jusqu'à Wear 4.
  • Pour migrer des cartes existantes vers des widgets, vous pouvez utiliser une stratégie recommandée à double service ou un service de compatibilité unique.

Compétences

Ressources

Gemini Notebook

Thèmes

  • wear-os
  • wear-widgets
  • jetpack-glance
  • remote-compose
  • tiles-migration
  • jetpack-compose

Transcription

Introduction

  • Bonjour à tous

  • Je m'appelle Frank et je dirige l'équipe chargée des expériences d'accès rapide pour Wear OS.

  • Les montres sont des compagnons essentiels pour vos utilisateurs, tout au long de la journée. Aujourd'hui, nous allons vous expliquer comment exploiter la nouvelle expérience des widgets Wear.

  • Nous allons voir comment proposer vos fonctionnalités les plus importantes directement au poignet de vos utilisateurs, et nous le ferons à l'aide des dernières API, des API conçues pour que le développement pour Wear OS soit familier et intuitif pour les développeurs Android, comme vous.

  • Pour m'aider à vous expliquer comment nous y parvenons, j'accueille sur scène mon brillant collègue et voisin de bureau, Yuri.

  • Bonjour, je m'appelle Yuri Schimke.

  • Je suis ingénieur dans l'équipe Wear OS et je travaille principalement sur les API Remote Compose.

  • Aujourd'hui, je vais vous présenter brièvement Remote Compose, puis je vous parlerai du processus de migration des cartes vers les widgets Wear.

  • Je laisse maintenant la parole à Frank.

Évolution des cartes vers les widgets Wear

  • Parfait.

  • Merci, Yuri.

  • Nous intégrons officiellement les montres à l'écosystème unifié des widgets Android en transformant nos cartes plein écran en widgets Wear.

  • Comme ces expériences sont désormais créées à l'aide de Jetpack Glance, vous pouvez voir qu'il s'agit de la même infrastructure sous-jacente que celle utilisée pour les widgets mobiles.

  • Vous verrez que le développement pour Wear OS vous deviendra encore plus familier à l'avenir.

Présentation de Remote Compose

  • Pour proposer ces expériences, nous faisons évoluer la pile d'UI sous-jacente.

  • Les widgets Wear sont optimisés par Remote Compose, un nouveau framework Android pour l'UI à distance.

  • Cela signifie que vous pouvez enfin utiliser une API déclarative étroitement alignée sur Jetpack Compose pour créer vos expériences hors application, comme les widgets.

  • Remote Compose apporte des graphismes modernes et des mises en page responsives aux widgets, tout en garantissant des performances et une efficacité énergétique optimales.

  • En dissociant votre UI du processus de l'application, vos widgets sont sérialisés de manière sécurisée et s'affichent facilement sur un large éventail d'appareils Android.

  • Vous pouvez ainsi proposer ces expériences enrichies sur pratiquement toutes les surfaces distantes compatibles, avec un minimum d'efforts.

Tailles de mise en page et rétrocompatibilité

  • Pour que la transition des cartes aux widgets se fasse le plus facilement possible, nous introduisons deux tailles de conteneur standards : petite et grande.

  • Ils sont conçus pour s'aligner étroitement sur les formats de widgets basés sur une grille 2x1 et 2x2 que vous avez l'habitude de créer sur mobile.

  • À partir de Wear 7, sur certains appareils, vos widgets Wear seront désormais affichés dans un panneau à défilement vertical, aux côtés des widgets d'autres applications.

  • Par exemple, voici à quoi ressemblent les widgets Wear sur une Samsung Galaxy Watch.

  • L'architecture de la plate-forme est conçue pour prendre en charge les tiroirs à défilement vertical, car les fabricants de matériel adopteront ce modèle à l'avenir.

  • Vous n'avez pas non plus à vous soucier de laisser des utilisateurs, ou des utilisateurs existants, de côté.

  • Les API Jetpack Glance Wear sont conçues pour assurer la rétrocompatibilité jusqu'à Wear 4.

  • Sur les appareils plus anciens qui acceptent un panneau horizontal de widgets ou de cartes, le système s'en chargera pour vous.

  • Il vous suffit d'indiquer dans votre code que vous souhaitez prendre en charge le mode plein écran. Le système prendra alors votre grand widget et l'affichera sous la forme d'une carte traditionnelle en plein écran sur ces appareils.

  • Yuri va maintenant nous présenter le code pour vous montrer exactement comment migrer vos cartes existantes vers des widgets Wear.

Stratégies de migration pour les cartes existantes

  • Merci, Frank.

  • Si vous créez un widget Wear de zéro, c'est simple.

  • Il vous suffit de définir un GlanceWearWidgetService et de créer votre UI à l'aide de Remote Compose.

  • Toutefois, si votre application dispose déjà d'une carte en production, vous devrez migrer vos services, votre UI et vos données.

  • Pour que cette transition se fasse en douceur, nous avons prévu des parcours permettant d'assurer une expérience fluide aux utilisateurs existants.

  • Pour le service, vous pouvez choisir entre deux stratégies : service double ou service unique.

  • Avec le double service, que nous recommandons, vous conserverez votre TileService existant pour les anciens appareils Wear OS et ajouterez un nouveau GlanceWearWidgetService pour Wear OS 7 et versions ultérieures.

  • En revanche, avec le service unique, vous créez un seul GlanceWearWidgetService qui gère les deux protocoles, en s'exécutant dans un mode de compatibilité pour traduire votre widget en carte sur les appareils plus anciens.

  • Dans les deux cas, vous continuerez à toucher vos utilisateurs.

  • Cela n'affecte que le code que vous gérerez à l'avenir.

  • Nous venons de publier une documentation complète sur le site des développeurs Android, qui explique exactement comment et quand utiliser ces deux modèles.

  • Consultez-le pour savoir quelle stratégie est la plus adaptée à votre application.

Créer une interface utilisateur avec Remote Compose

  • Parlons maintenant de la couche d'interface utilisateur.

  • Lors de la migration, votre modèle mental change.

  • Auparavant, les cartes utilisaient ProtoLayout, une bibliothèque conçue exclusivement pour les cartes Wear.

  • Les widgets Wear, quant à eux, s'appuient sur Remote Compose.

  • Remote Compose partage exactement la même base que l'UI Jetpack Compose, en utilisant le même compilateur et le même environnement d'exécution de base.

  • Il est conçu pour être familier, et ressemble beaucoup aux bibliothèques d'UI Compose telles que Foundation et Material 3.

  • Mais au lieu d'une zone, d'une colonne ou d'un texte, vous les remplacerez simplement par RemoteBox, RemoteColumn ou RemoteText.

  • Dans ce cas, vous allez vous habituer à écrire souvent "remote".

  • De plus, pour accélérer votre workflow, nous avons inclus un ensemble complet de composants Remote Compose prêts à l'emploi, conçus spécifiquement pour les widgets.

  • Cela signifie que vos conceptions Wear Material 3 existantes, y compris celles que vous avez dans Figma, peuvent être facilement adaptées aux widgets, ce qui les rend familières aux ingénieurs et aux concepteurs.

  • Pour voir à quel point ces frameworks sont alignés, prenons un exemple concret.

  • Nous allons créer notre propre composant d'application au-dessus du bouton de changement d'état Material 3.

  • À gauche, nous avons le composant Wear Compose standard que vous écririez dans votre application, et à droite, la version Remote Compose.

    // Wear Compose
    @Composable
    fun HistoryEraserButton(
      onClick: () -> Unit,
      modifier: Modifier = Modifier,
      checked: Boolean = true,
      onCheckedChange: (Boolean) -> Unit = {},
    )
    // Wear RemoteCompose
    @RemoteComposable
    @Composable
    fun RemoteHistoryEraserButton(
      onClick: Action,
      modifier: RemoteModifier = RemoteModifier,
      checked: RemoteBoolean = true.rb,
      onCheckedChange: Action = Action.Empty,
    )
  • Remarquez à quel point les motifs sont similaires.

  • Toutefois, lorsque vous écrivez du code Remote Compose, vous devez utiliser l'annotation RemoteComposable.

  • Et lorsque vous utilisez des paramètres familiers comme "modifier" et "boolean", ils ont des types Remote Compose équivalents, comme RemoteModifier et RemoteBoolean.

  • et vous utilisez des actions au lieu de lambdas.

  • Au lieu de définir le code à exécuter dans le lambda, vous devez déclarer une action, comme lancer un intent en attente, appeler un service ou mettre à jour une valeur.

  • Passons au corps de notre composant.

  • Vous remarquerez que même le thème est aligné.

  • Vous pouvez appliquer votre jeu de couleurs Material 3 au composant Remote Compose exactement comme vous le feriez dans Compose standard. Votre widget correspondra ainsi parfaitement au branding de votre application.

    // Wear Compose
      val colors = MaterialTheme.colorScheme
      SplitSwitchButton(
        checked = checked,
        onCheckedChange = onCheckedChange,
        toggleContentDescription = "Arm Eraser",
        onContainerClick = onClick,
        modifier = modifier.fillMaxWidth(),
        colors =
          SwitchButtonDefaults.splitSwitchButtonColors(
            checkedContainerColor = colors.errorContainer, …
          ),
        label = { Text("History Eraser") },
        secondaryLabel = { Text("Don't touch it!") },
      )
    // Wear RemoteCompose
      val colors = RemoteMaterialTheme.colorScheme
      RemoteSplitSwitchButton(
        checked = checked,
        onCheckedChange = onCheckedChange,
        toggleContentDescription = "Arm Eraser".rs,
        onContainerClick = onClick,
        modifier = modifier.fillMaxWidth(),
        colors =
          RemoteSplitSwitchButtonDefaults.splitSwitchButtonColors(
            checkedContainerColor = colors.errorContainer, …
          ),
        label = { RemoteText("History Eraser".rs) },
        secondaryLabel = { RemoteText("Don't touch it!".rs) },
      )
  • Enfin, voyons le résultat. L'UI obtenue est exactement celle que vous attendez : un bouton d'activation/désactivation propre, réactif et animé.

Prise en charge de l'aperçu Compose pour les widgets

  • Il est difficile de faire la différence.

  • Pour vous aider à concevoir, à itérer et à vérifier les mises en page de vos widgets sans avoir à les exécuter systématiquement sur un appareil ou un émulateur, nous avons ajouté la prise en charge de l'aperçu Compose pour les widgets Wear.

  • Étant donné que les widgets dépendent de contraintes de conteneur spécifiques à l'OEM, telles que la taille, la forme, la marge intérieure et le rayon d'angle, nous nous appuyons sur l'annotation d'aperçu standard, mais avec un fournisseur de paramètres supplémentaire pour injecter ces configurations.

    @Preview(name = "Squircle Preview", device = "spec:width=1000dp,height=1000dp,dpi=320")
    @Composable
    fun WeatherWidgetSquirclePreview(
        @PreviewParameter(SquircleAllWidgetPreviewParams::class) params: WearWidgetParams
    ) = WearWidgetPreview(MockWeatherWidget(65, WeatherCondition.CLOUDY), params)
    
    @Preview(name = "Round Preview", device = "spec:width=1000dp,height=1000dp,dpi=320")
    @Composable
    fun WeatherWidgetRoundPreview(
        @PreviewParameter(RoundAllWidgetPreviewParams::class) params: WearWidgetParams
    ) = WearWidgetPreview(MockWeatherWidget(65, WeatherCondition.CLOUDY), params)
  • La bibliothèque fournit des implémentations de fournisseurs PreviewParameter préconfigurées qui correspondent aux appareils Wear OS réels.

  • Ces annotations d'aperçu vous permettent de vérifier facilement votre mise en page sur différentes formes de matériel.

  • En plus de tester vos mises en page, vous pouvez utiliser ces aperçus pour générer les images statiques requises pour le sélecteur de widgets sur l'appareil.

    @Preview(name = "Widget Picker Preview", device = "spec:width=1000dp,height=1000dp,dpi=320")
    @Composable
    fun WeatherWidgetRectangularPreview(
        @PreviewParameter(RectangularAllWidgetPreviewParams::class) params: WearWidgetParams
    ) = WearWidgetPreview(MockWeatherWidget(65, WeatherCondition.CLOUDY), params)
  • La bibliothèque fournit un paramètre pour la forme d'aperçu rectangulaire, qui émule les conteneurs rectangulaires complets non recadrés.

Exemples d'outils d'implémentation et de migration

  • Si vous souhaitez voir comment cela fonctionne en pratique, nous avons actualisé notre application exemple multimédia Horologist pour y inclure un nouveau widget qui présente cette API.

  • Nous allons vous montrer comment ajouter les versions petite et grande du widget multimédia.

  • Consultez-le sur GitHub pour comparer côte à côte l'ancien code ProtoLayout et la nouvelle implémentation de Remote Compose.

  • Nous sommes sûrs que vous allez l'adorer.

  • Enfin, nous ne pourrions pas parler de l'avenir sans mentionner l'IA.

  • Pour accélérer votre migration, nous avons publié une nouvelle compétence de migration des cartes vers les widgets sur GitHub.

  • Vous pouvez l'utiliser avec l'agent de votre choix ou l'ajouter directement à Android Studio à l'aide de la CLI Android.

    android skills add wear-widgets
    android skills add --all
  • Je laisse maintenant la parole à François pour conclure.

Récapitulatif et conclusion

  • Merci.

  • Merci, Yuri.

  • Comme vous pouvez le voir, transformer vos cartes en widgets Wear est un processus très simple.

  • Pour résumer, en migrant vers Jetpack Glance Wear et Remote Compose, vous ne vous contentez pas de mettre à jour votre UI, vous rejoignez un écosystème de widgets Android plus unifié.

  • Vous pouvez désormais créer de magnifiques widgets rétrocompatibles à l'aide d'un framework déclaratif moderne entièrement aligné sur Jetpack Compose.

  • Vous pouvez commencer à migrer vos cartes dès aujourd'hui en consultant le guide de démarrage sur le site Développeurs Android pour en savoir plus.

  • Nous avons hâte de découvrir toutes les expériences expressives et faciles à consulter que vous allez créer pour les montres.

  • Merci beaucoup de nous avoir rejoints ce matin. Nous espérons que vous apprécierez le reste de la conférence.

  • Merci.

Code

Extraits de code

Extrait de code Kotlin à 06:40 dans "Créer une UI avec Remote Compose"
// Wear Compose
@Composable
fun HistoryEraserButton(
  onClick: () -> Unit,
  modifier: Modifier = Modifier,
  checked: Boolean = true,
  onCheckedChange: (Boolean) -> Unit = {},
)
Extrait de code Kotlin à 06:40 dans "Créer une UI avec Remote Compose"
// Wear RemoteCompose
@RemoteComposable
@Composable
fun RemoteHistoryEraserButton(
  onClick: Action,
  modifier: RemoteModifier = RemoteModifier,
  checked: RemoteBoolean = true.rb,
  onCheckedChange: Action = Action.Empty,
)
Extrait Kotlin à 07:39 dans "Créer une UI avec Remote Compose"
// Wear Compose
  val colors = MaterialTheme.colorScheme
  SplitSwitchButton(
    checked = checked,
    onCheckedChange = onCheckedChange,
    toggleContentDescription = "Arm Eraser",
    onContainerClick = onClick,
    modifier = modifier.fillMaxWidth(),
    colors =
      SwitchButtonDefaults.splitSwitchButtonColors(
        checkedContainerColor = colors.errorContainer, …
      ),
    label = { Text("History Eraser") },
    secondaryLabel = { Text("Don't touch it!") },
  )
Extrait Kotlin à 07:39 dans "Créer une UI avec Remote Compose"
// Wear RemoteCompose
  val colors = RemoteMaterialTheme.colorScheme
  RemoteSplitSwitchButton(
    checked = checked,
    onCheckedChange = onCheckedChange,
    toggleContentDescription = "Arm Eraser".rs,
    onContainerClick = onClick,
    modifier = modifier.fillMaxWidth(),
    colors =
      RemoteSplitSwitchButtonDefaults.splitSwitchButtonColors(
        checkedContainerColor = colors.errorContainer, …
      ),
    label = { RemoteText("History Eraser".rs) },
    secondaryLabel = { RemoteText("Don't touch it!".rs) },
  )
Extrait de code Kotlin à 08:20 dans "Compose preview support for widgets"
@Preview(name = "Squircle Preview", device = "spec:width=1000dp,height=1000dp,dpi=320")
@Composable
fun WeatherWidgetSquirclePreview(
    @PreviewParameter(SquircleAllWidgetPreviewParams::class) params: WearWidgetParams
) = WearWidgetPreview(MockWeatherWidget(65, WeatherCondition.CLOUDY), params)

@Preview(name = "Round Preview", device = "spec:width=1000dp,height=1000dp,dpi=320")
@Composable
fun WeatherWidgetRoundPreview(
    @PreviewParameter(RoundAllWidgetPreviewParams::class) params: WearWidgetParams
) = WearWidgetPreview(MockWeatherWidget(65, WeatherCondition.CLOUDY), params)
Extrait de code Kotlin à 08:58 dans "Prise en charge de l'aperçu Compose pour les widgets"
@Preview(name = "Widget Picker Preview", device = "spec:width=1000dp,height=1000dp,dpi=320")
@Composable
fun WeatherWidgetRectangularPreview(
    @PreviewParameter(RectangularAllWidgetPreviewParams::class) params: WearWidgetParams
) = WearWidgetPreview(MockWeatherWidget(65, WeatherCondition.CLOUDY), params)
Extrait de code shell à 09:58 dans "Exemple d'implémentation et outils de migration"
android skills add wear-widgets
android skills add --all