EN VI

Android - WearOS heartrate being read but not changing on the display?

2024-03-15 15:00:11
How to Android - WearOS heartrate being read but not changing on the display

I am developing a wearos app that reads the heart rate and then displays it. On the watch the app says Heart Rate: 0 and changes based off the user's heart rate. Although I have it set so that it shows the new heart rate data on the log cat so I can see it but it doesn't update the display. The display says "Heart Rate: 0" still when it should say the actual value.

I am fairly new to using kotlin and compose so it's pretty messy and I am fairly confused on where I have gone wrong. Below I have attached the code and some images. Watch Display image Logcat image

Code for MainActivity

package com.example.myapplication.presentation

import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Devices
import androidx.compose.ui.tooling.preview.Preview
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.lifecycle.lifecycleScope
import androidx.wear.compose.material.MaterialTheme
import androidx.wear.compose.material.Text
import kotlinx.coroutines.launch

class MainActivity : ComponentActivity() {

    private lateinit var sensorManager: SensorManager
    private var heartRateSensor: Sensor? = null
    private var heartRateSensorListener: SensorEventListener? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
        heartRateSensor = sensorManager.getDefaultSensor(Sensor.TYPE_HEART_RATE)

        setContent {
            HeartRateApp()
        }
    }

    @Preview(device = Devices.WEAR_OS_SMALL_ROUND, showSystemUi = true)
    @Composable
    fun HeartRateApp() {
        var heartRate by remember { mutableStateOf(0) }

        Column(
            modifier = Modifier
                .fillMaxSize()
                .background(MaterialTheme.colors.background),
            verticalArrangement = Arrangement.Center,
            horizontalAlignment = Alignment.CenterHorizontally


        ) {
            Text(
                text = "Heart Rate: $heartRate",
                textAlign = TextAlign.Center
            )

            LaunchedEffect(key1 = heartRateSensor) {
                startHeartRateUpdates()
            }
        }
    }

    private fun startHeartRateUpdates() {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.BODY_SENSORS)
            != PackageManager.PERMISSION_GRANTED
        ) {
            ActivityCompat.requestPermissions(
                this,
                arrayOf(Manifest.permission.BODY_SENSORS),
                PERMISSION_REQUEST_BODY_SENSORS
            )
            return
        }

        val mutableHeartRate = mutableStateOf(0)

        heartRateSensorListener = object : SensorEventListener {
            override fun onSensorChanged(event: SensorEvent?) {
                event?.let {
                    if (it.sensor.type == Sensor.TYPE_HEART_RATE) {
                        val heartRateValue = it.values[0].toInt()
                        Log.d(TAG, "New Heart Rate Data: $heartRateValue")
                        mutableHeartRate.value = heartRateValue
                    }
                }
            }

            override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
                // Not needed for heart rate sensor
            }
        }

        sensorManager.registerListener(
            heartRateSensorListener,
            heartRateSensor,
            SensorManager.SENSOR_DELAY_NORMAL
        )

        // Observe changes in mutableHeartRate and update heartRate accordingly
        lifecycleScope.launch {
            updateHeartRate(mutableHeartRate.value)
        }
    }





    private fun updateHeartRate(heartRate: Int) {
        // Update the UI with the latest heart rate value
        Log.d(TAG, "Heart Rate: $heartRate")


    }

    override fun onDestroy() {
        super.onDestroy()
        heartRateSensorListener?.let {
            sensorManager.unregisterListener(it)
        }
    }

    companion object {
        private const val PERMISSION_REQUEST_BODY_SENSORS = 101
        private const val TAG = "HeartRateApp"
    }
}

And thank you sm in advance for helping me :)

Solution:

first of all the link of picture you put in doesn't show any thing and then the UI doesn't seem to reflect the updated heart rate value.

you need to observe changes in the mutableHeartRate variable and update the heartRate variable in the HeartRateApp composable function. in your code you're only launching a coroutine to update the heart rate, but you're not updating the heartRate variable that is used in the UI.

    @Preview(device = Devices.WEAR_OS_SMALL_ROUND, showSystemUi = true)
@Composable
fun HeartRateApp() {
    var heartRate by remember { mutableStateOf(0) }

    Column(
        modifier = Modifier
            .fillMaxSize()
            .background(MaterialTheme.colors.background),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text(
            text = "Heart Rate: $heartRate",
            textAlign = TextAlign.Center
        )

        LaunchedEffect(key1 = heartRateSensor) {
            startHeartRateUpdates { newHeartRate ->
                heartRate = newHeartRate
            }
        }
    }
}

private fun startHeartRateUpdates(onHeartRateUpdated: (Int) -> Unit) {
    if (ContextCompat.checkSelfPermission(
            this,
            Manifest.permission.BODY_SENSORS
        ) != PackageManager.PERMISSION_GRANTED
    ) {
        ActivityCompat.requestPermissions(
            this,
            arrayOf(Manifest.permission.BODY_SENSORS),
            PERMISSION_REQUEST_BODY_SENSORS
        )
        return
    }

    heartRateSensorListener = object : SensorEventListener {
        override fun onSensorChanged(event: SensorEvent?) {
            event?.let {
                if (it.sensor.type == Sensor.TYPE_HEART_RATE) {
                    val heartRateValue = it.values[0].toInt()
                    Log.d(TAG, "New Heart Rate Data: $heartRateValue")
                    onHeartRateUpdated(heartRateValue)
                }
            }
        }

        override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
            // Not needed for heart rate sensor
        }
    }

    sensorManager.registerListener(
        heartRateSensorListener,
        heartRateSensor,
        SensorManager.SENSOR_DELAY_NORMAL
    )
}

private fun updateHeartRate(heartRate: Int) {
    // Update the UI with the latest heart rate value
    Log.d(TAG, "Heart Rate: $heartRate")
}
Answer

Login


Forgot Your Password?

Create Account


Lost your password? Please enter your email address. You will receive a link to create a new password.

Reset Password

Back to login