Langsung ke Konten Utama

How to Build a Wear OS App for Your Aplikasi Capacitor

Build a Wear OS app for your Aplikasi Capacitor app: add a wear module, write the Kotlin watch side, and sync data over the Data Layer with @capgo/capacitor-watch.

Kredit Artikel

Martin Donadieu

Pengarang

Valeria

Pengulas

Jordan

Pengedit

How to Build a Wear OS App for Your Aplikasi Capacitor

To build a Wear OS app for your Aplikasi Capacitor app, add a native Wear OS module to the android/ project, berikan ID aplikasi dan kunci tanda tangan yang sama seperti aplikasi ponsel, dan hubungkan kedua aplikasi tersebut dengan @capgo/capacitor-watchyang menggunakan Layer Data Wear OS dari Google di Android. Telepon menjaga TypeScript code, dan jam tangan menjalankan aplikasi Kotlin kecil yang berkomunikasi dengan JSON-nya.

Petunjuk ini menjelaskan cara mengatur modul, jalur Layer Data yang plugin gunakan secara tepat, aplikasi Kotlin code pada jam tangan, aplikasi TypeScript code pada telepon, melakukan tes dengan emulator, dan menerbitkan di Google Play.

Cara Capacitor ke bridge Wear OS berfungsi

Wear OS tidak memiliki Capacitor runtime, jadi aplikasi jam tangan adalah native. Plugin berbicara protokol Layer Data API (play-services-wearable) pada telepon, dan aplikasi jam tangan berbicara protokol yang sama.

Metode / event plugin Mechanisme Layer Data Jalur
sendMessage (telepon ke jam tangan) MessageClient /capgo/message
messageReceived (jam tangan ke telepon) 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}

Message membawa JSON UTF-8 sebagai byte mentah. DataItem membawa JSON yang sama sebagai string di bawah kunci DataMap payloadTelepon mengecek aplikasi jam dengan mencari kemampuan yang tersedia capgo_watch.

Mengetahui jalur ini adalah trik utama. Setelah jam tangan membaca dan menulisnya, setiap metode plugin berfungsi

Apa yang Anda butuhkan

  • Aplikasi Capacitor 8 dengan platform Android ditambahkan (petunjuk pembaruan jika Anda tertinggal).
  • Aplikasi Android Studio yang baru dengan gambar sistem Wear OS diinstal.
  • Telepon dengan layanan Google Play. Layer Data bergantung pada itu, jadi telepon tanpa layanan Play melaporkan isSupported: false.
  • Aplikasi jam tangan Wear OS atau emulator yang dipasangkan dengan telepon Android. Jam tangan Wear OS yang dipasangkan dengan iPhone tidak dapat mencapai aplikasi Anda.

Langkah 1: Pasang plugin

bun add @capgo/capacitor-watch
bunx cap sync android

Kemasan yang sama mengelola Apple Watch di iOS, jadi aplikasi lintas platform memerlukan satu import. Lihat Dokumentasi Plugin untuk referensi lengkap, dan kami Trik Membuat Aplikasi Pakaian Apple Watch untuk sisi iOS.

Langkah 2: Tambahkan modul Wear OS ke proyek Android

Buka android/ dalam Studio Android ("bunx cap open androiddan gunakan Buka File > Baru > Modul Baru > Wear OSMenggunakan Android Studio untuk menghasilkan modul akan memberikan Anda file Gradle yang sesuai dengan versi Android Gradle Plugin dan Kotlin Anda, yang lebih aman daripada menyalin file build dari posting blog. Beri nama modul wear.

Pengguna Wizard include ':wear' Ke android/settings.gradleJaga, dan biarkan baris-baris yang diatur oleh Capacitor tetap sama.

Ada dua aturan yang ditegakkan oleh Layer Data.

  1. Sama. applicationId. Dalam wear/build.gradle, tetapkan applicationId ke nilai yang tepat di android/app/build.gradle. Nilai namespace dapat berbeda, tetapi ID aplikasi tidak boleh.
  2. Sertifikat tanda tangan yang sama. Jika Anda membuat aplikasi debug di satu mesin, maka keystore debug akan digunakan, sehingga biasanya berfungsi secara lokal dan kemudian gagal dalam versi rilis. Tandatangani aplikasi rilis Wear dengan kunci unggah yang sama seperti aplikasi ponsel.

Jika salah satu aturan ini dilanggar, dua aplikasi tidak pernah melihat satu sama lain dan tidak ada yang dapat memperbaikinya di dalam code.

Ketergantungan dan versi minimum SDK

Wear OS 3 dan seterusnya berdasarkan API 30, jadi minSdk 30 adalah lantai yang praktis untuk aplikasi jam baru. Tambahkan Layer Data dan bantuan 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
}

Kemukakan kemampuan

Buat 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>

The string must be capgo_watch, karena itu adalah apa plugin yang ditanyakan untuk menghitung isWatchAppInstalled.

Mark aplikasi sebagai aplikasi jam

Dalam wear/src/main/AndroidManifest.xml, sihir ajaib menambahkan <uses-feature android:name="android.hardware.type.watch" />. Putuskan apakah aplikasi berfungsi tanpa ponsel. Sebuah teman yang hanya menampilkan data ponsel harus mengumumkan:

<meta-data
    android:name="com.google.android.wearable.standalone"
    android:value="false" />

Langkah 3: Terima data di jam

context WearableListenerService Biarkan sistem mengirimkan Data Layer events bahkan ketika antarmuka UI jam tangan ditutup. Daftarkan dengan filter untuk /capgo jalur:

<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>

Penyimpanan bersama kecil yang UI dapat mengamati:

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

Pelayanan menganalisis setiap jalur:

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

Ketika antarmuka jam tangan dibuka, baca konteks saat ini secara langsung sehingga menampilkan data yang telah datang saat aplikasi ditutup.

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

Langkah 4: Kirim data dari jam

Pengiriman jam tangan ke setiap node telepon terhubung. Pesan-pesan ini untuk interaksi langsung, DataItems untuk keadaan.

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

Ketika ponsel menerima sinyal /capgo/userinfo/... item yang dihasilkannya userInfoReceived dan menghapus item tersebut, sehingga antrian tidak akan terus tumbuh.

Sebuah layar Compose minimal yang menghubungkannya:

@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")}")
    }
}

Perangkat telepon menghasilkan callbackIdJadi, jam tangan tidak dapat menemukan jawaban untuk permintaan tertentu. Jika Anda mengirimkan beberapa permintaan sekaligus, masukkan ID permintaan Anda sendiri ke dalam payload dan ulangi kembali dalam jawaban.

Langkah 5: Berbicara dengan jam tangan dari Capacitor code

The TypeScript side is the same code you would write for 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' } });
  }
}

Spesifikasi Android yang perlu diketahui:

  • sendMessage Tidak menemukan perangkat Wear OS apa pun ketika tidak ada node jam terhubung.
  • isPaired dan isReachable keduanya berarti “setidaknya satu node Wear OS terhubung”. isWatchAppInstalled berarti sebuah node mengiklankan capgo_watch.
  • Ketersediaan tidak dipush sebagai event pada Android hari ini. Panggil getInfo() ketika Anda membutuhkan nilai yang segar, misalnya ketika layar mendapatkan fokus.
  • Pertanyaan yang belum dijawab akan berakhir setelah lima menit pada sisi ponsel.

Mana saluran yang harus digunakan

Perlu Menggunakan Mengapa
Tombol Live tekan, aplikasi jam tangan terbuka sendMessage / /capgo/message Cepat, tetapi jatuh jika tidak ada node yang terhubung
Apa yang harus ditampilkan pada jam tangan updateApplicationContext / /capgo/context Nilai terbaru disimpan dan disinkronkan kemudian
Event yang tidak boleh hilang transferUserInfo / /capgo/userinfo/{uuid} Setiap event adalah DataItem sendiri

Keterbatasan Layer Data

  • DataItem terbatas sekitar 100 KB. Pesan harus tetap kecil juga. Kirim ID dan ambil konten dari backend Anda.
  • Menggunakan DataItem dengan konten identik tidak akan memicu perubahan event. Tambahkan timestamp jika Anda membutuhkan event yang segar.
  • A pesan ke jam tangan memulai sesuatu WearableListenerService, bukan UI Anda.
  • Pada ponsel, pengguna plugin ada saat proses aplikasi Capacitor berjalan. Pesan yang dikirim saat aplikasi mati tidak akan diputar kembali ke JavaScript. DataItem tetap ada di Layer Data, jadi gunakan konteks untuk apa pun yang ponsel harus lihat kemudian.
  • Payloads adalah objek JSON. Array tingkat atas dan nilai non-JSON tidak akan diproses.

Menguji dengan emulator dan perangkat

  1. Buat emulator ponsel dengan gambar sistem Google Play dan emulator Wear OS di Device Manager.
  2. Gunakan asisten pairing Wear OS Android Studio untuk memasangkannya. Ini akan menginstal aplikasi kompanen pada emulator ponsel.
  3. Jalankan wear konfigurasi pada emulator jam tangan, kemudian jalankan aplikasi Capacitor Anda pada ponsel dengan bunx cap run android.
  4. Panggilan CapgoWatch.getInfo() mengkonfirmasi kemampuan dan paket sesuai. isWatchAppInstalled: true mengkonfirmasi kemampuan dan paket sesuai.

Untuk jam tangan fisik, aktifkan opsi pengembang dan debugging ADB melalui Wi-Fi pada jam, kemudian hubungkan dengan adb connect <watch-ip>:<port>.

Troubleshooting

isSupported: false. Jasa Google Play atau Wear OS API tidak terinstal di ponsel. Biasanya terjadi pada emulator tanpa gambar Play.

isWatchAppInstalled: false Saat jam tangan terhubung. The capgo_watch Kemampuan tersebut tidak ada atau salah eja, atau aplikasi jam tidak terinstal di jam tersebut.

Pesan tidak pernah sampai di jam. Different applicationId atau kunci tanda tangan yang berbeda. Bandingkan ./gradlew :app:signingReport and ./gradlew :wear:signingReport.

Pembaruan konteks berhenti sampai. Anda menulis payload yang sama berulang-ulang. Tambahkan bidang yang berubah.

Rilis build berfungsi pada debug tetapi tidak pada Play. Jika Anda menggunakan Play App Signing, baik artefak ponsel dan jam tangan harus ditandatangani oleh kunci tanda tangan aplikasi yang sama di aplikasi Play Console yang sama.

R8 menghilangkan layanan Anda. Tetapkan kelas yang hanya dapat diakses dari manifest jika Anda menggunakan aturan pemangkasan kustom.

Penerbitan di Google Play

Aplikasi Wear OS dikirimkan di bawah aplikasi Play Console yang sama dengan aplikasi ponsel, karena mereka memiliki nama paket yang sama. Tambahkan faktor bentuk Wear OS di Play Console, unggah bundle Wear OS ke jalur rilis Wear OS, dan tambahkan layar tangkapan jam tangan. Google meninjau aplikasi Wear OS terhadap pedoman kualitas Wear OS, jadi periksa tata letak layar lingkaran dan ukuran target sentuh.

Untuk pembaruan, Capgo pembaruan hidup 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 Bangun handles the Capacitor Android build and signing, and you can generate a matching keystore with the Generator Kunci Keamanan Android.

Penutupan

Apa itu aplikasi Wear OS untuk sebuah aplikasi Capacitor? Ini terdiri dari tiga bagian: @capgo/capacitor-watch di ponsel, sebuah modul Kotlin yang berbagi ID aplikasi dan kunci tanda tangan ponsel, serta /capgo/* Lapisan Data jalur di antara. Dapatkan kemampuan dan tanda tangan yang tepat terlebih dahulu, kemudian code yang singkat.

Update langsung untuk aplikasi Capacitor

Ketika ada bug layer web yang hidup, kirimkan perbaikan melalui Capgo bukan menunggu hari-hari untuk persetujuan toko aplikasi. Pengguna mendapatkan update di latar belakang sementara perubahan native tetap dalam jalur review normal.

Terbaru dari Blog Kami

Capgo memberikan Anda wawasan terbaik yang Anda butuhkan untuk membuat aplikasi seluler profesional yang sebenarnya.