The Samsung Privileged Well being SDK allows your utility to gather very important indicators and different well being parameters tracked on Galaxy Watch4 and better fashions powered by Put on OS. The tracked knowledge could be displayed instantly or retained for later evaluation. Some sorts of tracked knowledge, equivalent to batching knowledge, are impractical to show on a watch display in real-time, so it is not uncommon to retailer the information in a database or server resolution or present them on the bigger display of a cellular system.

This weblog demonstrates easy methods to develop 2 related pattern functions. A watch utility makes use of the Samsung Privileged Health SDK to gather coronary heart price tracker knowledge, then makes use of the Wearable Data Layer API to transmit it to a companion utility on the consumer’s Android cellular system, which shows the information as a easy checklist on its display.

You possibly can comply with together with the demonstration by downloading the sample application project. To check the functions, you want a Galaxy Watch4 (or larger mannequin) operating Put on OS and a related Android cellular system.

The appliance undertaking consists of a wearable module for the watch, and a cellular module for Android cellular units:

  1. In Android Studio, choose Open File > New > New Challenge.
  2. Choose Put on OS > Empty Put on App and click on Subsequent.

    New Put on app

  3. Outline the undertaking particulars.

    Challenge particulars

  4. To create a companion cellular utility for the watch utility, test the Pair with Empty Cellphone app field.

WordMake it possible for the applying ID is similar for each modules of their “construct.gradle” recordsdata.

For extra details about creating multi-module tasks, see From Wrist to Hand: Develop a Companion App for Your Wearable Application.

The watch utility UI has 2 buttons. The Begin/Cease button controls coronary heart knowledge monitoring, and the Ship button transfers the collected knowledge to the related cellular system. The display consists of a coronary heart price subject and 4 IBI worth fields, since there could be as much as 4 IBI values in a single monitoring end result.

Watch utility UI

Observe and extract coronary heart price knowledge

When the consumer faucets the Begin button on the wearable utility UI, the startTracking() perform from the MainViewModel class is invoked.

The appliance should test that the Galaxy Watch helps the center price monitoring functionality that we need to implement, because the supported capabilities depend upon the system mannequin and software program model.

Retrieve the checklist of supported well being trackers with the trackingCapability.supportHealthTrackerTypes of the HealthTrackingService class:

override enjoyable hasCapabilities(): Boolean {
    Log.i(TAG, "hasCapabilities()")
    healthTrackingService = healthTrackingServiceConnection.getHealthTrackingService()
    val trackers: Record<HealthTrackerType> =
        healthTrackingService!!.trackingCapability.supportHealthTrackerTypes
    return trackers.incorporates(trackingType)
}

To trace the center price values on the watch, learn the movement of values obtained within the onDataReceived() listener:

@ExperimentalCoroutinesApi
override droop enjoyable observe(): Circulate<TrackerMessage> = callbackFlow {
    val updateListener = object : HealthTracker.TrackerEventListener {
        override enjoyable 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, "legitimate HR: $hrValue")
                } else {
                    coroutineScope.runCatching {
                        trySendBlocking(TrackerMessage.TrackerWarningMessage(getError(hrStatus.toString())))
                    }
                }

                val validIbiList = getValidIbiList(dataPoint)
                if (validIbiList.measurement > 0) {
                    if (trackedData == null) trackedData = TrackedData()
                    trackedData.ibi.addAll(validIbiList)
                }

                if ((isHRValid(hrStatus) || validIbiList.measurement > 0) && trackedData != null) {
                    coroutineScope.runCatching {
                        trySendBlocking(TrackerMessage.DataMessage(trackedData))
                    }
                }
                if (trackedData != null) {
                    validHrData.add(trackedData)
                }
            }
            trimDataList()
        }

        enjoyable getError(errorKeyFromTracker: String): String {
            val str = errors.getValue(errorKeyFromTracker)
            return context.sources.getString(str)
        }

        override enjoyable onFlushCompleted() {

            Log.i(TAG, "onFlushCompleted()")
            coroutineScope.runCatching {
                trySendBlocking(TrackerMessage.FlushCompletedMessage)
            }
        }

        override enjoyable 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, "Monitoring movement awaitClose()")
        stopTracking()
    }
}

Every monitoring result’s inside a listing within the DataPoints argument of the onDataReceived() replace listener. The pattern utility implements on-demand coronary heart price monitoring, the replace listener is invoked each second and every knowledge level checklist incorporates 1 ingredient.

To extract a coronary heart price from knowledge level:

val hrValue = dataPoint.getValue(ValueKey.HeartRateSet.HEART_RATE)
val hrStatus = dataPoint.getValue(ValueKey.HeartRateSet.HEART_RATE_STATUS)

A standing parameter is returned along with the center price knowledge. If the center price studying was profitable, its worth is 1.

Every inter-beat interval knowledge level consists of a listing of values and the corresponding standing for every worth. Since Samsung Privileged Well being SDK model 1.2.0, there could be as much as 4 IBI values in a single knowledge level, relying on the center price. If the IBI studying is legitimate, the worth of the standing parameter is 0.

To extract solely IBI knowledge that’s legitimate and whose worth will not be 0:

personal enjoyable isIBIValid(ibiStatus: Int, ibiValue: Int): Boolean {
    return ibiStatus == 0 && ibiValue != 0
}

enjoyable 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])
        }
    }

Ship knowledge to the cellular utility

The appliance makes use of the MessageClient class of the Wearable Information Layer API to ship messages to the related cellular system. Messages are helpful for distant process calls (RPC), one-way requests, or in request-or-response communication fashions. When a message is distributed, if the sending and receiving units are related, the system queues the message for supply and returns a profitable end result code. The profitable end result code doesn’t essentially imply that the message was delivered efficiently, because the units could be disconnected earlier than the message is obtained.

To promote and uncover units on the identical community with options that the watch can work together with, use the CapabilityClient class of the Wearable Information Layer API. Every system on the community is represented as a node that helps numerous capabilities (options) that an utility defines at construct time or configures dynamically at runtime. Your watch utility can seek for nodes with a selected functionality and work together with it, equivalent to sending messages. This could additionally work in the wrong way, with the wearable utility promoting the capabilities it helps.

When the consumer faucets the Ship button on the wearable utility UI, the sendMessage() perform from the MainViewModel class is invoked, which triggers code within the SendMessageUseCase class:

override droop enjoyable sendMessage(message: String, node: Node, messagePath: String): Boolean {
    val nodeId = node.id
    var end result = false
    nodeId.additionally { id ->
        messageClient
            .sendMessage(
                id,
                messagePath,
                message.toByteArray(charset = Charset.defaultCharset())
            ).apply {
                addOnSuccessListener {
                    Log.i(TAG, "sendMessage OnSuccessListener")
                    end result = true
                }
                addOnFailureListener {
                    Log.i(TAG, "sendMessage OnFailureListener")
                    end result = false
                }
            }.await()
        Log.i(TAG, "end result: $end result")
        return end result
    }
}

To discover a vacation spot node for the message, retrieve all of the obtainable capabilities on the community:

override droop enjoyable getCapabilitiesForReachableNodes(): Map<Node, Set<String>> {
    Log.i(TAG, "getCapabilities()")

    val allCapabilities =
        capabilityClient.getAllCapabilities(CapabilityClient.FILTER_REACHABLE).await()

    return allCapabilities.flatMap { (functionality, capabilityInfo) ->
        capabilityInfo.nodes.map {
            it to functionality
        }
    }
        .groupBy(
            keySelector = { it.first },
            valueTransform = { it.second }
        )
        .mapValues { it.worth.toSet() }
}

Because the cellular module of the pattern utility advertises having the “put on” functionality, to seek out an applicable vacation spot node, retrieve the checklist of related nodes that help it:

override droop enjoyable getNodesForCapability(
    functionality: String,
    allCapabilities: Map<Node, Set<String>>
): Set<Node> {
    return allCapabilities.filterValues { functionality in it }.keys
}

Choose the primary node from the checklist, encode the message as a JSON string, and ship the message to the node:

droop operator enjoyable 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 suitable nodes discovered")
        false
    }
}

The cellular utility UI consists of a listing of the center price and inter-beat interval values obtained from the watch. The checklist is scrollable.

Cellular utility UI

Obtain and show knowledge from the watch utility

To allow the cellular utility to hear for knowledge from the watch and launch when it receives knowledge, outline the DataListenerService service within the cellular utility’s AndroidManifest.xml file, throughout the <utility> ingredient:

 <service
   android:identify="com.samsung.well being.cellular.knowledge.DataListenerService"
   android:exported="true">

   <intent-filter>
       <motion android:identify="com.google.android.gms.wearable.DATA_CHANGED" />
       <motion android:identify="com.google.android.gms.wearable.MESSAGE_RECEIVED" />
       <motion android:identify="com.google.android.gms.wearable.REQUEST_RECEIVED" />
       <motion android:identify="com.google.android.gms.wearable.CAPABILITY_CHANGED" />
       <motion android:identify="com.google.android.gms.wearable.CHANNEL_EVENT" />

       <knowledge
           android:host="*"
           android:pathPrefix="/msg"
           android:scheme="put on" />
   </intent-filter>
</service>

Implement the DataListenerService class within the utility code to hear for and obtain message knowledge. The obtained JSON string knowledge is handed as a parameter:

personal const val TAG = "DataListenerService"
personal const val MESSAGE_PATH = "/msg"

class DataListenerService : WearableListenerService() {

   override enjoyable onMessageReceived(messageEvent: MessageEvent) {
       tremendous.onMessageReceived(messageEvent)

       val worth = messageEvent.knowledge.decodeToString()
       Log.i(TAG, "onMessageReceived(): $worth")
       when (messageEvent.path) {
           MESSAGE_PATH -> {
               Log.i(TAG, "Service: message (/msg) obtained: $worth")

               if (worth != "") {
                   startActivity(
                       Intent(this, MainActivity::class.java)
                           .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK).putExtra("message", worth)
                   )
               } else {
                   Log.i(TAG, "worth is an empty string")
               }
           }
       }

To decode the message knowledge:

enjoyable decodeMessage(message: String): Record<TrackedData> {

    return Json.decodeFromString(message)
}

To show the obtained knowledge on the applying display:

@Composable
enjoyable MainScreen(
    outcomes: Record<TrackedData>
) {
    Column(
        modifier = Modifier
            .fillMaxSize()
            .background(Shade.Black),
        verticalArrangement = Association.Prime,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Spacer(
            Modifier
                .top(70.dp)
                .fillMaxWidth()
                .background(Shade.Black)
        )
        ListView(outcomes)
    }
}

To run the wearable and cellular functions:

  1. Join your Galaxy Watch and Android cellular system (each units have to be paired with one another) to Android Studio in your pc.
  2. Choose put on from the modules checklist and the Galaxy Watch system from the units checklist, then click on Run. The wearable utility launches on the watch.

    Linked units

  3. Choose cellular from the modules checklist and the Android cellular system from the units checklist, then click on Run. The cellular utility launches on the cellular system.
  4. Put on the watch in your wrist and faucet Begin. The watch begins monitoring your coronary heart price. After some tracked values seem on the watch display, to ship the values to the cellular utility, faucet Ship. If the cellular utility will not be operating, it’s launched. The tracked coronary heart knowledge seems on the cellular utility display.
  5. To cease monitoring, faucet Cease on the watch.

The Samsung Privileged Well being SDK allows you to observe well being knowledge, equivalent to coronary heart price, from a consumer’s Galaxy Watch4 or larger smartwatch mannequin. To show the tracked knowledge on a bigger display, you should use the MessageClient of the Wearable Information Layer API to ship the information to a companion utility on the related cellular system.

To develop extra superior utility options, it’s also possible to use the DataClient class to ship knowledge to units not presently in vary of the watch, delivering it solely when the system is related.

Heart Rate Data Transfer Code Lab