Pour créer une application Wear OS pour votre application Capacitor, ajoutez un module natif Wear OS à la android/ projet, donnez-lui le même ID d'application et la même clé de signature que l'application pour téléphone, et connectez les deux avec @capgo/capacitor-watch, which uses Google’s Wear OS Data Layer on Android. The phone keeps its TypeScript code, and the watch runs a small Kotlin app that exchanges JSON with it.
Cette guide vous guide à travers la configuration du module, les chemins de la couche de données exacts que le plugin utilise, le Kotlin code sur la montre, le TypeScript code sur le téléphone, le test avec des émulateurs et la publication sur Google Play.
Comment fonctionne le pont Capacitor vers Wear OS ?
Wear OS n'a pas de runtime Capacitor, donc l'application de montre est native. Le plugin parle la couche de données API.play-services-wearable) sur le téléphone, et votre application de la montre parle le même protocole.
| Méthode du plugin / événement | Mécanisme de la couche de données | Chemin |
|---|---|---|
sendMessage (téléphone vers montre) |
MessageClient |
/capgo/message |
messageReceived (montre vers téléphone) |
MessageClient |
/capgo/message |
messageReceivedWithReply |
MessageClient |
/capgo/message/withreply |
replyToMessage |
MessageClient |
/capgo/reply/{callbackId} |
updateApplicationContext / applicationContextReceived |
DataClient DataItem |
/capgo/context |
transferUserInfo / userInfoReceived |
DataClient DataItem |
/capgo/userinfo/{uuid} |
Les messages transportent des JSON UTF-8 sous forme de bytes bruts. Les DataItems transportent le même JSON sous forme de chaîne sous la clé DataMap payloadLe téléphone vérifie l'existence d'une application montre en recherchant la capacité. capgo_watch.
Connaître ces chemins est la clé. Une fois que la montre les lit et les écrit, chaque méthode de plugin fonctionne.
Ce dont vous avez besoin
- Un Capacitor 8 avec la plateforme Android ajoutée (guide d'upgrade si vous êtes en retard).
- Un Android Studio récent avec les images système Wear OS installées.
- Un téléphone avec Google Play services. Le Data Layer en dépend, donc les téléphones sans Play services signalent
isSupported: false. - Un montre Wear OS ou émulateur associé à un téléphone Android. Les montres Wear OS associées à un iPhone ne peuvent pas rejoindre votre application.
Étape 1 : Installer le plugin
bun add @capgo/capacitor-watch
bunx cap sync android
Le même package gère l'Apple Watch sur iOS, donc une application cross-platform nécessite une seule importation. Voir les docs du plugin pour la référence complète, et notre guide Apple Watch pour le côté iOS.
Étape 2 : Ajoutez un module Wear OS au projet Android
Open android/ dans Android Studio (bunx cap open androidet utilisez Fichier > Nouveau > Nouveau Module > Wear OS. Letting Android Studio generate the module gives you Gradle files that match your Android Gradle Plugin and Kotlin versions, which is safer than pasting a build file from a blog post. Name the module wear.
Le magicien ajoute include ':wear' to android/settings.gradleConservez-les, et laissez les lignes gérées par Capacitor intactes.
Deux règles que la couche de données impose
- Même
applicationId. Enwear/build.gradle, définissezapplicationIdà exactement la valeur dansandroid/app/build.gradleLenamespacepeut différer, l'ID de l'application ne peut pas. - Même certificat de signature. Les builds de débogage sur une machine partagent le keystore de débogage, donc cela fonctionne généralement localement et puis se brise en version de production. Signez la build de débogage Wear OS avec la même clé de téléversement que l'application du téléphone.
Si l'une des règles est brisée, les deux applications ne se verront jamais et rien dans votre code ne pourra les rétablir.
Dépendances et minimum SDK
Wear OS 3 et ultérieurs sont basés sur API 30, donc minSdk 30 is une plateforme pratique pour une nouvelle application montre. Ajoutez la couche de données et les aides de coroutine :
dependencies {
implementation "com.google.android.gms:play-services-wearable:20.0.1"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.11.0"
// Compose for Wear OS dependencies generated by the wizard stay as they are
}
Déclarez la capacité
Créer wear/src/main/res/values/wear.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
tools:keep="@array/android_wear_capabilities">
<string-array name="android_wear_capabilities" translatable="false">
<item>capgo_watch</item>
</string-array>
</resources>
La chaîne doit être capgo_watch, car c'est ce que le plugin interroge pour calculer isWatchAppInstalled.
Marquez l'application comme une application montre
In wear/src/main/AndroidManifest.xml, le guide ajoute <uses-feature android:name="android.hardware.type.watch" />. Décidez si l'application fonctionne sans téléphone. Un compagnon qui ne montre que les données du téléphone doit déclarer :
<meta-data
android:name="com.google.android.wearable.standalone"
android:value="false" />
Étape 3 : Recevez des données sur la montre
A WearableListenerService lets the system deliver Data Layer events even when the watch UI is closed. Register it with filters for the /capgo chemins :
<service
android:name=".PhoneListenerService"
android:exported="true">
<intent-filter>
<action android:name="com.google.android.gms.wearable.MESSAGE_RECEIVED" />
<data android:scheme="wear" android:host="*" android:pathPrefix="/capgo" />
</intent-filter>
<intent-filter>
<action android:name="com.google.android.gms.wearable.DATA_CHANGED" />
<data android:scheme="wear" android:host="*" android:pathPrefix="/capgo" />
</intent-filter>
</service>
Un petit magasin partagé que l'interface peut observer.
package com.example.shop.wear
import kotlinx.coroutines.flow.MutableStateFlow
import org.json.JSONObject
object WatchStore {
val context = MutableStateFlow(JSONObject())
val lastMessage = MutableStateFlow(JSONObject())
val lastReply = MutableStateFlow(JSONObject())
}
Le service analyse chaque chemin :
package com.example.shop.wear
import com.google.android.gms.wearable.DataEvent
import com.google.android.gms.wearable.DataEventBuffer
import com.google.android.gms.wearable.DataMapItem
import com.google.android.gms.wearable.MessageEvent
import com.google.android.gms.wearable.WearableListenerService
import org.json.JSONObject
class PhoneListenerService : WearableListenerService() {
override fun onMessageReceived(event: MessageEvent) {
val json = JSONObject(String(event.data, Charsets.UTF_8))
when {
event.path == "/capgo/message" -> WatchStore.lastMessage.value = json
event.path.startsWith("/capgo/reply/") -> WatchStore.lastReply.value = json
}
}
override fun onDataChanged(events: DataEventBuffer) {
for (event in events) {
if (event.type != DataEvent.TYPE_CHANGED) continue
val path = event.dataItem.uri.path ?: continue
val payload = DataMapItem.fromDataItem(event.dataItem)
.dataMap.getString("payload") ?: "{}"
val json = JSONObject(payload)
when {
path == "/capgo/context" -> WatchStore.context.value = json
path.startsWith("/capgo/userinfo/") -> handleUserInfo(json)
}
}
}
private fun handleUserInfo(json: JSONObject) {
// Persist or act on queued events from the phone
}
}
When the watch UI opens, also read the current context directly so it shows data that arrived while the app was closed:
suspend fun loadCurrentContext(context: android.content.Context) {
val items = Wearable.getDataClient(context).dataItems.await()
try {
for (item in items) {
if (item.uri.path == "/capgo/context") {
val payload = DataMapItem.fromDataItem(item).dataMap.getString("payload") ?: "{}"
WatchStore.context.value = JSONObject(payload)
}
}
} finally {
items.release()
}
}
Étape 4 : Envoyer des données de l'horloge
Le montre envoie des messages à chaque téléphone connecté. Les messages sont pour les interactions en temps réel, les DataItems pour l'état.
package com.example.shop.wear
import android.content.Context
import com.google.android.gms.wearable.PutDataMapRequest
import com.google.android.gms.wearable.Wearable
import kotlinx.coroutines.tasks.await
import org.json.JSONObject
import java.util.UUID
object PhoneLink {
suspend fun send(context: Context, data: JSONObject, expectReply: Boolean = false) {
val path = if (expectReply) "/capgo/message/withreply" else "/capgo/message"
val bytes = data.toString().toByteArray(Charsets.UTF_8)
val nodes = Wearable.getNodeClient(context).connectedNodes.await()
for (node in nodes) {
Wearable.getMessageClient(context).sendMessage(node.id, path, bytes).await()
}
}
suspend fun updateContext(context: Context, data: JSONObject) {
// Include a timestamp: identical DataItems are not redelivered
data.put("updatedAt", System.currentTimeMillis())
val request = PutDataMapRequest.create("/capgo/context").apply {
dataMap.putString("payload", data.toString())
setUrgent()
}.asPutDataRequest()
Wearable.getDataClient(context).putDataItem(request).await()
}
suspend fun queueEvent(context: Context, data: JSONObject) {
val request = PutDataMapRequest.create("/capgo/userinfo/${UUID.randomUUID()}").apply {
dataMap.putString("payload", data.toString())
setUrgent()
}.asPutDataRequest()
Wearable.getDataClient(context).putDataItem(request).await()
}
}
Lorsque le téléphone reçoit un /capgo/userinfo/... Lorsque le téléphone reçoit un userInfoReceived et supprime l'élément, afin que la file d'attente ne grandisse pas indéfiniment.
Une écran Compose minimal l'unité rassemble :
@Composable
fun OrdersScreen() {
val context = LocalContext.current
val scope = rememberCoroutineScope()
val state by WatchStore.context.collectAsState()
val reply by WatchStore.lastReply.collectAsState()
LaunchedEffect(Unit) { loadCurrentContext(context) }
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text("${state.optInt("openOrders", 0)} open orders")
Button(onClick = {
scope.launch {
PhoneLink.send(context, JSONObject().put("action", "refresh"), expectReply = true)
}
}) { Text("Refresh") }
if (reply.has("count")) Text("Phone: ${reply.getInt("count")}")
}
}
Une écran Compose minimal les relie : callbackId, so the watch cannot match a reply to a specific request. If you fire several requests at once, include your own request ID in the payload and echo it back in the reply.
Étape 5 : Parlez à l'horloge à partir de votre Capacitor code
Le côté TypeScript est le même code que vous écririez pour Apple Watch :
import { Capacitor } from '@capacitor/core';
import { CapgoWatch } from '@capgo/capacitor-watch';
export async function initWearBridge(loadOpenOrders: () => Promise<number>) {
if (Capacitor.getPlatform() === 'web') return;
await CapgoWatch.addListener('messageReceivedWithReply', async ({ message, callbackId }) => {
if (message.action === 'refresh') {
const count = await loadOpenOrders();
await CapgoWatch.replyToMessage({ callbackId, data: { count } });
}
});
await CapgoWatch.addListener('applicationContextReceived', ({ context }) => {
console.log('watch state', context);
});
await CapgoWatch.addListener('userInfoReceived', ({ userInfo }) => {
console.log('queued event from watch', userInfo);
});
}
export async function syncToWatch(openOrders: number) {
const info = await CapgoWatch.getInfo();
if (!info.isSupported || !info.isWatchAppInstalled) return;
await CapgoWatch.updateApplicationContext({ context: { openOrders } });
if (info.isReachable) {
await CapgoWatch.sendMessage({ data: { toast: 'Orders updated' } });
}
}
Spécificités Android à connaître :
sendMessagerefuse avec « Aucun appareil Wear OS connecté trouvé » lorsque pas de nœud d'horloge est connecté.isPairedetisReachableles deux signifient « au moins un nœud Wear OS est connecté ».isWatchAppInstalledsignifie qu'un nœud annoncecapgo_watch.- La disponibilité n'est pas envoyée comme événement sur Android aujourd'hui. Appelez
getInfo()lorsque vous avez besoin d'une valeur fraîche, par exemple lorsque l'écran gagne le focus. - Les réponses en attente expirent après cinq minutes du côté du téléphone.
Quel canal utiliser
| Besoin | Utilisez | Pourquoi |
|---|---|---|
| Live button press, watch app open | sendMessage / /capgo/message |
Rapide, mais abandonné si aucun nœud n'est connecté |
| Ce que la montre doit afficher | updateApplicationContext / /capgo/context |
Latest value is kept and synced later |
| Événements qui ne doivent pas être perdus | transferUserInfo / /capgo/userinfo/{uuid} |
Chaque événement est son propre DataItem |
Limits de la couche de données
- Les DataItems sont limités à environ 100 Ko. Les messages doivent rester petits aussi. Envoyez des IDs et récupérez le contenu de votre backend.
- Écrire un DataItem avec le même contenu n'entraîne pas de changement d'événement. Ajoutez une date et heure pour obtenir un événement frais.
- Un message à la montre démarre votre
WearableListenerServicepas votre interface utilisateur. - On l'application du téléphone, les écouteurs du plugin existent pendant que le processus d'application Capacitor fonctionne. Les messages envoyés pendant qu'il est mort ne sont pas réexpédiés vers JavaScript. Les DataItems restent dans la couche de données, utilisez donc le contexte pour tout ce que le téléphone doit voir plus tard.
- Les payloads sont des objets JSON. Les tableaux de niveau supérieur et les valeurs non-JSON ne seront pas parsés.
Testez avec des émulateurs et des appareils
- Créez un émulateur de téléphone avec une image système Google Play et un émulateur Wear OS dans Gestionnaire d'appareils.
- Utilisez l'assistant de pairing Wear OS dans Android Studio pour les pairer. Cela installe l'application de compagnon sur l'emulateur de téléphone.
- Exécutez la
wearconfiguration sur l'émulateur de montre, puis exécutez votre application Capacitor sur le téléphone avecbunx cap run android. - Appelle
CapgoWatch.getInfo()à partir du téléphone.isWatchAppInstalled: trueconfirme la correspondance de capacité et de package.
Activer les options de développeur et la débogage ADB par Wi-Fi sur l'horloge, puis connectez-vous adb connect <watch-ip>:<port>.
Troubleshooting
isSupported: false. Google Play services ou le Wear OS API est manquant sur le téléphone. Fréquent sur les émulateurs sans images Play.
isWatchAppInstalled: false tandis qu'un montre est connecté. Le capgo_watch la capacité est manquante ou mal orthographiée, ou l'application de montre n'est pas installée sur cette montre.
Les messages ne parviennent jamais sur la montre. Different applicationId ou des clés de signature différentes. Comparez ./gradlew :app:signingReport et ./gradlew :wear:signingReport.
Mises à jour de contexte cessent d'arriver. Vous écrivez le même payload plusieurs fois. Ajoutez un champ changeant.
Release build works on debug but not on Play. Si vous utilisez la signature d'application Play, les artefacts du téléphone et de l'horloge doivent être signés par la même clé de signature d'application dans la même application Play Console.
R8 supprime votre service. Keep classes referenced only from the manifest if you use custom shrinking rules.
Publier sur Google Play
L'application Wear OS est livrée sous la même application Play Console que l'application téléphone, car elles partagent le nom de package. Ajoutez la forme facteur Wear OS dans Play Console, téléchargez le bundle Wear OS sur une piste de version Wear OS et ajoutez des captures d'écran d'horloge. Google évalue les applications Wear OS en fonction de ses lignes directrices de qualité Wear OS, donc vérifiez les dispositions d'écran circulaire et les tailles de cibles de toucher.
Pour les mises à jour, pouvez pousser des correctifs vers l'application téléphone web sans une mise à jour de magasin, ce qui couvre les bogues dans votre pont TypeScript Capgo. Les modifications Kotlin sur l'horloge passent par Google Play. Versionnez vos payloads JSON afin qu'un bundle téléphone plus récent reste compatible avec une version horloge plus ancienne. Si vous construisez l'application téléphone dans le cloud, can push fixes to the phone app’s web layer without a store release, which covers bugs in your TypeScript bridge code. Kotlin changes on the watch go through Google Play. Version your JSON payloads so a newer phone bundle stays compatible with an older watch build. If you build the phone app in the cloud, Capgo Création Gère la Capacitor construction et l'authentification Android, et vous pouvez générer un coffre-fort correspondant avec le Générateur de clés de sécurité Android.
Wrap-up
Ainsi, une application Wear OS pour un Capacitor s'articule en trois éléments : @capgo/capacitor-watch sur le téléphone, un module Kotlin qui partage l'ID et la clé de signature de l'application du téléphone, et les /capgo/* Obtenez d'abord la capacité et la signature correctes, puis le code est court.