From 5ea70397e2fc376d9ce6e7e74eb1c096aaebaa20 Mon Sep 17 00:00:00 2001 From: tahaalam Date: Sun, 15 Feb 2026 12:13:50 +0300 Subject: [PATCH] samsung integration addded --- .../ejada/hmg/samsung_watch/SamsungWatch.kt | 118 +++++++++++++++++- .../ejada/hmg/samsung_watch/model/Vitals.kt | 11 +- .../reports/problems/problems-report.html | 2 +- .../health_provider.dart | 6 +- .../health_service.dart | 18 ++- .../smartwatch_health_data/model/Vitals.dart | 56 +++++++++ .../samsung_platform_channel.dart | 33 +++++ .../watch_connectors/samsung_health.dart | 37 ++++++ .../watch_connectors/watch_helper.dart | 4 + 9 files changed, 272 insertions(+), 13 deletions(-) create mode 100644 lib/features/smartwatch_health_data/model/Vitals.dart diff --git a/android/app/src/main/kotlin/com/ejada/hmg/samsung_watch/SamsungWatch.kt b/android/app/src/main/kotlin/com/ejada/hmg/samsung_watch/SamsungWatch.kt index 11778578..09efa394 100644 --- a/android/app/src/main/kotlin/com/ejada/hmg/samsung_watch/SamsungWatch.kt +++ b/android/app/src/main/kotlin/com/ejada/hmg/samsung_watch/SamsungWatch.kt @@ -28,7 +28,6 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel import kotlinx.coroutines.launch -import java.time.Duration import java.time.LocalDateTime import java.time.LocalTime @@ -75,6 +74,8 @@ class SamsungWatch( Permission.of(DataTypes.BLOOD_OXYGEN, AccessType.READ), Permission.of(DataTypes.ACTIVITY_SUMMARY, AccessType.READ), Permission.of(DataTypes.SLEEP, AccessType.READ), + Permission.of(DataTypes.BLOOD_OXYGEN, AccessType.READ), + Permission.of(DataTypes.BODY_TEMPERATURE, AccessType.READ), // Permission.of(DataTypes.SKIN_TEMPERATURE, AccessType.READ), // Permission.of(DataTypes.NUTRITION, AccessType.READ), @@ -115,7 +116,7 @@ class SamsungWatch( processHeartVital(heartRateList) Log.d("TAG"," the data is ${vitals}") print("the data is ${vitals}") - result.success("Permission Granted") + result.success("Data is obtained") } } @@ -132,7 +133,7 @@ class SamsungWatch( processSleepVital(sleepData) print("the data is $vitals") Log.d(TAG, "the data is $vitals") - result.success("Permission Granted") + result.success("Data is obtained") } } @@ -151,10 +152,80 @@ class SamsungWatch( processStepsCount(steps) print("the data is $vitals") Log.d(TAG, "the data is $vitals") - result.success("Permission Granted") + result.success("Data is obtained") + } + } + + "activitySummary"->{ + val dateTime = LocalDateTime.now().with(LocalTime.MIDNIGHT).minusDays(365) + val localTimeFilter = LocalTimeFilter.of(dateTime, LocalDateTime.now()) + val localTimeGroup = LocalTimeGroup.of(LocalTimeGroupUnit.HOURLY, 1) + val readRequest = DataType.ActivitySummaryType.TOTAL_CALORIES_BURNED.requestBuilder + .setLocalTimeFilterWithGroup(localTimeFilter, localTimeGroup) + .setOrdering(Ordering.DESC) + .build() + + scope.launch { + val activityResult = dataStore.aggregateData(readRequest).dataList + processActivity(activityResult) + Log.d("TAG"," the data is ${vitals}") + print("the data is ${vitals}") + result.success("Data is obtained") + } + } + + "bloodOxygen"->{ + val dateTime = LocalDateTime.now().with(LocalTime.MIDNIGHT).minusDays(365) + val localTimeFilter = LocalTimeFilter.of(dateTime, LocalDateTime.now()) + val readRequest = DataTypes.BLOOD_OXYGEN.readDataRequestBuilder + .setLocalTimeFilter(localTimeFilter) + .setOrdering(Ordering.DESC) + .build() + + scope.launch { + val bloodOxygenList = dataStore.readData(readRequest).dataList + processBloodOxygen(bloodOxygenList) + Log.d("TAG"," the data is ${vitals}") + print("the data is ${vitals["bloodOxygen"]}") + result.success("Data is obtained") + } + } + + + "bodyTemperature"->{ + val dateTime = LocalDateTime.now().with(LocalTime.MIDNIGHT).minusDays(365) + val localTimeFilter = LocalTimeFilter.of(dateTime, LocalDateTime.now()) + val readRequest = DataTypes.BODY_TEMPERATURE.readDataRequestBuilder + .setLocalTimeFilter(localTimeFilter) + .setOrdering(Ordering.DESC) + .build() + + scope.launch { + val bodyTemperatureList = dataStore.readData(readRequest).dataList + processBodyTemperature(bodyTemperatureList) + Log.d("TAG"," the data is ${vitals}") + print("the data is ${vitals["bodyTemperature"]}") + result.success("Data is obtained") } } + "retrieveData"->{ + if(vitals.isEmpty()){ + result.error("NoDataFound", "No Data was obtained", null) + return@setMethodCallHandler + } + result.success(""" + { + "heartRate": ${vitals["heartRate"]}, + "steps": ${vitals["steps"]}, + "sleep": ${vitals["sleep"]}, + "activity": ${vitals["activity"]}, + "bloodOxygen": ${vitals["bloodOxygen"]}, + "bodyTemperature": ${vitals["bodyTemperature"]} + } + """.trimIndent()) + } + "closeCoroutineScope"->{ destroy() @@ -168,16 +239,51 @@ class SamsungWatch( } } + private fun CoroutineScope.processBodyTemperature( bodyTemperatureList :List) { + vitals["bodyTemperature"] = mutableListOf() + bodyTemperatureList.forEach { stepData -> + val vitalData = Vitals().apply { + value = stepData.getValue(DataType.BodyTemperatureType.BODY_TEMPERATURE).toString() + timeStamp = stepData.endTime.toString() + } + (vitals["bodyTemperature"] as MutableList).add(vitalData) + } + } + + private fun CoroutineScope.processBloodOxygen( bloodOxygenList :List) { + vitals["bloodOxygen"] = mutableListOf() + bloodOxygenList.forEach { stepData -> + val vitalData = Vitals().apply { + value = stepData.getValue(DataType.BloodOxygenType.OXYGEN_SATURATION).toString() + timeStamp = stepData.endTime.toString() + } + (vitals["bloodOxygen"] as MutableList).add(vitalData) + } + } + + + private fun CoroutineScope.processActivity(activityResult: List>) { + + vitals["activity"] = mutableListOf() + activityResult.forEach { stepData -> + val vitalData = Vitals().apply { + value = stepData.value.toString() + timeStamp = stepData.endTime.toString() + } + (vitals["activity"] as MutableList).add(vitalData) + } + } + private fun CoroutineScope.processStepsCount(result: DataResponse>) { val stepCount = ArrayList>() var totalSteps: Long = 0 - vitals["steps"] = emptyList() + vitals["steps"] = mutableListOf() result.dataList.forEach { stepData -> val vitalData = Vitals().apply { value = (stepData.value as Long).toString() timeStamp = stepData.startTime.toString() } - (vitals["sleep"] as MutableList).add(vitalData) + (vitals["steps"] as MutableList).add(vitalData) } } diff --git a/android/app/src/main/kotlin/com/ejada/hmg/samsung_watch/model/Vitals.kt b/android/app/src/main/kotlin/com/ejada/hmg/samsung_watch/model/Vitals.kt index c3a0c672..6ab7d37c 100644 --- a/android/app/src/main/kotlin/com/ejada/hmg/samsung_watch/model/Vitals.kt +++ b/android/app/src/main/kotlin/com/ejada/hmg/samsung_watch/model/Vitals.kt @@ -2,5 +2,12 @@ package com.ejada.hmg.samsung_watch.model data class Vitals( var value : String = "", - var timeStamp : String = "" -) \ No newline at end of file + var timeStamp :String = "" +){ + override fun toString(): String { + return """{ + "value": "$value", + "timeStamp": "$timeStamp"} + """.trimIndent() + } +} \ No newline at end of file diff --git a/android/build/reports/problems/problems-report.html b/android/build/reports/problems/problems-report.html index 9b679bc9..a4239f0d 100644 --- a/android/build/reports/problems/problems-report.html +++ b/android/build/reports/problems/problems-report.html @@ -650,7 +650,7 @@ code + .copy-button { diff --git a/lib/features/smartwatch_health_data/health_provider.dart b/lib/features/smartwatch_health_data/health_provider.dart index 23d73114..7a175d8e 100644 --- a/lib/features/smartwatch_health_data/health_provider.dart +++ b/lib/features/smartwatch_health_data/health_provider.dart @@ -109,17 +109,17 @@ class HealthProvider with ChangeNotifier { if (result.isError) { error = 'Error initializing device: ${result.asError}'; } else { - getHeartRate(); + getVitals(); getIt.get().pushPage(page: SmartWatchActivity()); print('Device initialized successfully'); } notifyListeners(); } - void getHeartRate() async{ + void getVitals() async{ isLoading = true; notifyListeners(); - final result = await _healthService.getHeartRate(); + final result = await _healthService.getVitals(); isLoading = false; notifyListeners(); } diff --git a/lib/features/smartwatch_health_data/health_service.dart b/lib/features/smartwatch_health_data/health_service.dart index 198648a6..4d532a5a 100644 --- a/lib/features/smartwatch_health_data/health_service.dart +++ b/lib/features/smartwatch_health_data/health_service.dart @@ -1,8 +1,11 @@ import 'dart:async'; +import 'dart:convert'; +import 'dart:developer'; import 'dart:io'; import 'package:health/health.dart'; import 'package:hmg_patient_app_new/core/common_models/smart_watch.dart'; +import 'package:hmg_patient_app_new/features/smartwatch_health_data/model/Vitals.dart'; import 'package:hmg_patient_app_new/features/smartwatch_health_data/watch_connectors/create_watch_helper.dart'; import 'package:hmg_patient_app_new/features/smartwatch_health_data/watch_connectors/watch_helper.dart'; import 'package:permission_handler/permission_handler.dart'; @@ -180,7 +183,7 @@ class HealthService { return await watchHelper!.initDevice(); } - FutureOr getHeartRate() async { + FutureOr getVitals() async { if (watchHelper == null) { print('No watch helper found'); return; @@ -189,6 +192,19 @@ class HealthService { await watchHelper!.getHeartRate(); await watchHelper!.getSleep(); await watchHelper!.getSteps(); + await watchHelper!.getActivity(); + await watchHelper!.getBodyTemperature(); + await watchHelper!.getBloodOxygen(); + Result data = await watchHelper!.retrieveData(); + + if(data.isError) { + print('Unable to get the data'); + } + + + var response = jsonDecode(data.asValue?.value?.toString()?.trim().replaceAll("\n", "")??""); + VitalsWRTType vitals = VitalsWRTType.fromMap(response); + log("the data is ${vitals}"); }catch(e){ print('Error getting heart rate: $e'); } diff --git a/lib/features/smartwatch_health_data/model/Vitals.dart b/lib/features/smartwatch_health_data/model/Vitals.dart new file mode 100644 index 00000000..bbcf1035 --- /dev/null +++ b/lib/features/smartwatch_health_data/model/Vitals.dart @@ -0,0 +1,56 @@ +class Vitals { + final String value; + final String timestamp; + + Vitals({ + required this.value, + required this.timestamp, + }); + + factory Vitals.fromMap(Map map) { + return Vitals( + value: map['value'] ?? "", + timestamp: map['timestamp'] ?? "", + ); + } +} + +class VitalsWRTType { + final List heartRate; + final List sleep; + final List step; + final List activity; + final List bodyOxygen; + final List bodyTemperature; + + VitalsWRTType({required this.bodyOxygen, required this.bodyTemperature, required this.heartRate, required this.sleep, required this.step, required this.activity}); + + factory VitalsWRTType.fromMap(Map map) { + List activity = []; + List steps = []; + List sleeps = []; + List heartRate = []; + List bodyOxygen = []; + List bodyTemperature = []; + map["activity"].forEach((element) { + activity.add(Vitals.fromMap(element)); + }); + map["steps"].forEach((element) { + steps.add(Vitals.fromMap(element)); + }); + map["sleep"].forEach((element) { + sleeps.add(Vitals.fromMap(element)); + }); + map["heartRate"].forEach((element) { + heartRate.add(Vitals.fromMap(element)); + }); + map["bloodOxygen"].forEach((element) { + bodyOxygen.add(Vitals.fromMap(element)); + }); + map["bodyTemperature"].forEach((element) { + bodyTemperature.add(Vitals.fromMap(element)); + }); + + return VitalsWRTType(bodyTemperature: bodyTemperature, bodyOxygen: bodyOxygen, heartRate: heartRate, sleep: sleeps, step: steps, activity: activity); + } +} diff --git a/lib/features/smartwatch_health_data/platform_channel/samsung_platform_channel.dart b/lib/features/smartwatch_health_data/platform_channel/samsung_platform_channel.dart index efc3e39f..1e3ef6df 100644 --- a/lib/features/smartwatch_health_data/platform_channel/samsung_platform_channel.dart +++ b/lib/features/smartwatch_health_data/platform_channel/samsung_platform_channel.dart @@ -44,4 +44,37 @@ class SamsungPlatformChannel { return Result.error(e); } } + + Future> getActivity() async { + try{ + await _channel.invokeMethod('activitySummary'); + return Result.value(true); + }catch(e){ + return Result.error(e); + } + } + + Future> retrieveData() async { + try{ + return Result.value(await _channel.invokeMethod('retrieveData')); + }catch(e){ + return Result.error(e); + } + } + + Future> getBloodOxygen() async { + try{ + return Result.value(await _channel.invokeMethod('bloodOxygen')); + }catch(e){ + return Result.error(e); + } + } + + Future> getBodyTemperature() async { + try{ + return Result.value(await _channel.invokeMethod('bodyTemperature')); + }catch(e){ + return Result.error(e); + } + } } \ No newline at end of file diff --git a/lib/features/smartwatch_health_data/watch_connectors/samsung_health.dart b/lib/features/smartwatch_health_data/watch_connectors/samsung_health.dart index fd1c257b..c2e8fd3e 100644 --- a/lib/features/smartwatch_health_data/watch_connectors/samsung_health.dart +++ b/lib/features/smartwatch_health_data/watch_connectors/samsung_health.dart @@ -48,5 +48,42 @@ class SamsungHealth extends WatchHelper { print('Error getting heart rate: $e'); } } + @override + Future getActivity() async{ + try { + await platformChannel.getActivity(); + }catch(e){ + print('Error getting heart rate: $e'); + } + } + + @override + Future retrieveData() async{ + try { + return await platformChannel.retrieveData(); + }catch(e){ + print('Error getting heart rate: $e'); + } + } + + @override + Future getBloodOxygen() async{ + try { + return await platformChannel.getBloodOxygen(); + }catch(e){ + print('Error getting heart rate: $e'); + } + } + + @override + Future getBodyTemperature() async { + try { + return await platformChannel.getBodyTemperature(); + }catch(e){ + print('Error getting heart rate: $e'); + } + } + + } \ No newline at end of file diff --git a/lib/features/smartwatch_health_data/watch_connectors/watch_helper.dart b/lib/features/smartwatch_health_data/watch_connectors/watch_helper.dart index 0ce360fe..fe62fdc0 100644 --- a/lib/features/smartwatch_health_data/watch_connectors/watch_helper.dart +++ b/lib/features/smartwatch_health_data/watch_connectors/watch_helper.dart @@ -5,5 +5,9 @@ abstract class WatchHelper { FutureOr getHeartRate(); FutureOr getSleep(); FutureOr getSteps(); + Future getActivity(); + Future retrieveData(); + Future getBodyTemperature(); + Future getBloodOxygen(); } \ No newline at end of file