From dd6a32a5af51ffee1c4b2ef0f3e932bb6ed14914 Mon Sep 17 00:00:00 2001 From: bruce3x Date: Tue, 26 Dec 2023 17:10:13 +0800 Subject: [PATCH 1/4] feat: add `STAND_TIME` data type --- packages/health/README.md | 7 ++++--- packages/health/ios/Classes/SwiftHealthPlugin.swift | 4 +++- packages/health/lib/src/data_types.dart | 3 +++ 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/health/README.md b/packages/health/README.md index f3d47d36a..c2b583427 100644 --- a/packages/health/README.md +++ b/packages/health/README.md @@ -23,7 +23,7 @@ Note that for Android, the target phone **needs** to have [Google Fit](https://w ## Data Types | **Data Type** | **Unit** | **iOS** | **Android (Google Fit)** | **Android (Health Connect)** | **Comments** | -| --------------------------- | ----------------------- | ------- | ------------------------ |------------------------------| -------------------------------------- | +|-----------------------------|-------------------------| ------- |--------------------------|------------------------------| -------------------------------------- | | ACTIVE_ENERGY_BURNED | CALORIES | yes | yes | yes | | | BASAL_ENERGY_BURNED | CALORIES | yes | | yes | | | BLOOD_GLUCOSE | MILLIGRAM_PER_DECILITER | yes | yes | yes | | @@ -37,8 +37,8 @@ Note that for Android, the target phone **needs** to have [Google Fit](https://w | HEART_RATE | BEATS_PER_MINUTE | yes | yes | yes | | | HEIGHT | METERS | yes | yes | yes | | | RESTING_HEART_RATE | BEATS_PER_MINUTE | yes | | yes | | -| RESPIRATORY_RATE | RESPIRATIONS_PER_MINUTE | yes | | yes | -| PERIPHERAL_PERFUSION_INDEX | PERCENTAGE | yes | | | +| RESPIRATORY_RATE | RESPIRATIONS_PER_MINUTE | yes | | yes | +| PERIPHERAL_PERFUSION_INDEX | PERCENTAGE | yes | | | | STEPS | COUNT | yes | yes | yes | | | WAIST_CIRCUMFERENCE | METERS | yes | | | | | WALKING_HEART_RATE | BEATS_PER_MINUTE | yes | | | | @@ -71,6 +71,7 @@ Note that for Android, the target phone **needs** to have [Google Fit](https://w | AUDIOGRAM | DECIBEL_HEARING_LEVEL | yes | | | | | ELECTROCARDIOGRAM | VOLT | yes | | | Requires Apple Watch to write the data | | NUTRITION | NO_UNIT | yes | yes | yes | | +| STAND_TIME | MINUTES | yes | | | | ## Setup diff --git a/packages/health/ios/Classes/SwiftHealthPlugin.swift b/packages/health/ios/Classes/SwiftHealthPlugin.swift index 36d560010..89f18882a 100644 --- a/packages/health/ios/Classes/SwiftHealthPlugin.swift +++ b/packages/health/ios/Classes/SwiftHealthPlugin.swift @@ -62,7 +62,8 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { let HEADACHE_SEVERE = "HEADACHE_SEVERE" let ELECTROCARDIOGRAM = "ELECTROCARDIOGRAM" let NUTRITION = "NUTRITION" - + let STAND_TIME = "STAND_TIME" + // Health Unit types // MOLE_UNIT_WITH_MOLAR_MASS, // requires molar mass input - not supported yet // MOLE_UNIT_WITH_PREFIX_MOLAR_MASS, // requires molar mass & prefix input - not supported yet @@ -1052,6 +1053,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { dataTypesDict[WORKOUT] = HKSampleType.workoutType() dataTypesDict[NUTRITION] = HKSampleType.correlationType( forIdentifier: .food)! + dataTypesDict[STAND_TIME] = HKSampleType.quantityType(forIdentifier: .appleStandTime) healthDataTypes = Array(dataTypesDict.values) } diff --git a/packages/health/lib/src/data_types.dart b/packages/health/lib/src/data_types.dart index bca127cd0..1c2372834 100644 --- a/packages/health/lib/src/data_types.dart +++ b/packages/health/lib/src/data_types.dart @@ -49,6 +49,7 @@ enum HealthDataType { HEADACHE_SEVERE, HEADACHE_UNSPECIFIED, NUTRITION, + STAND_TIME, // Heart Rate events (specific to Apple Watch) HIGH_HEART_RATE_EVENT, @@ -114,6 +115,7 @@ const List _dataTypeKeysIOS = [ HealthDataType.HEADACHE_UNSPECIFIED, HealthDataType.ELECTROCARDIOGRAM, HealthDataType.NUTRITION, + HealthDataType.STAND_TIME, ]; /// List of data types available on Android @@ -208,6 +210,7 @@ const Map _dataTypeToUnit = { HealthDataType.ELECTROCARDIOGRAM: HealthDataUnit.VOLT, HealthDataType.NUTRITION: HealthDataUnit.NO_UNIT, + HealthDataType.STAND_TIME: HealthDataUnit.MINUTE, }; const PlatformTypeJsonValue = { From b6978abbba4ab6cf54a94ec34ebc4f8348d6bb50 Mon Sep 17 00:00:00 2001 From: bruce3x Date: Mon, 8 Jan 2024 18:56:52 +0800 Subject: [PATCH 2/4] feat: get data only from watch --- packages/health/ios/Classes/SwiftHealthPlugin.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/health/ios/Classes/SwiftHealthPlugin.swift b/packages/health/ios/Classes/SwiftHealthPlugin.swift index 89f18882a..280e5e3dd 100644 --- a/packages/health/ios/Classes/SwiftHealthPlugin.swift +++ b/packages/health/ios/Classes/SwiftHealthPlugin.swift @@ -582,10 +582,13 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { let predicate = HKQuery.predicateForSamples( withStart: dateFrom, end: dateTo, options: .strictStartDate) + let watchPredicate = HKQuery.predicateForObjects(withDeviceProperty: HKDevicePropertyKeyModel, allowedValues: ["Watch"]) + let combinedPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: [predicate, watchPredicate]) + let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false) let query = HKSampleQuery( - sampleType: dataType, predicate: predicate, limit: limit, sortDescriptors: [sortDescriptor] + sampleType: dataType, predicate: combinedPredicate, limit: limit, sortDescriptors: [sortDescriptor] ) { [self] x, samplesOrNil, error in From 42f7eef7ceef85adb6e55d6627431c58155b4bd6 Mon Sep 17 00:00:00 2001 From: tm-hirai Date: Thu, 14 Mar 2024 21:46:29 +0900 Subject: [PATCH 3/4] add body water mass to health connect --- packages/health/README.md | 1 + .../cachet/plugins/health/HealthPlugin.kt | 18 ++++++++++++++++++ packages/health/lib/src/data_types.dart | 3 +++ 3 files changed, 22 insertions(+) diff --git a/packages/health/README.md b/packages/health/README.md index 800e245fc..7d6a63ca2 100644 --- a/packages/health/README.md +++ b/packages/health/README.md @@ -33,6 +33,7 @@ Note that for Android, the target phone **needs** to have [Google Fit](https://w | BODY_FAT_PERCENTAGE | PERCENTAGE | yes | yes | yes | | | BODY_MASS_INDEX | NO_UNIT | yes | yes | yes | | | BODY_TEMPERATURE | DEGREE_CELSIUS | yes | yes | yes | | +| BODY_WATER_MASS | KILOGRAMS | | | yes | | | ELECTRODERMAL_ACTIVITY | SIEMENS | yes | | | | | HEART_RATE | BEATS_PER_MINUTE | yes | yes | yes | | | HEIGHT | METERS | yes | yes | yes | | diff --git a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt index 424f0afea..77014f78d 100644 --- a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt +++ b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt @@ -85,6 +85,7 @@ class HealthPlugin(private var channel: MethodChannel? = null) : private var ACTIVE_ENERGY_BURNED = "ACTIVE_ENERGY_BURNED" private var HEART_RATE = "HEART_RATE" private var BODY_TEMPERATURE = "BODY_TEMPERATURE" + private var BODY_WATER_MASS = "BODY_WATER_MASS" private var BLOOD_PRESSURE_SYSTOLIC = "BLOOD_PRESSURE_SYSTOLIC" private var BLOOD_PRESSURE_DIASTOLIC = "BLOOD_PRESSURE_DIASTOLIC" private var BLOOD_OXYGEN = "BLOOD_OXYGEN" @@ -1940,6 +1941,16 @@ class HealthPlugin(private var channel: MethodChannel? = null) : ), ) + is BodyWaterMassRecord -> return listOf( + mapOf( + "value" to record.mass.inKilograms, + "date_from" to record.time.toEpochMilli(), + "date_to" to record.time.toEpochMilli(), + "source_id" to "", + "source_name" to metadata.dataOrigin.packageName, + ), + ) + is BloodPressureRecord -> return listOf( mapOf( "value" to if (dataType == BLOOD_PRESSURE_DIASTOLIC) record.diastolic.inMillimetersOfMercury else record.systolic.inMillimetersOfMercury, @@ -2122,6 +2133,12 @@ class HealthPlugin(private var channel: MethodChannel? = null) : zoneOffset = null, ) + BODY_WATER_MASS -> BodyWaterMassRecord( + time = Instant.ofEpochMilli(startTime), + mass = Mass.kilograms(value), + zoneOffset = null, + ) + BLOOD_OXYGEN -> OxygenSaturationRecord( time = Instant.ofEpochMilli(startTime), percentage = Percentage(value), @@ -2406,6 +2423,7 @@ class HealthPlugin(private var channel: MethodChannel? = null) : ACTIVE_ENERGY_BURNED to ActiveCaloriesBurnedRecord::class, HEART_RATE to HeartRateRecord::class, BODY_TEMPERATURE to BodyTemperatureRecord::class, + BODY_WATER_MASS to BodyWaterMassRecord::class, BLOOD_PRESSURE_SYSTOLIC to BloodPressureRecord::class, BLOOD_PRESSURE_DIASTOLIC to BloodPressureRecord::class, BLOOD_OXYGEN to OxygenSaturationRecord::class, diff --git a/packages/health/lib/src/data_types.dart b/packages/health/lib/src/data_types.dart index bca127cd0..ffda58380 100644 --- a/packages/health/lib/src/data_types.dart +++ b/packages/health/lib/src/data_types.dart @@ -12,6 +12,7 @@ enum HealthDataType { BODY_FAT_PERCENTAGE, BODY_MASS_INDEX, BODY_TEMPERATURE, + BODY_WATER_MASS, DIETARY_CARBS_CONSUMED, DIETARY_ENERGY_CONSUMED, DIETARY_FATS_CONSUMED, @@ -126,6 +127,7 @@ const List _dataTypeKeysAndroid = [ HealthDataType.BODY_FAT_PERCENTAGE, HealthDataType.BODY_MASS_INDEX, HealthDataType.BODY_TEMPERATURE, + HealthDataType.BODY_WATER_MASS, HealthDataType.HEART_RATE, HealthDataType.HEIGHT, HealthDataType.STEPS, @@ -160,6 +162,7 @@ const Map _dataTypeToUnit = { HealthDataType.BODY_FAT_PERCENTAGE: HealthDataUnit.PERCENT, HealthDataType.BODY_MASS_INDEX: HealthDataUnit.NO_UNIT, HealthDataType.BODY_TEMPERATURE: HealthDataUnit.DEGREE_CELSIUS, + HealthDataType.BODY_WATER_MASS: HealthDataUnit.KILOGRAM, HealthDataType.DIETARY_CARBS_CONSUMED: HealthDataUnit.GRAM, HealthDataType.DIETARY_ENERGY_CONSUMED: HealthDataUnit.KILOCALORIE, HealthDataType.DIETARY_FATS_CONSUMED: HealthDataUnit.GRAM, From 1dc36ae524a72a0c2074c81a312d9fb51f394f2c Mon Sep 17 00:00:00 2001 From: Shubham Singh Date: Tue, 26 Mar 2024 19:52:47 +0530 Subject: [PATCH 4/4] Add empty value check for `calories` nutrition --- packages/health/ios/Classes/SwiftHealthPlugin.swift | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/health/ios/Classes/SwiftHealthPlugin.swift b/packages/health/ios/Classes/SwiftHealthPlugin.swift index ec101d222..824099a78 100644 --- a/packages/health/ios/Classes/SwiftHealthPlugin.swift +++ b/packages/health/ios/Classes/SwiftHealthPlugin.swift @@ -466,8 +466,10 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { var nutrition = Set() - let caloriesSample = HKQuantitySample(type: HKSampleType.quantityType(forIdentifier: .dietaryEnergyConsumed)!, quantity: HKQuantity(unit: HKUnit.kilocalorie(), doubleValue: calories), start: dateFrom, end: dateTo, metadata: metadata) - nutrition.insert(caloriesSample) + if(calories > 0) { + let caloriesSample = HKQuantitySample(type: HKSampleType.quantityType(forIdentifier: .dietaryEnergyConsumed)!, quantity: HKQuantity(unit: HKUnit.kilocalorie(), doubleValue: calories), start: dateFrom, end: dateTo, metadata: metadata) + nutrition.insert(caloriesSample) + } if(carbs > 0) { let carbsSample = HKQuantitySample(type: HKSampleType.quantityType(forIdentifier: .dietaryCarbohydrates)!, quantity: HKQuantity(unit: HKUnit.gram(), doubleValue: carbs), start: dateFrom, end: dateTo, metadata: metadata)