Expande tu app a wearable para mejorar la experiencia de salud y fitness dispositivos con tecnología Wear OS
Cómo agregar un módulo de Wear OS
Android Studio proporciona un asistente útil para agregar un módulo de Wear OS a tu app. En la pestaña Archivo > En el menú New Module, selecciona Wear OS, como se muestra a continuación. imagen:
Es importante tener en cuenta que el Minimum SDK debe ser API 30 o superior. para permitirle usar la versión más reciente de los Servicios de salud. Servicios de salud facilita el seguimiento de las métricas y el registro de datos mediante la configuración del estado automáticamente.
Después de completar los pasos del asistente, sincroniza tu proyecto. Con el siguiente comando Run, configuración de Terraform:
De esta manera, podrás ejecutar el módulo de Wear OS en un dispositivo wearable. Tienes dos opciones:
Ejecuta la app en un emulador.
Ejecuta la app en un dispositivo real.
Cuando se ejecuta la configuración, se implementa la app en el emulador de Wear OS. y se muestra el mensaje “Hello World” una experiencia fluida a los desarrolladores. Esta es la configuración básica de la IU, con Compose para Wear OS, para comenzar a usar tu app.
Cómo agregar servicios de salud y Hilt
Integra las siguientes bibliotecas en tu módulo de Wear OS:
- Servicios de salud: Facilita el acceso a los sensores y los datos del reloj. muy conveniente y eficiente en términos de energía.
- Hilt: Permite una inserción y administración eficaces de dependencias.
Cómo crear el Administrador de servicios de salud
Para que el uso de los Servicios de salud sea un poco más conveniente y expongas una imagen y más fluida, puedes crear un wrapper como el siguiente:
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()
}
Una vez que hayas creado el módulo de Hilt para administrarlo, usa el siguiente fragmento:
@Module
@InstallIn(SingletonComponent::class)
internal object DataModule {
@Provides
@Singleton
fun provideHealthServices(@ApplicationContext context: Context): HealthServicesManager = HealthServicesManager(context)
}
puedes insertar el HealthServicesManager
como cualquier otra dependencia de Hilt.
El nuevo HealthServicesManager
proporciona un método heartRateMeasureFlow()
que
registra un objeto de escucha para el monitor cardíaco y emite los datos recibidos.
Cómo habilitar las actualizaciones de datos en dispositivos wearable
Las actualizaciones de datos relacionados con el entrenamiento requieren el permiso BODY_SENSORS
. Si
si aún no lo has hecho, declara el permiso BODY_SENSORS
en tu
archivo de manifiesto de tu app. Luego, solicita el permiso, como se muestra en este fragmento:
val permissionState = rememberPermissionState(
permission = Manifest.permission.BODY_SENSORS,
onPermissionResult = { granted -> /* do something */ }
)
[...]
if (permissionState.status.isGranted) {
// do something
} else {
permissionState.launchPermissionRequest()
}
Si pruebas la app en un dispositivo físico, los datos deberían comenzar a actualizarse.
A partir de Wear OS 4, los emuladores también muestran datos de prueba automáticamente. El anterior puedes simular el flujo de datos desde el sensor. En una terminal ejecuta este comando de ADB:
adb shell am broadcast \
-a "whs.USE_SYNTHETIC_PROVIDERS" \
com.google.android.wearable.healthservices
Para ver diferentes valores de frecuencia cardíaca, intenta simular distintos ejercicios. Este comando simula el recorrido a pie:
adb shell am broadcast \
-a "whs.synthetic.user.START_WALKING" \
com.google.android.wearable.healthservices
Este comando simula la ejecución de los siguientes elementos:
adb shell am broadcast \
-a "whs.synthetic.user.START_RUNNING" \
com.google.android.wearable.healthservices
Para dejar de simular los datos, ejecuta este comando:
adb shell am broadcast -a \
"whs.USE_SENSOR_PROVIDERS" \
com.google.android.wearable.healthservices
Cómo leer datos de frecuencia cardíaca
Si otorgas el permiso BODY_SENSORS
, podrás leer la frecuencia cardíaca del usuario
(heartRateMeasureFlow()
) en HealthServicesManager
. En la app de Wear OS
IU, aparece el valor actual de la frecuencia cardíaca, medido por el sensor del
wearable.
En tu ViewModel
, comienza a recopilar datos con el objeto de flujo de frecuencia cardíaca.
como se muestra en el siguiente fragmento:
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
}
}
Usa un objeto componible similar al siguiente para mostrar los datos en tiempo real en la IU de tu app:
val heartRate by viewModel.hr
Text(
text = "Heart Rate: $heartRate",
style = MaterialTheme.typography.display1
)
Cómo enviar datos a un dispositivo de mano
Para enviar datos de salud y actividad física a un dispositivo de mano, usa DataClient
.
en Servicios de salud. En el siguiente fragmento de código, se muestra cómo enviar mensajes de corazón
datos de tarifas que la app recopiló anteriormente:
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")
}
}
}
Recibir los datos en el teléfono
Para recibir datos en el teléfono, 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
}
}
}
}
}
Cuando completes este paso, observa algunos detalles interesantes:
- La anotación
@AndroidEntryPoint
nos permite usar Hilt en esta clase. @Inject lateinit var heartRateMonitor: HeartRateMonitor
, de hecho, Inserta una dependencia en esta clase.- La clase implementa
onDataChanged()
y recibe una colección de eventos que puedes analizar y usar
La siguiente lógica de HeartRateMonitor
te permite enviar la frecuencia cardíaca recibida
de salida a otra parte de la base de código de tu 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 de datos recibe los eventos del método onDataChanged()
y los hace.
disponible para los observadores de datos usando un SharedFlow
.
El último bit es la declaración de Service
en la aplicación para teléfonos.
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>
Cómo mostrar datos en tiempo real en un dispositivo de mano
En la parte de la app que se ejecuta en un dispositivo de mano, inserta el
HeartRateMonitor
en el constructor de tu modelo de vista. Este/a HeartRateMonitor
observa los datos de la frecuencia cardíaca y emite actualizaciones de la IU según sea necesario.