Tingkatkan pengalaman kesehatan dan kebugaran aplikasi Anda dengan memperluasnya ke perangkat wearable yang didukung oleh Wear OS.
Menambahkan modul Wear OS
Android Studio menyediakan wizard praktis untuk menambahkan modul Wear OS ke aplikasi Anda. Di menu File > New Module, pilih Wear OS, seperti yang ditunjukkan pada gambar berikut:
Perlu diperhatikan bahwa SDK Minimum harus API 30 atau yang lebih tinggi agar Anda dapat menggunakan Layanan Kesehatan versi terbaru. Health Services mempermudah pelacakan metrik dan perekaman data dengan mengonfigurasi sensor kesehatan secara otomatis.
Setelah Anda menyelesaikan wizard, sinkronkan project Anda. Konfigurasi Run berikut akan muncul:
Tindakan ini memungkinkan Anda menjalankan modul Wear OS di perangkat wearable. Anda memiliki dua opsi:
Jalankan di emulator.
Jalankan di perangkat sungguhan.
Menjalankan konfigurasi akan men-deploy aplikasi ke emulator atau perangkat Wear OS dan menampilkan pengalaman "hello world". Ini adalah penyiapan UI dasar, menggunakan Compose untuk Wear OS, untuk mulai menggunakan aplikasi Anda.
Menambahkan Layanan Kesehatan dan Hilt
Integrasikan library berikut ke dalam modul Wear OS Anda:
- Layanan Kesehatan: memudahkan akses ke sensor dan data di smartwatch serta lebih hemat daya.
- Hilt: Memungkinkan injeksi dan pengelolaan dependensi yang efektif.
Membuat Pengelola Layanan Kesehatan
Agar penggunaan Health Services menjadi lebih mudah, dan mengekspos API yang lebih kecil dan lancar, Anda dapat membuat wrapper seperti ini:
private const val TAG = "WATCHMAIN"
class HealthServicesManager(context: Context) {
private val measureClient = HealthServices.getClient(context).measureClient
suspend fun hasHeartRateCapability() = runCatching {
val capabilities = measureClient.getCapabilities()
(DataType.HEART_RATE_BPM in capabilities.supportedDataTypesMeasure)
}.getOrDefault(false)
/**
* Returns a cold flow. When activated, the flow will register a callback for heart rate data
* and start to emit messages. When the consuming coroutine is canceled, the measure callback
* is unregistered.
*
* [callbackFlow] creates a bridge between a callback-based API and Kotlin flows.
*/
@ExperimentalCoroutinesApi
fun heartRateMeasureFlow(): Flow<MeasureMessage> = callbackFlow {
val callback = object : MeasureCallback {
override fun onAvailabilityChanged(dataType: DeltaDataType<*, *>, availability: Availability) {
// Only send back DataTypeAvailability (not LocationAvailability)
if (availability is DataTypeAvailability) {
trySendBlocking(MeasureMessage.MeasureAvailability(availability))
}
}
override fun onDataReceived(data: DataPointContainer) {
val heartRateBpm = data.getData(DataType.HEART_RATE_BPM)
Log.d(TAG, "💓 Received heart rate: ${heartRateBpm.first().value}")
trySendBlocking(MeasureMessage.MeasureData(heartRateBpm))
}
}
Log.d(TAG, "⌛ Registering for data...")
measureClient.registerMeasureCallback(DataType.HEART_RATE_BPM, callback)
awaitClose {
Log.d(TAG, "👋 Unregistering for data")
runBlocking {
measureClient.unregisterMeasureCallback(DataType.HEART_RATE_BPM, callback)
}
}
}
}
sealed class MeasureMessage {
class MeasureAvailability(val availability: DataTypeAvailability) : MeasureMessage()
class MeasureData(val data: List<SampleDataPoint<Double>>) : MeasureMessage()
}
Setelah membuat modul Hilt untuk mengelolanya, gunakan cuplikan berikut:
@Module
@InstallIn(SingletonComponent::class)
internal object DataModule {
@Provides
@Singleton
fun provideHealthServices(@ApplicationContext context: Context): HealthServicesManager = HealthServicesManager(context)
}
Anda dapat menyuntikkan HealthServicesManager sebagai dependensi Hilt lainnya.
HealthServicesManager baru menyediakan metode heartRateMeasureFlow() yang
mendaftarkan pemroses untuk monitor detak jantung dan memancarkan data yang diterima.
Mengaktifkan pembaruan data di perangkat wearable
Pembaruan data terkait kebugaran memerlukan izin BODY_SENSORS. Jika
belum melakukannya, deklarasikan izin BODY_SENSORS di file manifes
aplikasi Anda. Kemudian, minta izin, seperti yang ditunjukkan dalam cuplikan ini:
val permissionState = rememberPermissionState(
permission = Manifest.permission.BODY_SENSORS,
onPermissionResult = { granted -> /* do something */ }
)
[...]
if (permissionState.status.isGranted) {
// do something
} else {
permissionState.launchPermissionRequest()
}
Jika Anda menguji aplikasi di perangkat fisik, data akan mulai diperbarui.
Mulai Wear OS 4, emulator juga menampilkan data pengujian secara otomatis. Pada versi sebelumnya, Anda dapat menyimulasikan aliran data dari sensor. Di jendela terminal, jalankan perintah ADB ini:
adb shell am broadcast \
-a "whs.USE_SYNTHETIC_PROVIDERS" \
com.google.android.wearable.healthservices
Untuk melihat nilai detak jantung yang berbeda, coba simulasikan latihan yang berbeda. Perintah ini menyimulasikan jalan kaki:
adb shell am broadcast \
-a "whs.synthetic.user.START_WALKING" \
com.google.android.wearable.healthservices
Perintah ini menyimulasikan lari:
adb shell am broadcast \
-a "whs.synthetic.user.START_RUNNING" \
com.google.android.wearable.healthservices
Untuk berhenti menyimulasikan data, jalankan perintah ini:
adb shell am broadcast -a \
"whs.USE_SENSOR_PROVIDERS" \
com.google.android.wearable.healthservices
Membaca data detak jantung
Dengan izin BODY_SENSORS yang diberikan, Anda dapat membaca detak jantung pengguna
(heartRateMeasureFlow()) di HealthServicesManager. Di UI aplikasi Wear OS, nilai detak jantung saat ini akan muncul, yang diukur oleh sensor pada perangkat wearable.
Di ViewModel, mulai kumpulkan data menggunakan objek alur detak jantung,
seperti yang ditunjukkan dalam cuplikan berikut:
val hr: MutableState<Double> = mutableStateOf(0.0)
[...]
healthServicesManager
.heartRateMeasureFlow()
.takeWhile { enabled.value }
.collect { measureMessage ->
when (measureMessage) {
is MeasureData -> {
val latestHeartRateValue = measureMessage.data.last().value
hr.value = latestHeartRateValue
}
is MeasureAvailability -> availability.value =
measureMessage.availability
}
}
Gunakan objek composable yang mirip dengan berikut untuk menampilkan data live di UI aplikasi Anda:
val heartRate by viewModel.hr
Text(
text = "Heart Rate: $heartRate",
style = MaterialTheme.typography.display1
)
Mengirim data ke perangkat genggam
Untuk mengirim data kesehatan dan kebugaran ke perangkat genggam, gunakan class DataClient
di Layanan Kesehatan. Cuplikan kode berikut menunjukkan cara mengirim data detak jantung yang sebelumnya dikumpulkan oleh aplikasi Anda:
class HealthServicesManager(context: Context) {
private val dataClient by lazy { Wearable.getDataClient(context) }
[...]
suspend fun sendToHandheldDevice(heartRate: Int) {
try {
val result = dataClient
.putDataItem(PutDataMapRequest
.create("/heartrate")
.apply { dataMap.putInt("heartrate", heartRate) }
.asPutDataRequest()
.setUrgent())
.await()
Log.d(TAG, "DataItem saved: $result")
} catch (cancellationException: CancellationException) {
throw cancellationException
} catch (exception: Exception) {
Log.d(TAG, "Saving DataItem failed: $exception")
}
}
}
Menerima data di ponsel
Untuk menerima data di ponsel, buat
WearableListenerService:
@AndroidEntryPoint
class DataLayerListenerService : WearableListenerService() {
@Inject
lateinit var heartRateMonitor: HeartRateMonitor
override fun onDataChanged(dataEvents: DataEventBuffer) {
dataEvents.forEach { event ->
when (event.type) {
DataEvent.TYPE_CHANGED -> {
event.dataItem.run {
if (uri.path?.compareTo("/heartrate") == 0) {
val heartRate = DataMapItem.fromDataItem(this)
.dataMap.getInt(HR_KEY)
Log.d("DataLayerListenerService",
"New heart rate value received: $heartRate")
heartRateMonitor.send(heartRate)
}
}
}
DataEvent.TYPE_DELETED -> {
// DataItem deleted
}
}
}
}
}
Setelah menyelesaikan langkah ini, perhatikan beberapa detail menarik:
- Anotasi
@AndroidEntryPointmemungkinkan kita menggunakan Hilt di class ini @Inject lateinit var heartRateMonitor: HeartRateMonitormemang akan menyuntikkan dependensi di class ini- Class mengimplementasikan
onDataChanged()dan menerima kumpulan peristiwa yang dapat Anda parsing dan gunakan
Logika HeartRateMonitor berikut memungkinkan Anda mengirim nilai detak jantung yang diterima ke bagian lain dari codebase aplikasi Anda:
class HeartRateMonitor {
private val datapoints = MutableSharedFlow<Int>(extraBufferCapacity = 10)
fun receive(): SharedFlow<Int> = datapoints.asSharedFlow()
fun send(hr: Int) {
datapoints.tryEmit(hr)
}
}
Bus data menerima peristiwa dari metode onDataChanged() dan menyediakannya untuk pengamat data menggunakan SharedFlow.
Bagian terakhir adalah deklarasi Service di aplikasi ponsel
AndroidManifest.xml:
<service
android:name=".DataLayerListenerService"
android:exported="true">
<intent-filter>
<!-- listeners receive events that match the action and data filters -->
<action android:name="com.google.android.gms.wearable.DATA_CHANGED" />
<data
android:host="*"
android:pathPrefix="/heartrate"
android:scheme="wear" />
</intent-filter>
</service>
Menampilkan data real-time di perangkat genggam
Di bagian aplikasi yang berjalan di perangkat genggam, masukkan
HeartRateMonitor ke dalam konstruktor model tampilan Anda. Objek HeartRateMonitor
ini mengamati data detak jantung dan memancarkan update UI sesuai kebutuhan.