From 6c9c0e6f48bb5291e430445a4933dc28bacf1564 Mon Sep 17 00:00:00 2001 From: yatharthranjan Date: Mon, 9 Sep 2024 13:17:47 +0100 Subject: [PATCH] PR feedback and other improvements --- plugins/radar-android-polar/README.md | 17 ++ .../radarbase/passive/polar/PolarManager.kt | 178 +++++++++++------- .../radarbase/passive/polar/PolarService.kt | 19 +- 3 files changed, 131 insertions(+), 83 deletions(-) diff --git a/plugins/radar-android-polar/README.md b/plugins/radar-android-polar/README.md index 320f8f156..f2fbc5495 100644 --- a/plugins/radar-android-polar/README.md +++ b/plugins/radar-android-polar/README.md @@ -50,3 +50,20 @@ Add the provider `.polar.PolarProvider` to the Firebase Remote Config `plugins` This plugin was build using the [POLAR BLE SDK][1]. [1]: https://github.com/polarofficial/polar-ble-sdk + + +## Information + +On the **Polar Vantage V3**, streaming data is only possible when a training session is started on the watch (see https://github.com/polarofficial/polar-ble-sdk/issues/456). + +Here are the instructions how to start data streaming: + +1. Have a phone running the app with Polar SDK paired with the watch. +2. The sensor data sharing must be enabled for the phone in the watch pairing menu (General settings/Pair and sync/Paired devices/SDK/Share). +3. Go to the 'Start training' -menu in watch and choose the desired sport profile. +4. On phone running the app with Polar SDK connect phone with watch. +5. Start the recording in the app. +6. On the watch start the training recording or stay in 'Start training' -menu. + +Note! When streaming data we would recommend removing Polar Flow app from the mobile if it was installed. All other phones or Polar devices not used for streaming should be shut down or at least Bluetooth turned off. +Also after each streaming session you might need to go back to the watch mode before starting a new exercise & streaming session. diff --git a/plugins/radar-android-polar/src/main/java/org/radarbase/passive/polar/PolarManager.kt b/plugins/radar-android-polar/src/main/java/org/radarbase/passive/polar/PolarManager.kt index fe8387f16..50c39bea6 100644 --- a/plugins/radar-android-polar/src/main/java/org/radarbase/passive/polar/PolarManager.kt +++ b/plugins/radar-android-polar/src/main/java/org/radarbase/passive/polar/PolarManager.kt @@ -54,7 +54,7 @@ class PolarManager( private var accDisposable: Disposable? = null private var ppiDisposable: Disposable? = null private var ppgDisposable: Disposable? = null - + init { status = SourceStatusListener.Status.DISCONNECTED // red icon name = service.getString(R.string.polarDisplayName) @@ -64,9 +64,7 @@ class PolarManager( override fun start(acceptableIds: Set) { status = SourceStatusListener.Status.READY // blue loading - logger.debug("Polar Device is $deviceId") - disconnectToPolarSDK(deviceId) connectToPolarSDK() register() @@ -125,8 +123,7 @@ class PolarManager( override fun deviceDisconnected(polarDeviceInfo: PolarDeviceInfo) { logger.debug("Device disconnected ${polarDeviceInfo.deviceId}") isDeviceConnected = false - status = SourceStatusListener.Status.DISCONNECTED // red circle - + disconnect() } override fun bleSdkFeatureReady( @@ -137,11 +134,10 @@ class PolarManager( if (isDeviceConnected) { logger.debug("Feature ready {} for {}", feature, deviceId) - if (feature == PolarBleApi.PolarBleSdkFeature.FEATURE_POLAR_DEVICE_TIME_SETUP) { - setDeviceTime(deviceId) - } - when (feature) { + PolarBleApi.PolarBleSdkFeature.FEATURE_POLAR_DEVICE_TIME_SETUP -> + setDeviceTime(deviceId) + PolarBleApi.PolarBleSdkFeature.FEATURE_POLAR_ONLINE_STREAMING -> { streamHR() streamEcg() @@ -167,14 +163,17 @@ class PolarManager( val batteryLevel = level.toFloat() / 100.0f state.batteryLevel = batteryLevel logger.debug("Battery level $level%, which is $batteryLevel at $currentTime") - send( - batteryLevelTopic, - PolarBatteryLevel(name, currentTime, currentTime, batteryLevel) - ) + mHandler.execute { + send( + batteryLevelTopic, + PolarBatteryLevel(name, currentTime, currentTime, batteryLevel) + ) + } } - }) + api.setApiLogger { s: String -> logger.debug("POLAR_API: {}", s) } + try { deviceId = service.getPolarDevice() if (deviceId == null) { @@ -187,6 +186,33 @@ class PolarManager( } catch (a: PolarInvalidArgument) { a.printStackTrace() } + } + + override fun onClose() { + super.onClose() + if (autoConnectDisposable != null && !autoConnectDisposable!!.isDisposed) { + autoConnectDisposable?.dispose() + } + if (hrDisposable != null && !hrDisposable!!.isDisposed) { + hrDisposable?.dispose() + } + if (ecgDisposable != null && !ecgDisposable!!.isDisposed) { + ecgDisposable?.dispose() + } + if (accDisposable != null && !accDisposable!!.isDisposed) { + accDisposable?.dispose() + } + if (ppiDisposable != null && !ppiDisposable!!.isDisposed) { + ppiDisposable?.dispose() + } + if (ppgDisposable != null && !ppgDisposable!!.isDisposed) { + ppgDisposable?.dispose() + } + + disconnectToPolarSDK(deviceId) + mHandler.stop { + wakeLock?.release() + } } @@ -200,7 +226,6 @@ class PolarManager( } autoConnectDisposable = Flowable.interval(0, 5, TimeUnit.SECONDS) - .observeOn(AndroidSchedulers.mainThread()) .subscribe({ if (deviceId == null || !isDeviceConnected) { searchPolarDevice(true) @@ -211,7 +236,6 @@ class PolarManager( private fun searchPolarDevice(force: Boolean = false): Disposable? { try { return api.searchForDevice() - .observeOn(AndroidSchedulers.mainThread()) .subscribe({ device: PolarDeviceInfo -> logger.debug("Device found: ${device.deviceId} ${device.name}") if (deviceId == null || force) { @@ -267,7 +291,6 @@ class PolarManager( if (isDisposed) { hrDisposable = deviceId?.let { api.startHrStreaming(it) - .observeOn(AndroidSchedulers.mainThread()) .doOnSubscribe { logger.debug("Subscribed to HrStreaming for $deviceId") } .subscribe( { hrData: PolarHrData -> @@ -279,19 +302,21 @@ class PolarManager( "contactStatus: ${sample.contactStatus} " + "contactStatusSupported: ${sample.contactStatusSupported}" ) - send( - heartRateTopic, - PolarHeartRate( - name, - getTimeNano(), - currentTime, - sample.hr, - sample.rrsMs, - sample.rrAvailable, - sample.contactStatus, - sample.contactStatusSupported + mHandler.execute { + send( + heartRateTopic, + PolarHeartRate( + name, + getTimeNano(), + currentTime, + sample.hr, + sample.rrsMs, + sample.rrAvailable, + sample.contactStatus, + sample.contactStatusSupported + ) ) - ) + } } }, @@ -328,15 +353,17 @@ class PolarManager( PolarUtils.convertEpochPolarToUnixEpoch(data.timeStamp) } currentTime: $currentTime PolarTimeStamp: ${data.timeStamp}" ) - send( - ecgTopic, - PolarEcg( - name, - PolarUtils.convertEpochPolarToUnixEpoch(data.timeStamp), - currentTime, - data.voltage + mHandler.execute { + send( + ecgTopic, + PolarEcg( + name, + PolarUtils.convertEpochPolarToUnixEpoch(data.timeStamp), + currentTime, + data.voltage + ) ) - ) + } } }, { error: Throwable -> @@ -369,17 +396,19 @@ class PolarManager( PolarUtils.convertEpochPolarToUnixEpoch(data.timeStamp) } currentTime: $currentTime PolarTimeStamp: ${data.timeStamp}" ) - send( - accelerationTopic, - PolarAcceleration( - name, - PolarUtils.convertEpochPolarToUnixEpoch(data.timeStamp), - currentTime, - data.x, - data.y, - data.z + mHandler.execute { + send( + accelerationTopic, + PolarAcceleration( + name, + PolarUtils.convertEpochPolarToUnixEpoch(data.timeStamp), + currentTime, + data.x, + data.y, + data.z + ) ) - ) + } } }, { error: Throwable -> @@ -416,18 +445,20 @@ class PolarManager( PolarUtils.convertEpochPolarToUnixEpoch(data.timeStamp) } currentTime: $currentTime PolarTimeStamp: ${data.timeStamp}" ) - send( - ppgTopic, - PolarPpg( - name, - PolarUtils.convertEpochPolarToUnixEpoch(data.timeStamp), - currentTime, - data.channelSamples[0], - data.channelSamples[1], - data.channelSamples[2], - data.channelSamples[3] + mHandler.execute { + send( + ppgTopic, + PolarPpg( + name, + PolarUtils.convertEpochPolarToUnixEpoch(data.timeStamp), + currentTime, + data.channelSamples[0], + data.channelSamples[1], + data.channelSamples[2], + data.channelSamples[3] + ) ) - ) + } } } }, @@ -447,7 +478,6 @@ class PolarManager( if (isDisposed) { ppiDisposable = deviceId?.let { api.startPpiStreaming(it) - .observeOn(AndroidSchedulers.mainThread()) .subscribe( { ppiData: PolarPpiData -> for (sample in ppiData.samples) { @@ -456,20 +486,22 @@ class PolarManager( "errorEstimate: ${sample.errorEstimate} " + "currentTime: $currentTime" ) - send( - ppIntervalTopic, - PolarPpInterval( - name, - currentTime, - currentTime, - sample.blockerBit, - sample.errorEstimate, - sample.hr, - sample.ppi, - sample.skinContactStatus, - sample.skinContactSupported + mHandler.execute { + send( + ppIntervalTopic, + PolarPpInterval( + name, + currentTime, + currentTime, + sample.blockerBit, + sample.errorEstimate, + sample.hr, + sample.ppi, + sample.skinContactStatus, + sample.skinContactSupported + ) ) - ) + } } }, { error: Throwable -> @@ -482,7 +514,7 @@ class PolarManager( ppiDisposable?.dispose() } } - + companion object { private val logger = LoggerFactory.getLogger(PolarManager::class.java) } diff --git a/plugins/radar-android-polar/src/main/java/org/radarbase/passive/polar/PolarService.kt b/plugins/radar-android-polar/src/main/java/org/radarbase/passive/polar/PolarService.kt index e064432ed..147c6039e 100644 --- a/plugins/radar-android-polar/src/main/java/org/radarbase/passive/polar/PolarService.kt +++ b/plugins/radar-android-polar/src/main/java/org/radarbase/passive/polar/PolarService.kt @@ -1,25 +1,25 @@ package org.radarbase.passive.polar import android.content.Context -import android.os.Process +import android.content.SharedPreferences import org.radarbase.android.config.SingleRadarConfiguration import org.radarbase.android.source.SourceManager import org.radarbase.android.source.SourceService -import org.radarbase.android.util.SafeHandler /** * A service that manages the Polar manager and a TableDataHandler to send store the data of * the phone sensors and send it to a Kafka REST proxy. */ class PolarService : SourceService() { - private lateinit var handler: SafeHandler override val defaultState: PolarState get() = PolarState() + private lateinit var sharedPreferences: SharedPreferences + override fun onCreate() { super.onCreate() - handler = SafeHandler.getInstance("Polar", Process.THREAD_PRIORITY_FOREGROUND) + sharedPreferences = getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE) } override fun createSourceManager() = PolarManager(this) @@ -31,17 +31,16 @@ class PolarService : SourceService() { manager as PolarManager } - fun savePolarDevice(deviceId: String) { - applicationContext.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE) + + fun savePolarDevice(deviceId: String) = + sharedPreferences .edit() .putString(SHARED_PREF_KEY, deviceId) .apply() - } - fun getPolarDevice(): String? { - return applicationContext.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE) + fun getPolarDevice(): String? = + sharedPreferences .getString(SHARED_PREF_KEY, null) - } companion object { val SHARED_PREF_NAME: String = PolarService::class.java.name