Créer votre première carte dans Wear OS

1. Introduction

Trois cartes : Fitness, Messages et Agenda.

Les cartes Wear OS permettent d'accéder facilement aux informations et aux actions dont les utilisateurs ont besoin pour effectuer une tâche. Il suffit de balayer l'écran à partir du cadran pour consulter les dernières prévisions ou lancer un minuteur.

Une carte s'exécute dans l'interface utilisateur du système au lieu de s'exécuter dans son propre conteneur d'application. Nous utilisons un service pour décrire la mise en page et le contenu de cette carte. L'UI du système affiche ensuite la carte si nécessaire.

Objectifs de l'atelier

656045bed9c45083.png

Vous créerez une carte qui affichera les conversations récentes pour une application de chat. À partir de cette surface, l'utilisateur peut effectuer trois tâches courantes :

  • Ouvrir une conversation
  • Écrire un nouveau message

Points abordés

Dans cet atelier de programmation, vous apprendrez à écrire votre propre carte Wear OS, ce qui comprend les tâches suivantes :

  • Créer un élément TileService
  • Tester une carte sur un appareil
  • Prévisualiser l'interface utilisateur d'une carte dans Android Studio
  • Développer l'interface utilisateur d'une carte
  • Ajouter des images
  • Gérer les interactions

Prérequis

  • Connaissances de base du langage Kotlin

2. Configuration

Au cours de cette étape, vous configurerez votre environnement et téléchargerez le projet de démarrage.

Ce dont vous avez besoin

Si vous ne savez pas comment utiliser Wear OS, nous vous recommandons de lire ce guide rapide avant de commencer. Il contient des instructions pour configurer un émulateur Wear OS et décrit comment naviguer dans le système.

Télécharger le code

Si git est installé, vous pouvez simplement exécuter la commande ci-dessous pour cloner le code à partir de ce dépôt.

git clone https://github.com/android/codelab-wear-tiles.git
cd codelab-wear-tiles

Si vous n'avez pas git, cliquez sur le bouton ci-dessous pour télécharger l'ensemble du code de cet atelier de programmation :

Télécharger le fichier ZIP

Ouvrir un projet dans Android Studio

Dans la fenêtre "Welcome to Android Studio" (Bienvenue dans Android Studio), sélectionnez c01826594f360d94.png Open an Existing Project (Ouvrir un projet existant) ouFile > Open (Fichier > Ouvrir), puis sélectionnez le dossier [Emplacement de téléchargement].

3. Créer une carte de base

Le point d'entrée d'une carte est le service de cartes. Au cours de cette étape, vous allez enregistrer un service de cartes et définir une mise en page de carte.

HelloWorldTileService

Une classe qui implémente TileService doit spécifier deux méthodes :

  • onTileResourcesRequest(requestParams: ResourcesRequest): ListenableFuture<Resources>
  • onTileRequest(requestParams: TileRequest): ListenableFuture<Tile>

La première méthode renvoie un objet Resources qui mappe les ID de chaîne aux ressources image que nous utiliserons dans notre carte.

La seconde renvoie la description d'une carte, y compris sa mise en page. C'est là que nous définirons la mise en page d'une carte et la manière dont les données y sont liées.

Ouvrez HelloWorldTileService.kt à partir du module start. Toutes les modifications que vous allez apporter seront incluses dans ce module. Si vous voulez voir le résultat de cet atelier de programmation, il existe également un module finished.

HelloWorldTileService complète SuspendingTileService, un wrapper compatible avec la coroutine Kotlin issu de la bibliothèque Horologist Tiles. Horologist est un groupe de bibliothèques Google visant à fournir aux développeurs Wear OS des fonctionnalités complémentaires qui sont couramment plébiscitées, mais qui ne sont pas encore disponibles dans Jetpack.

SuspendingTileService fournit deux fonctions de suspension, qui sont des équivalents de coroutine des fonctions de TileService :

  • suspend resourcesRequest(requestParams: ResourcesRequest): Resources
  • suspend tileRequest(requestParams: TileRequest): Tile

Pour en savoir plus sur les coroutines, consultez la documentation sur les coroutines Kotlin sur Android.

HelloWorldTileService n'est pas encore terminé. Nous devons enregistrer le service dans notre fichier manifeste et fournir également une implémentation pour tileLayout.

Enregistrer le service de cartes

Une fois le service de cartes enregistré dans le fichier manifeste, il apparaît dans la liste des cartes que l'utilisateur peut ajouter.

Ajoutez <service> dans l'élément <application> :

start/src/main/AndroidManifest.xml

<service
    android:name="com.example.wear.tiles.hello.HelloWorldTileService"
    android:icon="@drawable/ic_waving_hand_24"
    android:label="@string/hello_tile_label"
    android:description="@string/hello_tile_description"
    android:exported="true"
    android:permission="com.google.android.wearable.permission.BIND_TILE_PROVIDER">
    
    <intent-filter>
        <action android:name="androidx.wear.tiles.action.BIND_TILE_PROVIDER" />
    </intent-filter>

    <!-- The tile preview shown when configuring tiles on your phone -->
    <meta-data
        android:name="androidx.wear.tiles.PREVIEW"
        android:resource="@drawable/tile_hello" />
</service>

L'icône et le libellé sont utilisés (comme des espaces réservés) lors du chargement initial de la carte ou en cas d'erreur lors du chargement. Les métadonnées à la fin définissent une image d'aperçu qui s'affiche dans le carrousel lorsque l'utilisateur ajoute une carte.

Définir la mise en page de la carte

HelloWorldTileService possède une fonction appelée tileLayout avec l'élément TODO() comme corps. Nous allons maintenant remplacer cela par une implémentation dans laquelle nous définirons la mise en page de la carte :

start/src/main/java/com/example/wear/tiles/hello/HelloWorldTileService.kt

fun tileLayout(
    context: Context,
    deviceConfiguration: DeviceParametersBuilders.DeviceParameters,
    message: String,
) =
    materialScope(
        context = context,
        deviceConfiguration = deviceConfiguration,
        allowDynamicTheme = false,
    ) {
        primaryLayout(mainSlot = { text(message.layoutString) })
    }

Et voilà notre première carte Wear OS créée. Installons cette carte et voyons ce à quoi elle ressemble.

4. Tester votre carte sur un appareil

Une fois le module de démarrage sélectionné dans le menu déroulant de la configuration d'exécution, vous pouvez installer l'application (module start) sur votre appareil ou dans un émulateur, puis installer manuellement la carte, comme le ferait un utilisateur.

Toutefois, Android Studio propose un raccourci pour cette opération : appuyez sur l'icône "Run service" (Exécuter le service) (▷) dans la marge, puis sélectionnez "Run HelloWorldTileService" (Exécuter HelloWoldTileService). La carte sera alors installée et lancée sur un appareil connecté.

ded9f9355abd02f3.png

Cliquez sur "Run HelloWorldTileService" (Exécuter HelloWorldTileService) pour créer et exécuter votre carte sur un appareil connecté. Une capture d'écran semblable à celle ci-dessous devrait s'afficher.

693c130912097be6.png

L'icône représentant une main qui fait signe, qui s'affiche en haut de l'écran, est fournie par le système. Pour la modifier, modifiez la propriété android:icon de l'élément <service> de la carte dans le fichier manifeste.

Pour plus de commodité, ce processus crée également une "configuration d'exécution" "HelloWorldTileService" pour une utilisation ultérieure.

b3335148771abbeb.png

5. Ajouter des fonctions d'aperçu

Vous pouvez afficher un aperçu de l'interface utilisateur des cartes dans Android Studio. Cela permet de raccourcir la boucle de rétroaction lors du développement de l'interface utilisateur, ce qui augmente la vitesse de développement.

Ajoutez un aperçu de carte pour le HelloWorldTileService à la fin du fichier HelloWorldTileService.kt.

start/src/main/java/com/example/wear/tiles/hello/HelloWorldTileService.kt

@Preview(device = WearDevices.SMALL_ROUND, name = "Small Round")
@Preview(device = WearDevices.LARGE_ROUND, name = "Large Round")
internal fun helloLayoutPreview(context: Context): TilePreviewData {
    return TilePreviewData {
        TilePreviewHelper.singleTimelineEntryTileBuilder(
            helloLayout(context, it.deviceConfiguration, "Hello, preview tile!")
        )
            .build()
    }
}

Utilisez la vue d'écran partagé pour afficher un aperçu de la carte :

Vue d&#39;écran partagé d&#39;Android Studio avec aperçu du code à gauche et image de la carte à droite.

Notez que l'annotation @Composable n'est pas utilisée. Bien que les cartes utilisent la même UI d'aperçu que les fonctions composables, elles n'utilisent pas Compose et ne sont pas composables.

6. Créer une carte de messages

cf18db0f604b1999.png

La carte de messages que nous allons créer s'apparente davantage à une carte réelle. Contrairement à l'exemple HelloWorld, celui-ci présente les composants de Material 3 Expressive, affiche des images et gère les interactions pour ouvrir l'application.

MessagingTileService

MessagingTileService complète la classe SuspendingTileService que nous avons vue précédemment.

7. Ajouter des composants d'interface utilisateur

La bibliothèque ProtoLayout fournit des composants et des mises en page prédéfinis qui vous permettent de créer des cartes utilisant les derniers designs Material 3 Expressive pour Wear OS.

Ajoutez la dépendance Tiles Material au fichier build.gradle :

start/build.gradle

implementation "androidx.wear.protolayout:protolayout-material3:$protoLayoutVersion"

Ajoutez le code de mise en page à la fonction tileLayout(), en tant que corps de la fonction materialScope(). Cette opération crée une mise en page composée de deux lignes (avec deux boutons chacune) et d'un bouton de bord.

Recherchez la ligne TODO() // Add primaryLayout() et remplacez-la par le code ci-dessous.

start/src/main/java/com/example/wear/tiles/messaging/tile/Layout.kt

primaryLayout(
    mainSlot = {
        // This layout code assumes "contacts" contains at least 4 elements, for sample code
        // that can handle an arbitrary number of contacts, and also shows different numbers
        // of contacts based on the physical screen size, see
        // <https://github.com/android/wear-os-samples/tree/main/WearTilesKotlin>.
        Column.Builder()
            .apply {
                setWidth(expand())
                setHeight(expand())
                addContent(
                    buttonGroup {
                        buttonGroupItem { contactButton(contacts[0]) }
                        buttonGroupItem { contactButton(contacts[1]) }
                    }
                )
                addContent(DEFAULT_SPACER_BETWEEN_BUTTON_GROUPS)
                addContent(
                    buttonGroup {
                        buttonGroupItem { contactButton(contacts[2]) }
                        buttonGroupItem { contactButton(contacts[3]) }
                    }
                )
            }
            .build()
    },
    bottomSlot = {
        textEdgeButton(
            onClick = clickable(), // TODO: Launch new conversation activity
            labelContent = { text("New".layoutString) },
        )
    },
)

La fonction contactButton() dans le même fichier crée les boutons de contact individuels. Si le contact est associé à une image, celle-ci s'affiche sur le bouton. Sinon, les initiales du contact sont utilisées.

À ce stade, vous remarquerez peut-être que, bien que la mise en page générale soit correcte, les images sont absentes :

809bdb9d1213c376.png

Vous verrez la même chose si vous déployez la carte sur un appareil :

4671bb2eafdcc528.png

À l'étape suivante, nous allons résoudre le problème d'images manquantes.

8. Ajouter des images

De manière générale, les cartes se composent de deux éléments : une mise en page (qui fait référence à des ressources par des ID de chaîne) et les ressources elles-mêmes (qui peuvent être des images).

Pour le moment, notre code fournit la mise en page, mais pas les ressources elles-mêmes. Pour corriger l'aperçu, nous devons fournir les "ressources" de l'image. Pour ce faire, recherchez TODO: Add onTileResourceRequest et ajoutez le code ci-dessous en tant qu'argument nommé supplémentaire à TilePreviewData() :

start/src/main/java/com/example/wear/tiles/messaging/tile/Layout.kt

// Additional named argument to TilePreviewData
onTileResourceRequest = { resourcesRequest ->
    Resources.Builder()
        .setVersion(resourcesRequest.version)
        .apply {
            contacts.forEach {
                if (it.avatarSource is AvatarSource.Resource) {
                    addIdToImageMapping(
                        it.imageResourceId(),
                        it.avatarSource.resourceId
                    )
                }
            }
        }
        .build()
}

Les images devraient maintenant s'afficher dans l'aperçu :

e77d746268f293f2.png

Toutefois, si la carte est déployée sur un appareil, les images seront absentes. Pour résoudre ce problème, remplacez la fonction resourcesRequest() dans Service.kt par le code suivant :

start/src/main/java/com/example/wear/tiles/messaging/tile/Service.kt

override suspend fun resourcesRequest(
    requestParams: ResourcesRequest
): Resources {
    // resourceIds is a list of the ids we need to provide images for. If we're passed an empty
    // list, set resourceIds to all resources.
    val resourceIds =
        requestParams.resourceIds.ifEmpty {
            contacts.map { it.imageResourceId() }
        }

    // resourceMap maps (tile) resource ids to (Android) resource ids.
    val resourceMap =
        contacts
            .mapNotNull {
                when (it.avatarSource) {
                    is AvatarSource.Resource ->
                        it.imageResourceId() to
                            it.avatarSource.resourceId
                    else -> null
                }
            }
            .toMap()
            .filterKeys {
                it in resourceIds
            } // filter to only the resources we need

    // Add images in the resourceMap to the Resources object, and return the result.
    return Resources.Builder()
        .setVersion(requestParams.version)
        .apply {
            resourceMap.forEach { (id, imageResource) ->
                addIdToImageMapping(id, imageResource)
            }
        }
        .build()
}

Désormais, les images s'affichent également lorsque la carte est déployée sur un appareil :

cf18db0f604b1999.png

À l'étape suivante, nous traiterons les clics sur chacun des éléments.

9. Gérer les interactions

Les raccourcis vers les parcours utilisateur les plus importants sont l'un des principaux atouts d'une carte. À ne pas confondre avec le lanceur d'applications, qui ouvre simplement l'application ! Dans le cas présent, nous avons l'espace nécessaire pour fournir des raccourcis contextuels sur un écran spécifique de votre application.

Jusqu'à présent, nous avons utilisé une action fictive fournie par le clickable() sans argument pour le chip et chacun des boutons. Cette approche convient pour les aperçus, qui ne sont pas interactifs, mais voyons comment ajouter des actions correspondant aux éléments.

LaunchAction

L'élément LaunchAction peut être utilisé pour lancer une activité. Modifions Layout de sorte que l'utilisateur puisse appuyer sur le bouton "New" (Nouveau) pour lancer le parcours utilisateur "New conversation" (Nouvelle conversation).

Recherchez la ligne TODO: Launch new conversation activity et remplacez clickable() par :

start/src/main/java/com/example/wear/tiles/messaging/tile/Layout.kt

clickable(
    id = "new_button",
    action =
        launchAction(
            ComponentName(
                "com.example.wear.tiles",
                "com.example.wear.tiles.messaging.MainActivity",
            ),
            mapOf(
                MainActivity.EXTRA_JOURNEY to
                    ActionBuilders.stringExtra(
                        MainActivity.EXTRA_JOURNEY_NEW
                    )
            ),
        ),
)

Redéployez la carte. Désormais, au lieu de ne rien faire, le fait d'appuyer sur "New" (Nouveau) lance MainActivity et démarre le parcours utilisateur "New conversation" (Nouvelle conversation) :

a08c28b4a142fb8f.png

De même, modifiez Layout pour que l'utilisateur puisse lancer une conversation avec un contact spécifique en appuyant sur un bouton.

Recherchez la ligne Launch open conversation activity (Lancer l'activité de conversation ouverte) et remplacez clickable() par :

start/src/main/java/com/example/wear/tiles/messaging/tile/Layout.kt

clickable(
    id = contact.id.toString(),
    action =
        launchAction(
            ComponentName(
                "com.example.wear.tiles",
                "com.example.wear.tiles.messaging.MainActivity",
            ),
            mapOf(
                MainActivity.EXTRA_JOURNEY to
                    ActionBuilders.stringExtra(
                        MainActivity
                            .EXTRA_JOURNEY_CONVERSATION
                    ),
                MainActivity.EXTRA_CONVERSATION_CONTACT to
                    ActionBuilders.stringExtra(
                        contact.name
                    ),
            ),
        ),
)

Redéployez la carte. Désormais, au lieu de ne rien faire, le fait d'appuyer sur un contact lancera une conversation avec ce dernier :

b684a1ced0b226f9.png

10. Félicitations

Félicitations ! Vous avez appris à créer une carte pour Wear OS.

Et maintenant ?

Pour en savoir plus, consultez les implémentations de Golden Tiles sur GitHub, le guide des cartes Wear OS et les consignes de conception.