Migliora l'esperienza di salute e fitness della tua app espandendola su indossabile Dispositivi con Wear OS.
Aggiungi un modulo per Wear OS
Android Studio ti offre una pratica procedura guidata per aggiungere un modulo per Wear OS alla tua app. Nella File > Nuovo modulo, seleziona Wear OS, come mostrato di seguito immagine:
È importante notare che l'SDK minimo deve essere API 30 o versioni successive. per consentirti di utilizzare la versione più recente di Health Services. Servizi sanitari semplifica il monitoraggio delle metriche e la registrazione di dati configurando l'integrità i sensori automaticamente.
Dopo aver completato la procedura guidata, sincronizza il progetto. Il seguente comando Esegui dell'immagine:
Questo ti consente di eseguire il modulo Wear OS su un dispositivo indossabile. Sono disponibili due opzioni:
Esegui su un emulatore.
Esegui su un dispositivo reale.
L'esecuzione della configurazione comporta il deployment dell'app nell'emulatore Wear OS oppure dispositivo e mostra "Hello World" un'esperienza senza intervento manuale. Questa è la configurazione di base dell'interfaccia utente, utilizzando Scrivi per Wear OS per iniziare a usare la tua app.
Aggiungi servizi sanitari ed Hilt
Integra le seguenti librerie nel tuo modulo Wear OS:
- Health Services: consente di accedere a sensori e dati sull'orologio molto conveniente e più efficiente dal punto di vista energetico.
- Hilt: consente un'inserimento e una gestione efficaci delle dipendenze.
Crea il responsabile dei servizi sanitari
Per rendere l'utilizzo dei servizi sanitari un po' più comodo ed esporre un modello e fluida, puoi creare un wrapper come questo:
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()
}
Dopo aver creato il modulo Hilt per gestirlo, utilizza il seguente snippet:
@Module
@InstallIn(SingletonComponent::class)
internal object DataModule {
@Provides
@Singleton
fun provideHealthServices(@ApplicationContext context: Context): HealthServicesManager = HealthServicesManager(context)
}
puoi inserire HealthServicesManager
come qualsiasi altra dipendenza Hilt.
Il nuovo HealthServicesManager
fornisce un metodo heartRateMeasureFlow()
che
registra un listener per il cardiofrequenzimetro ed emette i dati ricevuti.
Attiva gli aggiornamenti dei dati sui dispositivi indossabili
Gli aggiornamenti dei dati relativi al fitness richiedono l'autorizzazione BODY_SENSORS
. Se
non lo hai già fatto, dichiara l'autorizzazione BODY_SENSORS
nel tuo
il file manifest dell'app. Quindi, richiedi l'autorizzazione, come mostrato in questo snippet:
val permissionState = rememberPermissionState(
permission = Manifest.permission.BODY_SENSORS,
onPermissionResult = { granted -> /* do something */ }
)
[...]
if (permissionState.status.isGranted) {
// do something
} else {
permissionState.launchPermissionRequest()
}
Se testi l'app su un dispositivo fisico, i dati dovrebbero iniziare l'aggiornamento.
A partire da Wear OS 4, gli emulatori mostrano automaticamente anche i dati dei test. Alla precedente puoi simulare lo stream di dati dal sensore. In un terminale esegui questo comando ADB:
adb shell am broadcast \
-a "whs.USE_SYNTHETIC_PROVIDERS" \
com.google.android.wearable.healthservices
Per visualizzare i diversi valori del battito cardiaco, prova a simulare allenamenti diversi. Questo comando simula una camminata:
adb shell am broadcast \
-a "whs.synthetic.user.START_WALKING" \
com.google.android.wearable.healthservices
Questo comando simula l'esecuzione:
adb shell am broadcast \
-a "whs.synthetic.user.START_RUNNING" \
com.google.android.wearable.healthservices
Per interrompere la simulazione dei dati, esegui questo comando:
adb shell am broadcast -a \
"whs.USE_SENSOR_PROVIDERS" \
com.google.android.wearable.healthservices
Lettura dati battito cardiaco
Con l'autorizzazione BODY_SENSORS
concessa, puoi leggere la frequenza cardiaca dell'utente
(heartRateMeasureFlow()
) in HealthServicesManager
. Nell'app Wear OS
UI, appare il valore del battito cardiaco corrente, misurato dal sensore sulla
dispositivo indossabile.
In ViewModel
, inizia a raccogliere dati utilizzando l'oggetto Flusso del battito cardiaco,
come mostrato nel seguente snippet:
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
}
}
Utilizza un oggetto componibile simile al seguente per visualizzare i dati in tempo reale nell'interfaccia utente della tua app:
val heartRate by viewModel.hr
Text(
text = "Heart Rate: $heartRate",
style = MaterialTheme.typography.display1
)
Inviare dati a un dispositivo portatile
Per inviare dati relativi a salute e attività fisica a un dispositivo portatile, utilizza DataClient
in Servizi sanitari. Il seguente snippet di codice mostra come inviare un cuore
Valuta i dati raccolti in precedenza dalla tua app:
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")
}
}
}
Ricevi i dati sullo smartphone
Per ricevere i dati sullo smartphone, crea un
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
}
}
}
}
}
Al termine di questo passaggio, noterai alcuni dettagli interessanti:
- L'annotazione
@AndroidEntryPoint
ci consente di utilizzare Hilt in questa classe @Inject lateinit var heartRateMonitor: HeartRateMonitor
inserisci una dipendenza in questa classe- La classe implementa
onDataChanged()
e riceve una raccolta di eventi che puoi analizzare e utilizzare
La seguente logica HeartRateMonitor
ti consente di inviare il battito cardiaco ricevuto
a un'altra parte del codebase della tua app:
class HeartRateMonitor {
private val datapoints = MutableSharedFlow<Int>(extraBufferCapacity = 10)
fun receive(): SharedFlow<Int> = datapoints.asSharedFlow()
fun send(hr: Int) {
datapoints.tryEmit(hr)
}
}
Un bus di dati riceve gli eventi dal metodo onDataChanged()
e li rende
disponibili per gli osservatori di dati utilizzando un SharedFlow
.
L'ultima parte è la dichiarazione di Service
nell'applicazione per smartphone
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>
Mostra dati in tempo reale su un dispositivo portatile
Nella parte dell'app eseguita su un dispositivo portatile, inserisci
HeartRateMonitor
nel costruttore del modello della vista. Questo HeartRateMonitor
osserva i dati del battito cardiaco ed emette aggiornamenti UI secondo necessità.