admin管理员组

文章数量:1278914

I’m working with the Samsung Health Sensor API (v1.3.0) to collect Inter-Beat Interval (IBI) data from a Galaxy Watch. My goal is to gather IBI samples periodically (e.g., every hour for 10 minutes) even when the app is not active and the screen is off. The issue I’m facing is that data collection only works when the screen is on or the app is actively running. As soon as the screen turns off or the app moves to the background, the Samsung Health Sensor API stops delivering IBI data, making it impossible to collect the necessary samples as scheduled.

I have tried using Foreground Services to keep the app running in the foreground, but the Samsung Health Sensor API still stops delivering IBI data when the screen turns off. Additionally, I implemented a Wake Lock to prevent the watch from going into a low-power state, but this did not resolve the issue either. Despite these efforts, data collection only works when the screen is on or the app is actively running. I also checked for any relevant permissions or settings that might allow continuous background data collection, but I haven't found a solution yet.

This is the worker:

class IBIDataCollectionWorker(
    private val context: Context,
    workerParams: WorkerParameters
) : CoroutineWorker(context, workerParams) {

    private var healthService: HealthTrackingService? = null
    private var healthTracker: HealthTracker? = null
    private val ibiDataList = mutableListOf<Int>()
    private val dateFormat = SimpleDateFormat("yyyy-MM-dd_HH-mm-ss", Locale.getDefault())

    private val trackerEventListener = object : HealthTracker.TrackerEventListener {
        override fun onDataReceived(dataPoints: List<DataPoint>) {
            for (dataPoint in dataPoints) {
                val validIbiList = getValidIbiList(dataPoint)
                ibiDataList.addAll(validIbiList)
            }
        }

        override fun onError(error: HealthTracker.TrackerError) {
            Log.e("IBIWorker", "Error: $error")
        }

        override fun onFlushCompleted() {}
    }

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

    private fun getValidIbiList(dataPoint: DataPoint): List<Int> {
        val ibiValues = dataPoint.getValue(ValueKey.HeartRateSet.IBI_LIST)
        val ibiStatuses = dataPoint.getValue(ValueKey.HeartRateSet.IBI_STATUS_LIST)

        return ibiValues.filterIndexed { index, value ->
            isIBIValid(ibiStatuses[index], value)
        }
    }

    override suspend fun doWork(): Result = withContext(Dispatchers.IO) {
        return@withContext try {
            val connectionDeferred = CompletableDeferred<Boolean>()

            healthService = HealthTrackingService(object : ConnectionListener {
                override fun onConnectionSuccess() {
                    connectionDeferredplete(true)
                }

                override fun onConnectionFailed(e: HealthTrackerException) {
                    Log.e("IBIWorker", "Connection Failed: $e")
                    connectionDeferredplete(false)
                }

                override fun onConnectionEnded() {}
            }, context)

            healthService?.connectService()

            if (!connectionDeferred.await()) {
                return@withContext Result.failure(workDataOf("error" to "Connection failed"))
            }

            startTracking()
            delay(600000) // collect data for 10 min
            stopTracking()
            saveDataToFile()
            Result.success()

        } catch (e: Exception) {
            Log.e("IBIWorker", "Error during work execution", e)
            Result.failure(workDataOf("error" to e.message.toString()))
        } finally {
            healthService?.disconnectService()
            healthService = null
        }
    }


    private fun startTracking() {
        try {
            healthTracker = healthService?.getHealthTracker(HealthTrackerType.HEART_RATE_CONTINUOUS)
            if (healthTracker == null) {
                Log.e("IBIWorker", "Failed to create HealthTracker")
                return
            }
            healthTracker?.setEventListener(trackerEventListener)
            Log.d("IBIWorker", "Tracking started")
        } catch (e: Exception) {
            Log.e("IBIWorker", "Error starting tracking", e)
        }
    }

    private fun stopTracking() {
        healthTracker?.unsetEventListener()
    }

    private fun saveDataToFile() {
        val fileName = "ibi_data_worker.txt"
        val file = File(context.getExternalFilesDir(null), fileName)

        try {
            FileOutputStream(file, true).use { outputStream ->
                val timestamp = dateFormat.format(Date())
                outputStream.write("${timestamp}:\n".toByteArray())
                ibiDataList.forEach { ibiValue ->
                    outputStream.write("${ibiValue},".toByteArray())
                }
            }
        } catch (e: IOException) {
            Log.e("IBIWorker", "Error saving to file", e)
        } finally {
            ibiDataList.clear()
        }
    }
}

And this is how I run it in the MainActivity

    private fun requestPermissions() {
        val permissionLauncher = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
            permissionGranted = permissions.all { it.value }
            if (permissionGranted) {
                scheduleIBIDataCollection()
            }
            else Toast.makeText(this, "permission denied", Toast.LENGTH_LONG).show()
        }

        permissionLauncher.launch(arrayOf(android.Manifest.permission.BODY_SENSORS))
    }

    private fun scheduleIBIDataCollection() {
        val ibiWorkRequest = PeriodicWorkRequestBuilder<IBIDataCollectionWorker>(
            repeatInterval = 60,
            repeatIntervalTimeUnit = TimeUnit.MINUTES
        ).build()

        WorkManager.getInstance(applicationContext).enqueueUniquePeriodicWork(
            "ibi_data_collection",
            ExistingPeriodicWorkPolicy.KEEP,
            ibiWorkRequest
        )
    }

本文标签: wear osSamsung Health Sensor API Collecting IBI Data in Background on Galaxy WatchStack Overflow