[ad_1]
Samsung Privileged Health SDK consente alla tua applicazione di raccogliere segni vitali e altri parametri sanitari monitorati su Galaxy Watch con sistema operativo Wear basato su Samsung. I dati tracciati possono essere visualizzati immediatamente o conservati per un’analisi successiva. Alcuni tipi di dati tracciati, come i dati in batch, non sono pratici da visualizzare sullo schermo di un orologio in tempo reale, quindi è comune archiviare i dati in un database o in una soluzione server o mostrarli sullo schermo più grande di un dispositivo mobile.
Questo blog illustra come sviluppare 2 applicazioni di esempio connesse. Un’applicazione per l’orologio utilizza l’SDK Samsung Privileged Health per raccogliere i dati del monitoraggio della frequenza cardiaca, quindi utilizza l’API Wearable Data Layer per trasmetterli a un’applicazione complementare sul dispositivo mobile Android dell’utente, che visualizza i dati come un semplice elenco sullo schermo.
È possibile seguire la dimostrazione scaricando il progetto dell’applicazione di esempio. Per testare le applicazioni è necessario un Galaxy Watch4 (o modello superiore) e un dispositivo mobile Android connesso.
Il progetto applicativo è costituito da un modulo indossabile per l’orologio e da un modulo mobile per dispositivi mobili Android:
-
In Android Studio, seleziona Apri File > Nuovo > Nuovo progetto.
-
Selezionare Sistema operativo Wear > App Wear vuota e fare clic Prossimo.
Nuova applicazione Wear
-
Definire i dettagli del progetto.
Dettagli del progetto
-
Per creare un’applicazione mobile complementare per l’applicazione dell’orologio, controlla il file Accoppia con l’app Telefono vuoto scatola.
NotaAssicurati che l’ID dell’applicazione sia identico per entrambi i moduli nei relativi file “build.gradle”.
Per ulteriori informazioni sulla creazione di progetti multi-modulo, consulta Dal polso alla mano: sviluppare un’app complementare per la tua applicazione indossabile.
L’interfaccia utente dell’applicazione orologio dispone di 2 pulsanti. IL Inizio/Fermare Il pulsante controlla il monitoraggio dei dati cardiaci e il pulsante Inviare Il pulsante trasferisce i dati raccolti al dispositivo mobile connesso. La schermata è composta da un campo della frequenza cardiaca e da 4 campi del valore IBI, poiché possono esserci fino a 4 valori IBI in un singolo risultato del monitoraggio.
Guarda l’interfaccia utente dell’applicazione
Traccia ed estrai i dati sulla frequenza cardiaca
Quando l’utente tocca il file Inizio pulsante sull’interfaccia utente dell’applicazione indossabile, il startTracking()
funzione da MainViewModel
viene invocata la classe
L’applicazione deve verificare che Galaxy Watch supporti la funzionalità di rilevamento della frequenza cardiaca che desideriamo implementare, poiché le funzionalità supportate dipendono dal modello del dispositivo e dalla versione del software.
Recupera l’elenco dei tracker sanitari supportati con trackingCapability.supportHealthTrackerTypes
del HealthTrackingService
classe:
override fun hasCapabilities(): Boolean {
Log.i(TAG, "hasCapabilities()")
healthTrackingService = healthTrackingServiceConnection.getHealthTrackingService()
val trackers: List<HealthTrackerType> =
healthTrackingService!!.trackingCapability.supportHealthTrackerTypes
return trackers.contains(trackingType)
}
Per monitorare i valori della frequenza cardiaca sull’orologio, leggere il flusso dei valori ricevuti nel onDataReceived()
ascoltatore:
@ExperimentalCoroutinesApi
override suspend fun track(): Flow<TrackerMessage> = callbackFlow {
val updateListener = object : HealthTracker.TrackerEventListener {
override fun onDataReceived(dataPoints: MutableList<DataPoint>) {
for (dataPoint in dataPoints) {
var trackedData: TrackedData? = null
val hrValue = dataPoint.getValue(ValueKey.HeartRateSet.HEART_RATE)
val hrStatus = dataPoint.getValue(ValueKey.HeartRateSet.HEART_RATE_STATUS)
if (isHRValid(hrStatus)) {
trackedData = TrackedData()
trackedData.hr = hrValue
Log.i(TAG, "valid HR: $hrValue")
} else {
coroutineScope.runCatching {
trySendBlocking(TrackerMessage.TrackerWarningMessage(getError(hrStatus.toString())))
}
}
val validIbiList = getValidIbiList(dataPoint)
if (validIbiList.size > 0) {
if (trackedData == null) trackedData = TrackedData()
trackedData.ibi.addAll(validIbiList)
}
if ((isHRValid(hrStatus) || validIbiList.size > 0) && trackedData != null) {
coroutineScope.runCatching {
trySendBlocking(TrackerMessage.DataMessage(trackedData))
}
}
if (trackedData != null) {
validHrData.add(trackedData)
}
}
trimDataList()
}
fun getError(errorKeyFromTracker: String): String {
val str = errors.getValue(errorKeyFromTracker)
return context.resources.getString(str)
}
override fun onFlushCompleted() {
Log.i(TAG, "onFlushCompleted()")
coroutineScope.runCatching {
trySendBlocking(TrackerMessage.FlushCompletedMessage)
}
}
override fun onError(trackerError: HealthTracker.TrackerError?) {
Log.i(TAG, "onError()")
coroutineScope.runCatching {
trySendBlocking(TrackerMessage.TrackerErrorMessage(getError(trackerError.toString())))
}
}
}
heartRateTracker =
healthTrackingService!!.getHealthTracker(trackingType)
setListener(updateListener)
awaitClose {
Log.i(TAG, "Tracking flow awaitClose()")
stopTracking()
}
}
Ogni risultato del monitoraggio si trova all’interno di un elenco nel file DataPoints
argomento del onDataReceived()
aggiorna l’ascoltatore. L’applicazione di esempio implementa il monitoraggio della frequenza cardiaca su richiesta, il listener degli aggiornamenti viene richiamato ogni secondo e ogni elenco di punti dati contiene 1 elemento.
Per estrarre una frequenza cardiaca dal punto dati:
val hrValue = dataPoint.getValue(ValueKey.HeartRateSet.HEART_RATE)
val hrStatus = dataPoint.getValue(ValueKey.HeartRateSet.HEART_RATE_STATUS)
Oltre ai dati sulla frequenza cardiaca viene restituito un parametro di stato. Se la lettura della frequenza cardiaca ha avuto esito positivo, il suo valore è 1
.
Ciascun punto dati dell’intervallo tra battiti è costituito da un elenco di valori e dallo stato corrispondente per ciascun valore. A partire dalla versione 1.2.0 di Samsung Privileged Health SDK, possono essere presenti fino a 4 valori IBI in un singolo punto dati, a seconda della frequenza cardiaca. Se la lettura IBI è valida, il valore del parametro status lo è 0
.
Per estrarre solo i dati IBI validi e il cui valore non lo è 0
:
private fun isIBIValid(ibiStatus: Int, ibiValue: Int): Boolean {
return ibiStatus == 0 && ibiValue != 0
}
fun getValidIbiList(dataPoint: DataPoint): ArrayList<Int> {
val ibiValues = dataPoint.getValue(ValueKey.HeartRateSet.IBI_LIST)
val ibiStatuses = dataPoint.getValue(ValueKey.HeartRateSet.IBI_STATUS_LIST)
val validIbiList = ArrayList<Int>()
for ((i, ibiStatus) in ibiStatuses.withIndex()) {
if (isIBIValid(ibiStatus, ibiValues[i])) {
validIbiList.add(ibiValues[i])
}
}
Invia dati all’applicazione mobile
L’applicazione utilizza la classe MessageClient dell’API Wearable Data Layer per inviare messaggi al dispositivo mobile connesso. I messaggi sono utili per le chiamate di procedura remota (RPC), le richieste unidirezionali o nei modelli di comunicazione di richiesta o risposta. Quando un messaggio viene inviato, se i dispositivi di invio e di ricezione sono collegati, il sistema mette in coda il messaggio per la consegna e restituisce un codice di risultato positivo. Il codice di risultato positivo non significa necessariamente che il messaggio sia stato recapitato correttamente, poiché i dispositivi possono essere disconnessi prima della ricezione del messaggio.
Per pubblicizzare e scoprire dispositivi sulla stessa rete con funzionalità con cui l’orologio può interagire, utilizza la classe CapabilityClient dell’API Wearable Data Layer. Ogni dispositivo sulla rete è rappresentato come un nodo che supporta varie funzionalità (funzionalità) che un’applicazione definisce in fase di creazione o configura dinamicamente in fase di esecuzione. La tua applicazione Watch può cercare nodi con una funzionalità specifica e interagire con essi, ad esempio inviando messaggi. Ciò può funzionare anche nella direzione opposta, con l’applicazione indossabile che pubblicizza le funzionalità che supporta.
Quando l’utente tocca il file Inviare pulsante sull’interfaccia utente dell’applicazione indossabile, il sendMessage()
funzione da MainViewModel
viene richiamata la classe, che attiva il codice nel file SendMessageUseCase
classe:
override suspend fun sendMessage(message: String, node: Node, messagePath: String): Boolean {
val nodeId = node.id
var result = false
nodeId.also { id ->
messageClient
.sendMessage(
id,
messagePath,
message.toByteArray(charset = Charset.defaultCharset())
).apply {
addOnSuccessListener {
Log.i(TAG, "sendMessage OnSuccessListener")
result = true
}
addOnFailureListener {
Log.i(TAG, "sendMessage OnFailureListener")
result = false
}
}.await()
Log.i(TAG, "result: $result")
return result
}
}
Per trovare un nodo di destinazione per il messaggio, recuperare tutte le funzionalità disponibili sulla rete:
override suspend fun getCapabilitiesForReachableNodes(): Map<Node, Set<String>> {
Log.i(TAG, "getCapabilities()")
val allCapabilities =
capabilityClient.getAllCapabilities(CapabilityClient.FILTER_REACHABLE).await()
return allCapabilities.flatMap { (capability, capabilityInfo) ->
capabilityInfo.nodes.map {
it to capability
}
}
.groupBy(
keySelector = { it.first },
valueTransform = { it.second }
)
.mapValues { it.value.toSet() }
}
Poiché il modulo mobile dell’applicazione di esempio pubblicizza la capacità di “usura”, per trovare un nodo di destinazione appropriato, recuperare l’elenco dei nodi connessi che lo supportano:
override suspend fun getNodesForCapability(
capability: String,
allCapabilities: Map<Node, Set<String>>
): Set<Node> {
return allCapabilities.filterValues { capability in it }.keys
}
Seleziona il primo nodo dall’elenco, codifica il messaggio come una stringa JSON e invia il messaggio al nodo:
suspend operator fun invoke(): Boolean {
val nodes = getCapableNodes()
return if (nodes.isNotEmpty()) {
val node = nodes.first()
val message =
encodeMessage(trackingRepository.getValidHrData())
messageRepository.sendMessage(message, node, MESSAGE_PATH)
true
} else {
Log.i(TAG, "No compatible nodes found")
false
}
}
L’interfaccia utente dell’applicazione mobile è costituita da un elenco dei valori della frequenza cardiaca e dell’intervallo tra battiti ricevuti dall’orologio. L’elenco è scorrevole.
Interfaccia utente dell’applicazione mobile
Ricevi e visualizza i dati dall’applicazione dell’orologio
Per consentire all’applicazione mobile di ascoltare i dati dall’orologio e di avviarsi quando riceve i dati, definire il file DataListenerService
servizio nell’applicazione mobile AndroidManifest.xml
file, all’interno del file <application>
elemento:
<service
android:name="com.samsung.health.mobile.data.DataListenerService"
android:exported="true">
<intent-filter>
<action android:name="com.google.android.gms.wearable.DATA_CHANGED" />
<action android:name="com.google.android.gms.wearable.MESSAGE_RECEIVED" />
<action android:name="com.google.android.gms.wearable.REQUEST_RECEIVED" />
<action android:name="com.google.android.gms.wearable.CAPABILITY_CHANGED" />
<action android:name="com.google.android.gms.wearable.CHANNEL_EVENT" />
<data
android:host="*"
android:pathPrefix="/msg"
android:scheme="wear" />
</intent-filter>
</service>
Implementare il DataListenerService
classe nel codice dell’applicazione per ascoltare e ricevere i dati dei messaggi. I dati della stringa JSON ricevuti vengono passati come parametro:
private const val TAG = "DataListenerService"
private const val MESSAGE_PATH = "/msg"
class DataListenerService : WearableListenerService() {
override fun onMessageReceived(messageEvent: MessageEvent) {
super.onMessageReceived(messageEvent)
val value = messageEvent.data.decodeToString()
Log.i(TAG, "onMessageReceived(): $value")
when (messageEvent.path) {
MESSAGE_PATH -> {
Log.i(TAG, "Service: message (/msg) received: $value")
if (value != "") {
startActivity(
Intent(this, MainActivity::class.java)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK).putExtra("message", value)
)
} else {
Log.i(TAG, "value is an empty string")
}
}
}
Per decodificare i dati del messaggio:
fun decodeMessage(message: String): List<TrackedData> {
return Json.decodeFromString(message)
}
Per visualizzare i dati ricevuti sulla schermata dell’applicazione:
@Composable
fun MainScreen(
results: List<TrackedData>
) {
Column(
modifier = Modifier
.fillMaxSize()
.background(Color.Black),
verticalArrangement = Arrangement.Top,
horizontalAlignment = Alignment.CenterHorizontally
) {
Spacer(
Modifier
.height(70.dp)
.fillMaxWidth()
.background(Color.Black)
)
ListView(results)
}
}
Per eseguire le applicazioni indossabili e mobili:
- Collega il tuo Galaxy Watch e il tuo dispositivo mobile Android (entrambi i dispositivi devono essere accoppiati tra loro) ad Android Studio sul tuo computer.
- Selezionare Indossare dall’elenco dei moduli e il dispositivo Galaxy Watch dall’elenco dei dispositivi, quindi fare clic su Correre. L’applicazione indossabile viene avviata sull’orologio.
Dispositivi connessi
- Selezionare mobile dall’elenco dei moduli e il dispositivo mobile Android dall’elenco dei dispositivi, quindi fare clic su Correre. L’applicazione mobile viene avviata sul dispositivo mobile.
- Indossa l’orologio al polso e tocca Inizio. L’orologio inizia a monitorare la frequenza cardiaca. Dopo che alcuni valori monitorati vengono visualizzati sullo schermo dell’orologio, per inviare i valori all’applicazione mobile, toccare Inviare. Se l’applicazione mobile non è in esecuzione, viene avviata. I dati cardiaci tracciati vengono visualizzati sulla schermata dell’applicazione mobile.
- Per interrompere il monitoraggio, tocca Fermare sull’orologio.
Samsung Privileged Health SDK consente di monitorare i dati sanitari, come la frequenza cardiaca, dal Galaxy Watch4 di un utente o da un modello di smartwatch superiore. Per visualizzare i dati tracciati su uno schermo più grande, puoi utilizzare il MessageClient dell’API Wearable Data Layer per inviare i dati a un’applicazione complementare sul dispositivo mobile connesso.
Per sviluppare funzionalità applicative più avanzate, puoi anche utilizzare la classe DataClient per inviare dati a dispositivi attualmente non nel raggio d’azione dell’orologio, consegnandoli solo quando il dispositivo è connesso.
Lab. Codice trasferimento dati frequenza cardiaca
[ad_2]
Source link