Migliora l'esperienza di salute e fitness della tua app estendendola ai dispositivi indossabili dotati di Wear OS.
Aggiungi un modulo Wear OS
Android Studio offre una pratica procedura guidata per aggiungere un modulo Wear OS alla tua app. Nel menu File > Nuovo modulo, seleziona Wear OS, come mostrato nella seguente immagine:
È importante notare che l'SDK minimo deve essere API 30 o successivo per consentirti di utilizzare la versione più recente di Health Services. Health Services semplifica il monitoraggio delle metriche e la registrazione dei dati configurando automaticamente i sensori di salute.
Dopo aver completato la procedura guidata, sincronizza il tuo progetto. Viene visualizzata la seguente configurazione Esegui:
In questo modo puoi eseguire il modulo Wear OS su un dispositivo indossabile. Puoi scegliere tra due opzioni:
Eseguilo su un emulatore.
Eseguirli su un dispositivo reale.
L'esecuzione della configurazione esegue il deployment dell'app nell'emulatore o sul dispositivo Wear OS e mostra un'esperienza "Hello World". Questa è la configurazione di base dell'interfaccia utente, che utilizza Scrivi per Wear OS, per iniziare a usare l'app.
Aggiungi servizi sanitari e Hilt
Integra le seguenti librerie nel tuo modulo Wear OS:
- Servizi sanitari: rende l'accesso ai sensori e ai dati sull'orologio molto comodo e a maggiore efficienza energetica.
- Hilt: consente un'efficace inserimento e gestione delle dipendenze.
Crea Gestore servizi sanitari
Per rendere l'utilizzo di Health Services un po' più pratico ed esporre un'API più piccola e più 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 di 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 all'attività fisica richiedono l'autorizzazione BODY_SENSORS
. Se non l'hai ancora fatto, dichiara l'autorizzazione BODY_SENSORS
nel file manifest della tua 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 la tua app su un dispositivo fisico, dovresti iniziare l'aggiornamento dei dati.
A partire da Wear OS 4, gli emulatori mostrano automaticamente anche i dati dei test. Nelle versioni precedenti, puoi simulare lo stream di dati del sensore. In una finestra del terminale, esegui questo comando ADB:
adb shell am broadcast \
-a "whs.USE_SYNTHETIC_PROVIDERS" \
com.google.android.wearable.healthservices
Per visualizzare diversi valori del battito cardiaco, prova a simulare diversi allenamenti. 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 di:
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 dei dati del battito cardiaco
Con l'autorizzazione BODY_SENSORS
concessa, puoi leggere la frequenza cardiaca dell'utente (heartRateMeasureFlow()
) in HealthServicesManager
. Nell'interfaccia utente dell'app Wear OS viene visualizzato il valore corrente della frequenza cardiaca, misurato dal sensore sul 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 dell'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 alla salute e all'attività fisica a un dispositivo portatile, utilizza la classe DataClient
in Health Services. Il seguente snippet di codice mostra come inviare i dati sul battito cardiaco 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 sul telefono
Per ricevere i dati sul telefono, 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, nota alcuni dettagli interessanti:
- L'annotazione
@AndroidEntryPoint
ci consente di utilizzare Hilt in questo corso @Inject lateinit var heartRateMonitor: HeartRateMonitor
inserirà effettivamente 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 i valori del battito cardiaco ricevuti 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
.
Il bit finale è la dichiarazione di Service
nell'applicazione del telefono
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>
Mostrare dati in tempo reale su un dispositivo portatile
Nella parte dell'app eseguita su un dispositivo portatile, inserisci HeartRateMonitor
nel costruttore del modello di visualizzazione. Questo oggetto HeartRateMonitor
osserva i dati della frequenza cardiaca ed emette aggiornamenti UI secondo necessità.