@capgo/capacitor-health 8.2.17 → 8.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -3
- package/android/build.gradle +1 -1
- package/android/src/main/AndroidManifest.xml +20 -0
- package/android/src/main/java/app/capgo/plugin/health/HealthDataType.kt +24 -1
- package/android/src/main/java/app/capgo/plugin/health/HealthManager.kt +264 -14
- package/android/src/main/java/app/capgo/plugin/health/HealthPlugin.kt +4 -1
- package/android/src/main/java/app/capgo/plugin/health/WorkoutType.kt +117 -1
- package/dist/docs.json +336 -0
- package/dist/esm/definitions.d.ts +11 -3
- package/dist/esm/definitions.js.map +1 -1
- package/ios/Sources/HealthPlugin/Health.swift +582 -2
- package/ios/Sources/HealthPlugin/HealthPlugin.swift +7 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -174,10 +174,45 @@ await Health.saveSample({
|
|
|
174
174
|
| `oxygenSaturation` | `percent` | Blood oxygen saturation (SpO2) |
|
|
175
175
|
| `restingHeartRate` | `bpm` | Resting heart rate |
|
|
176
176
|
| `heartRateVariability` | `millisecond` | Heart rate variability (HRV) |
|
|
177
|
+
| `bloodPressure` | `mmHg` | Blood pressure (requires systolic/diastolic values) |
|
|
178
|
+
| `bloodGlucose` | `mg/dL` | Blood glucose level |
|
|
179
|
+
| `bodyTemperature` | `celsius` | Body temperature |
|
|
180
|
+
| `height` | `centimeter` | Body height |
|
|
181
|
+
| `flightsClimbed` | `count` | Floors / flights of stairs climbed |
|
|
182
|
+
| `exerciseTime` | `minute` | Apple Exercise Time (iOS only) |
|
|
183
|
+
| `distanceCycling` | `meter` | Cycling distance |
|
|
184
|
+
| `bodyFat` | `percent` | Body fat percentage |
|
|
185
|
+
| `basalBodyTemperature` | `celsius` | Basal body temperature |
|
|
186
|
+
| `basalCalories` | `kilocalorie` | Basal metabolic rate / resting energy |
|
|
187
|
+
| `totalCalories` | `kilocalorie` | Total energy burned (active + basal) |
|
|
188
|
+
| `mindfulness` | `minute` | Mindfulness / meditation sessions |
|
|
177
189
|
| `workouts` | N/A | Workout sessions (read-only, use with `queryWorkouts()`) |
|
|
178
190
|
|
|
179
191
|
All write operations expect the default unit shown above. On Android the `metadata` option is currently ignored by Health Connect.
|
|
180
192
|
|
|
193
|
+
**Blood Pressure:** Blood pressure requires both systolic and diastolic values:
|
|
194
|
+
|
|
195
|
+
```ts
|
|
196
|
+
await Health.saveSample({
|
|
197
|
+
dataType: 'bloodPressure',
|
|
198
|
+
value: 120, // systolic value (used as main value)
|
|
199
|
+
systolic: 120,
|
|
200
|
+
diastolic: 80,
|
|
201
|
+
startDate: new Date().toISOString(),
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
// Reading blood pressure returns samples with systolic/diastolic fields
|
|
205
|
+
const { samples } = await Health.readSamples({
|
|
206
|
+
dataType: 'bloodPressure',
|
|
207
|
+
startDate: new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString(),
|
|
208
|
+
endDate: new Date().toISOString(),
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
samples.forEach((sample) => {
|
|
212
|
+
console.log(`BP: ${sample.systolic}/${sample.diastolic} mmHg`);
|
|
213
|
+
});
|
|
214
|
+
```
|
|
215
|
+
|
|
181
216
|
**Note about workouts:** To query workout data using `queryWorkouts()`, you need to explicitly request `workouts` permission:
|
|
182
217
|
|
|
183
218
|
```ts
|
|
@@ -521,6 +556,8 @@ Supported on iOS (HealthKit) and Android (Health Connect).
|
|
|
521
556
|
| **`sourceName`** | <code>string</code> | |
|
|
522
557
|
| **`sourceId`** | <code>string</code> | |
|
|
523
558
|
| **`sleepState`** | <code><a href="#sleepstate">SleepState</a></code> | For sleep data, indicates the sleep state (e.g., 'asleep', 'awake', 'rem', 'deep', 'light'). |
|
|
559
|
+
| **`systolic`** | <code>number</code> | For blood pressure data, the systolic value in mmHg. |
|
|
560
|
+
| **`diastolic`** | <code>number</code> | For blood pressure data, the diastolic value in mmHg. |
|
|
524
561
|
|
|
525
562
|
|
|
526
563
|
#### QueryOptions
|
|
@@ -544,6 +581,8 @@ Supported on iOS (HealthKit) and Android (Health Connect).
|
|
|
544
581
|
| **`startDate`** | <code>string</code> | ISO 8601 start date for the sample. Defaults to now. |
|
|
545
582
|
| **`endDate`** | <code>string</code> | ISO 8601 end date for the sample. Defaults to startDate. |
|
|
546
583
|
| **`metadata`** | <code><a href="#record">Record</a><string, string></code> | Metadata key-value pairs forwarded to the native APIs where supported. |
|
|
584
|
+
| **`systolic`** | <code>number</code> | For blood pressure data, the systolic value in mmHg. Required when dataType is 'bloodPressure'. |
|
|
585
|
+
| **`diastolic`** | <code>number</code> | For blood pressure data, the diastolic value in mmHg. Required when dataType is 'bloodPressure'. |
|
|
547
586
|
|
|
548
587
|
|
|
549
588
|
#### QueryWorkoutsResult
|
|
@@ -614,12 +653,12 @@ Supported on iOS (HealthKit) and Android (Health Connect).
|
|
|
614
653
|
|
|
615
654
|
#### HealthDataType
|
|
616
655
|
|
|
617
|
-
<code>'steps' | 'distance' | 'calories' | 'heartRate' | 'weight' | 'sleep' | 'respiratoryRate' | 'oxygenSaturation' | 'restingHeartRate' | 'heartRateVariability'</code>
|
|
656
|
+
<code>'steps' | 'distance' | 'calories' | 'heartRate' | 'weight' | 'sleep' | 'respiratoryRate' | 'oxygenSaturation' | 'restingHeartRate' | 'heartRateVariability' | 'bloodPressure' | 'bloodGlucose' | 'bodyTemperature' | 'height' | 'flightsClimbed' | 'exerciseTime' | 'distanceCycling' | 'bodyFat' | 'basalBodyTemperature' | 'basalCalories' | 'totalCalories' | 'mindfulness'</code>
|
|
618
657
|
|
|
619
658
|
|
|
620
659
|
#### HealthUnit
|
|
621
660
|
|
|
622
|
-
<code>'count' | 'meter' | 'kilocalorie' | 'bpm' | 'kilogram' | 'minute' | 'percent' | 'millisecond'</code>
|
|
661
|
+
<code>'count' | 'meter' | 'kilocalorie' | 'bpm' | 'kilogram' | 'minute' | 'percent' | 'millisecond' | 'mmHg' | 'mg/dL' | 'celsius' | 'fahrenheit' | 'centimeter'</code>
|
|
623
662
|
|
|
624
663
|
|
|
625
664
|
#### SleepState
|
|
@@ -636,7 +675,7 @@ Construct a type with a set of properties K of type T
|
|
|
636
675
|
|
|
637
676
|
#### WorkoutType
|
|
638
677
|
|
|
639
|
-
<code>'running' | 'cycling' | 'walking' | 'swimming' | 'yoga' | 'strengthTraining' | 'hiking' | 'tennis' | 'basketball' | 'soccer' | 'americanFootball' | 'baseball' | 'crossTraining' | 'elliptical' | 'rowing' | 'stairClimbing' | 'traditionalStrengthTraining' | 'waterFitness' | 'waterPolo' | 'waterSports' | 'wrestling' | 'other'</code>
|
|
678
|
+
<code>'running' | 'cycling' | 'walking' | 'swimming' | 'yoga' | 'strengthTraining' | 'hiking' | 'tennis' | 'basketball' | 'soccer' | 'americanFootball' | 'baseball' | 'crossTraining' | 'elliptical' | 'rowing' | 'stairClimbing' | 'traditionalStrengthTraining' | 'waterFitness' | 'waterPolo' | 'waterSports' | 'wrestling' | 'archery' | 'australianFootball' | 'badminton' | 'barre' | 'bowling' | 'boxing' | 'climbing' | 'cooldown' | 'coreTraining' | 'cricket' | 'crossCountrySkiing' | 'curling' | 'dance' | 'discSports' | 'downhillSkiing' | 'equestrianSports' | 'fencing' | 'fishing' | 'fitnessGaming' | 'flexibility' | 'functionalStrengthTraining' | 'golf' | 'gymnastics' | 'handball' | 'handCycling' | 'highIntensityIntervalTraining' | 'hockey' | 'hunting' | 'jumpRope' | 'kickboxing' | 'lacrosse' | 'martialArts' | 'mindAndBody' | 'mixedCardio' | 'paddleSports' | 'pickleball' | 'pilates' | 'play' | 'preparationAndRecovery' | 'racquetball' | 'rugby' | 'sailing' | 'skatingSports' | 'snowboarding' | 'snowSports' | 'softball' | 'squash' | 'stairs' | 'stepTraining' | 'surfingSports' | 'tableTennis' | 'taiChi' | 'trackAndField' | 'transition' | 'underwaterDiving' | 'volleyball' | 'wheelchairRunPace' | 'wheelchairWalkPace' | 'cardioDance' | 'socialDance' | 'other'</code>
|
|
640
679
|
|
|
641
680
|
|
|
642
681
|
#### BucketType
|
package/android/build.gradle
CHANGED
|
@@ -62,7 +62,7 @@ dependencies {
|
|
|
62
62
|
implementation project(':capacitor-android')
|
|
63
63
|
implementation "androidx.appcompat:appcompat:${androidxAppCompatVersion}"
|
|
64
64
|
implementation ("org.jetbrains.kotlin:kotlin-stdlib:" + project.ext.kotlinVersion)
|
|
65
|
-
implementation 'androidx.health.connect:connect-client:1.1.0
|
|
65
|
+
implementation 'androidx.health.connect:connect-client:1.1.0'
|
|
66
66
|
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.1'
|
|
67
67
|
testImplementation "junit:junit:${junitVersion}"
|
|
68
68
|
androidTestImplementation "androidx.test.ext:junit:${androidxJunitVersion}"
|
|
@@ -19,6 +19,26 @@
|
|
|
19
19
|
<uses-permission android:name="android.permission.health.WRITE_RESTING_HEART_RATE" />
|
|
20
20
|
<uses-permission android:name="android.permission.health.READ_HEART_RATE_VARIABILITY" />
|
|
21
21
|
<uses-permission android:name="android.permission.health.WRITE_HEART_RATE_VARIABILITY" />
|
|
22
|
+
<uses-permission android:name="android.permission.health.READ_BLOOD_PRESSURE" />
|
|
23
|
+
<uses-permission android:name="android.permission.health.WRITE_BLOOD_PRESSURE" />
|
|
24
|
+
<uses-permission android:name="android.permission.health.READ_BLOOD_GLUCOSE" />
|
|
25
|
+
<uses-permission android:name="android.permission.health.WRITE_BLOOD_GLUCOSE" />
|
|
26
|
+
<uses-permission android:name="android.permission.health.READ_BODY_TEMPERATURE" />
|
|
27
|
+
<uses-permission android:name="android.permission.health.WRITE_BODY_TEMPERATURE" />
|
|
28
|
+
<uses-permission android:name="android.permission.health.READ_HEIGHT" />
|
|
29
|
+
<uses-permission android:name="android.permission.health.WRITE_HEIGHT" />
|
|
30
|
+
<uses-permission android:name="android.permission.health.READ_FLOORS_CLIMBED" />
|
|
31
|
+
<uses-permission android:name="android.permission.health.WRITE_FLOORS_CLIMBED" />
|
|
32
|
+
<uses-permission android:name="android.permission.health.READ_BODY_FAT" />
|
|
33
|
+
<uses-permission android:name="android.permission.health.WRITE_BODY_FAT" />
|
|
34
|
+
<uses-permission android:name="android.permission.health.READ_BASAL_BODY_TEMPERATURE" />
|
|
35
|
+
<uses-permission android:name="android.permission.health.WRITE_BASAL_BODY_TEMPERATURE" />
|
|
36
|
+
<uses-permission android:name="android.permission.health.READ_BASAL_METABOLIC_RATE" />
|
|
37
|
+
<uses-permission android:name="android.permission.health.WRITE_BASAL_METABOLIC_RATE" />
|
|
38
|
+
<uses-permission android:name="android.permission.health.READ_TOTAL_CALORIES_BURNED" />
|
|
39
|
+
<uses-permission android:name="android.permission.health.WRITE_TOTAL_CALORIES_BURNED" />
|
|
40
|
+
<uses-permission android:name="android.permission.health.READ_MINDFULNESS" />
|
|
41
|
+
<uses-permission android:name="android.permission.health.WRITE_MINDFULNESS" />
|
|
22
42
|
<uses-permission android:name="android.permission.health.READ_EXERCISE" />
|
|
23
43
|
|
|
24
44
|
<!-- Query for Health Connect availability -->
|
|
@@ -1,19 +1,31 @@
|
|
|
1
1
|
package app.capgo.plugin.health
|
|
2
2
|
|
|
3
|
+
import androidx.health.connect.client.feature.ExperimentalMindfulnessSessionApi
|
|
3
4
|
import androidx.health.connect.client.permission.HealthPermission
|
|
4
5
|
import androidx.health.connect.client.records.ActiveCaloriesBurnedRecord
|
|
6
|
+
import androidx.health.connect.client.records.BasalBodyTemperatureRecord
|
|
7
|
+
import androidx.health.connect.client.records.BasalMetabolicRateRecord
|
|
8
|
+
import androidx.health.connect.client.records.BloodGlucoseRecord
|
|
9
|
+
import androidx.health.connect.client.records.BloodPressureRecord
|
|
10
|
+
import androidx.health.connect.client.records.BodyFatRecord
|
|
11
|
+
import androidx.health.connect.client.records.BodyTemperatureRecord
|
|
5
12
|
import androidx.health.connect.client.records.DistanceRecord
|
|
13
|
+
import androidx.health.connect.client.records.FloorsClimbedRecord
|
|
6
14
|
import androidx.health.connect.client.records.HeartRateRecord
|
|
7
15
|
import androidx.health.connect.client.records.HeartRateVariabilityRmssdRecord
|
|
16
|
+
import androidx.health.connect.client.records.HeightRecord
|
|
17
|
+
import androidx.health.connect.client.records.MindfulnessSessionRecord
|
|
8
18
|
import androidx.health.connect.client.records.OxygenSaturationRecord
|
|
9
19
|
import androidx.health.connect.client.records.Record
|
|
10
20
|
import androidx.health.connect.client.records.RespiratoryRateRecord
|
|
11
21
|
import androidx.health.connect.client.records.RestingHeartRateRecord
|
|
12
22
|
import androidx.health.connect.client.records.SleepSessionRecord
|
|
13
23
|
import androidx.health.connect.client.records.StepsRecord
|
|
24
|
+
import androidx.health.connect.client.records.TotalCaloriesBurnedRecord
|
|
14
25
|
import androidx.health.connect.client.records.WeightRecord
|
|
15
26
|
import kotlin.reflect.KClass
|
|
16
27
|
|
|
28
|
+
@OptIn(ExperimentalMindfulnessSessionApi::class)
|
|
17
29
|
enum class HealthDataType(
|
|
18
30
|
val identifier: String,
|
|
19
31
|
val recordClass: KClass<out Record>,
|
|
@@ -28,7 +40,18 @@ enum class HealthDataType(
|
|
|
28
40
|
RESPIRATORY_RATE("respiratoryRate", RespiratoryRateRecord::class, "bpm"),
|
|
29
41
|
OXYGEN_SATURATION("oxygenSaturation", OxygenSaturationRecord::class, "percent"),
|
|
30
42
|
RESTING_HEART_RATE("restingHeartRate", RestingHeartRateRecord::class, "bpm"),
|
|
31
|
-
HEART_RATE_VARIABILITY("heartRateVariability", HeartRateVariabilityRmssdRecord::class, "millisecond")
|
|
43
|
+
HEART_RATE_VARIABILITY("heartRateVariability", HeartRateVariabilityRmssdRecord::class, "millisecond"),
|
|
44
|
+
BLOOD_PRESSURE("bloodPressure", BloodPressureRecord::class, "mmHg"),
|
|
45
|
+
BLOOD_GLUCOSE("bloodGlucose", BloodGlucoseRecord::class, "mg/dL"),
|
|
46
|
+
BODY_TEMPERATURE("bodyTemperature", BodyTemperatureRecord::class, "celsius"),
|
|
47
|
+
HEIGHT("height", HeightRecord::class, "centimeter"),
|
|
48
|
+
FLIGHTS_CLIMBED("flightsClimbed", FloorsClimbedRecord::class, "count"),
|
|
49
|
+
DISTANCE_CYCLING("distanceCycling", DistanceRecord::class, "meter"),
|
|
50
|
+
BODY_FAT("bodyFat", BodyFatRecord::class, "percent"),
|
|
51
|
+
BASAL_BODY_TEMPERATURE("basalBodyTemperature", BasalBodyTemperatureRecord::class, "celsius"),
|
|
52
|
+
BASAL_CALORIES("basalCalories", BasalMetabolicRateRecord::class, "kilocalorie"),
|
|
53
|
+
TOTAL_CALORIES("totalCalories", TotalCaloriesBurnedRecord::class, "kilocalorie"),
|
|
54
|
+
MINDFULNESS("mindfulness", MindfulnessSessionRecord::class, "minute");
|
|
32
55
|
|
|
33
56
|
val readPermission: String
|
|
34
57
|
get() = HealthPermission.getReadPermission(recordClass)
|
|
@@ -1,20 +1,32 @@
|
|
|
1
1
|
package app.capgo.plugin.health
|
|
2
2
|
|
|
3
3
|
import androidx.health.connect.client.HealthConnectClient
|
|
4
|
+
import androidx.health.connect.client.feature.ExperimentalMindfulnessSessionApi
|
|
4
5
|
import androidx.health.connect.client.permission.HealthPermission
|
|
5
6
|
import androidx.health.connect.client.request.AggregateRequest
|
|
6
7
|
import androidx.health.connect.client.records.ActiveCaloriesBurnedRecord
|
|
8
|
+
import androidx.health.connect.client.records.BasalBodyTemperatureRecord
|
|
9
|
+
import androidx.health.connect.client.records.BasalMetabolicRateRecord
|
|
10
|
+
import androidx.health.connect.client.records.BloodGlucoseRecord
|
|
11
|
+
import androidx.health.connect.client.records.BloodPressureRecord
|
|
12
|
+
import androidx.health.connect.client.records.BodyFatRecord
|
|
13
|
+
import androidx.health.connect.client.records.BodyTemperatureRecord
|
|
7
14
|
import androidx.health.connect.client.records.DistanceRecord
|
|
8
15
|
import androidx.health.connect.client.records.ExerciseSessionRecord
|
|
16
|
+
import androidx.health.connect.client.records.FloorsClimbedRecord
|
|
9
17
|
import androidx.health.connect.client.records.HeartRateRecord
|
|
10
18
|
import androidx.health.connect.client.records.HeartRateVariabilityRmssdRecord
|
|
19
|
+
import androidx.health.connect.client.records.HeightRecord
|
|
20
|
+
import androidx.health.connect.client.records.MindfulnessSessionRecord
|
|
11
21
|
import androidx.health.connect.client.records.OxygenSaturationRecord
|
|
12
22
|
import androidx.health.connect.client.records.Record
|
|
13
23
|
import androidx.health.connect.client.records.RespiratoryRateRecord
|
|
14
24
|
import androidx.health.connect.client.records.RestingHeartRateRecord
|
|
15
25
|
import androidx.health.connect.client.records.SleepSessionRecord
|
|
16
26
|
import androidx.health.connect.client.records.StepsRecord
|
|
27
|
+
import androidx.health.connect.client.records.TotalCaloriesBurnedRecord
|
|
17
28
|
import androidx.health.connect.client.records.WeightRecord
|
|
29
|
+
import androidx.health.connect.client.records.metadata.Metadata
|
|
18
30
|
import androidx.health.connect.client.request.ReadRecordsRequest
|
|
19
31
|
import androidx.health.connect.client.time.TimeRangeFilter
|
|
20
32
|
import androidx.health.connect.client.units.Energy
|
|
@@ -22,18 +34,18 @@ import androidx.health.connect.client.units.Length
|
|
|
22
34
|
import androidx.health.connect.client.units.Mass
|
|
23
35
|
import androidx.health.connect.client.units.Percentage
|
|
24
36
|
import androidx.health.connect.client.units.Power
|
|
25
|
-
import androidx.health.connect.client.records.metadata.Metadata
|
|
26
|
-
import java.time.Duration
|
|
27
37
|
import com.getcapacitor.JSArray
|
|
28
38
|
import com.getcapacitor.JSObject
|
|
39
|
+
import java.time.Duration
|
|
29
40
|
import java.time.Instant
|
|
30
41
|
import java.time.ZoneId
|
|
31
42
|
import java.time.ZoneOffset
|
|
32
43
|
import java.time.format.DateTimeFormatter
|
|
33
|
-
import kotlin.math.min
|
|
34
44
|
import kotlin.collections.buildSet
|
|
45
|
+
import kotlin.math.min
|
|
35
46
|
import kotlinx.coroutines.CancellationException
|
|
36
47
|
|
|
48
|
+
@OptIn(ExperimentalMindfulnessSessionApi::class)
|
|
37
49
|
class HealthManager {
|
|
38
50
|
|
|
39
51
|
private val formatter: DateTimeFormatter = DateTimeFormatter.ISO_INSTANT
|
|
@@ -211,6 +223,119 @@ class HealthManager {
|
|
|
211
223
|
)
|
|
212
224
|
samples.add(record.time to payload)
|
|
213
225
|
}
|
|
226
|
+
HealthDataType.BLOOD_PRESSURE -> readRecords(client, BloodPressureRecord::class, startTime, endTime, limit) { record ->
|
|
227
|
+
val payload = createSamplePayload(
|
|
228
|
+
dataType,
|
|
229
|
+
record.time,
|
|
230
|
+
record.time,
|
|
231
|
+
record.systolic.inMillimetersOfMercury,
|
|
232
|
+
record.metadata
|
|
233
|
+
)
|
|
234
|
+
payload.put("systolic", record.systolic.inMillimetersOfMercury)
|
|
235
|
+
payload.put("diastolic", record.diastolic.inMillimetersOfMercury)
|
|
236
|
+
samples.add(record.time to payload)
|
|
237
|
+
}
|
|
238
|
+
HealthDataType.BLOOD_GLUCOSE -> readRecords(client, BloodGlucoseRecord::class, startTime, endTime, limit) { record ->
|
|
239
|
+
val payload = createSamplePayload(
|
|
240
|
+
dataType,
|
|
241
|
+
record.time,
|
|
242
|
+
record.time,
|
|
243
|
+
record.level.inMilligramsPerDeciliter,
|
|
244
|
+
record.metadata
|
|
245
|
+
)
|
|
246
|
+
samples.add(record.time to payload)
|
|
247
|
+
}
|
|
248
|
+
HealthDataType.BODY_TEMPERATURE -> readRecords(client, BodyTemperatureRecord::class, startTime, endTime, limit) { record ->
|
|
249
|
+
val payload = createSamplePayload(
|
|
250
|
+
dataType,
|
|
251
|
+
record.time,
|
|
252
|
+
record.time,
|
|
253
|
+
record.temperature.inCelsius,
|
|
254
|
+
record.metadata
|
|
255
|
+
)
|
|
256
|
+
samples.add(record.time to payload)
|
|
257
|
+
}
|
|
258
|
+
HealthDataType.HEIGHT -> readRecords(client, HeightRecord::class, startTime, endTime, limit) { record ->
|
|
259
|
+
val payload = createSamplePayload(
|
|
260
|
+
dataType,
|
|
261
|
+
record.time,
|
|
262
|
+
record.time,
|
|
263
|
+
record.height.inMeters * 100.0, // Convert to centimeters
|
|
264
|
+
record.metadata
|
|
265
|
+
)
|
|
266
|
+
samples.add(record.time to payload)
|
|
267
|
+
}
|
|
268
|
+
HealthDataType.FLIGHTS_CLIMBED -> readRecords(client, FloorsClimbedRecord::class, startTime, endTime, limit) { record ->
|
|
269
|
+
val payload = createSamplePayload(
|
|
270
|
+
dataType,
|
|
271
|
+
record.startTime,
|
|
272
|
+
record.endTime,
|
|
273
|
+
record.floors,
|
|
274
|
+
record.metadata
|
|
275
|
+
)
|
|
276
|
+
samples.add(record.startTime to payload)
|
|
277
|
+
}
|
|
278
|
+
HealthDataType.DISTANCE_CYCLING -> readRecords(client, DistanceRecord::class, startTime, endTime, limit) { record ->
|
|
279
|
+
val payload = createSamplePayload(
|
|
280
|
+
dataType,
|
|
281
|
+
record.startTime,
|
|
282
|
+
record.endTime,
|
|
283
|
+
record.distance.inMeters,
|
|
284
|
+
record.metadata
|
|
285
|
+
)
|
|
286
|
+
samples.add(record.startTime to payload)
|
|
287
|
+
}
|
|
288
|
+
HealthDataType.BODY_FAT -> readRecords(client, BodyFatRecord::class, startTime, endTime, limit) { record ->
|
|
289
|
+
val payload = createSamplePayload(
|
|
290
|
+
dataType,
|
|
291
|
+
record.time,
|
|
292
|
+
record.time,
|
|
293
|
+
record.percentage.value,
|
|
294
|
+
record.metadata
|
|
295
|
+
)
|
|
296
|
+
samples.add(record.time to payload)
|
|
297
|
+
}
|
|
298
|
+
HealthDataType.BASAL_BODY_TEMPERATURE -> readRecords(client, BasalBodyTemperatureRecord::class, startTime, endTime, limit) { record ->
|
|
299
|
+
val payload = createSamplePayload(
|
|
300
|
+
dataType,
|
|
301
|
+
record.time,
|
|
302
|
+
record.time,
|
|
303
|
+
record.temperature.inCelsius,
|
|
304
|
+
record.metadata
|
|
305
|
+
)
|
|
306
|
+
samples.add(record.time to payload)
|
|
307
|
+
}
|
|
308
|
+
HealthDataType.BASAL_CALORIES -> readRecords(client, BasalMetabolicRateRecord::class, startTime, endTime, limit) { record ->
|
|
309
|
+
val payload = createSamplePayload(
|
|
310
|
+
dataType,
|
|
311
|
+
record.time,
|
|
312
|
+
record.time,
|
|
313
|
+
record.basalMetabolicRate.inKilocaloriesPerDay,
|
|
314
|
+
record.metadata
|
|
315
|
+
)
|
|
316
|
+
samples.add(record.time to payload)
|
|
317
|
+
}
|
|
318
|
+
HealthDataType.TOTAL_CALORIES -> readRecords(client, TotalCaloriesBurnedRecord::class, startTime, endTime, limit) { record ->
|
|
319
|
+
val payload = createSamplePayload(
|
|
320
|
+
dataType,
|
|
321
|
+
record.startTime,
|
|
322
|
+
record.endTime,
|
|
323
|
+
record.energy.inKilocalories,
|
|
324
|
+
record.metadata
|
|
325
|
+
)
|
|
326
|
+
samples.add(record.startTime to payload)
|
|
327
|
+
}
|
|
328
|
+
HealthDataType.MINDFULNESS -> readRecords(client, MindfulnessSessionRecord::class, startTime, endTime, limit) { record ->
|
|
329
|
+
val durationMinutes = Duration.between(record.startTime, record.endTime).toMinutes().toDouble()
|
|
330
|
+
val payload = createSamplePayload(
|
|
331
|
+
dataType,
|
|
332
|
+
record.startTime,
|
|
333
|
+
record.endTime,
|
|
334
|
+
durationMinutes,
|
|
335
|
+
record.metadata
|
|
336
|
+
)
|
|
337
|
+
samples.add(record.startTime to payload)
|
|
338
|
+
}
|
|
214
339
|
}
|
|
215
340
|
|
|
216
341
|
val sorted = samples.sortedBy { it.first }
|
|
@@ -257,8 +382,12 @@ class HealthManager {
|
|
|
257
382
|
value: Double,
|
|
258
383
|
startTime: Instant,
|
|
259
384
|
endTime: Instant,
|
|
260
|
-
metadata: Map<String, String
|
|
385
|
+
metadata: Map<String, String>?,
|
|
386
|
+
systolic: Double?,
|
|
387
|
+
diastolic: Double?
|
|
261
388
|
) {
|
|
389
|
+
val recordMetadata = Metadata.manualEntry()
|
|
390
|
+
|
|
262
391
|
when (dataType) {
|
|
263
392
|
HealthDataType.STEPS -> {
|
|
264
393
|
val record = StepsRecord(
|
|
@@ -266,7 +395,8 @@ class HealthManager {
|
|
|
266
395
|
startZoneOffset = zoneOffset(startTime),
|
|
267
396
|
endTime = endTime,
|
|
268
397
|
endZoneOffset = zoneOffset(endTime),
|
|
269
|
-
count = value.toLong().coerceAtLeast(0)
|
|
398
|
+
count = value.toLong().coerceAtLeast(0),
|
|
399
|
+
metadata = recordMetadata
|
|
270
400
|
)
|
|
271
401
|
client.insertRecords(listOf(record))
|
|
272
402
|
}
|
|
@@ -276,7 +406,8 @@ class HealthManager {
|
|
|
276
406
|
startZoneOffset = zoneOffset(startTime),
|
|
277
407
|
endTime = endTime,
|
|
278
408
|
endZoneOffset = zoneOffset(endTime),
|
|
279
|
-
distance = Length.meters(value)
|
|
409
|
+
distance = Length.meters(value),
|
|
410
|
+
metadata = recordMetadata
|
|
280
411
|
)
|
|
281
412
|
client.insertRecords(listOf(record))
|
|
282
413
|
}
|
|
@@ -286,7 +417,8 @@ class HealthManager {
|
|
|
286
417
|
startZoneOffset = zoneOffset(startTime),
|
|
287
418
|
endTime = endTime,
|
|
288
419
|
endZoneOffset = zoneOffset(endTime),
|
|
289
|
-
energy = Energy.kilocalories(value)
|
|
420
|
+
energy = Energy.kilocalories(value),
|
|
421
|
+
metadata = recordMetadata
|
|
290
422
|
)
|
|
291
423
|
client.insertRecords(listOf(record))
|
|
292
424
|
}
|
|
@@ -294,7 +426,8 @@ class HealthManager {
|
|
|
294
426
|
val record = WeightRecord(
|
|
295
427
|
time = startTime,
|
|
296
428
|
zoneOffset = zoneOffset(startTime),
|
|
297
|
-
weight = Mass.kilograms(value)
|
|
429
|
+
weight = Mass.kilograms(value),
|
|
430
|
+
metadata = recordMetadata
|
|
298
431
|
)
|
|
299
432
|
client.insertRecords(listOf(record))
|
|
300
433
|
}
|
|
@@ -305,7 +438,8 @@ class HealthManager {
|
|
|
305
438
|
startZoneOffset = zoneOffset(startTime),
|
|
306
439
|
endTime = endTime,
|
|
307
440
|
endZoneOffset = zoneOffset(endTime),
|
|
308
|
-
samples = samples
|
|
441
|
+
samples = samples,
|
|
442
|
+
metadata = recordMetadata
|
|
309
443
|
)
|
|
310
444
|
client.insertRecords(listOf(record))
|
|
311
445
|
}
|
|
@@ -314,7 +448,8 @@ class HealthManager {
|
|
|
314
448
|
startTime = startTime,
|
|
315
449
|
startZoneOffset = zoneOffset(startTime),
|
|
316
450
|
endTime = endTime,
|
|
317
|
-
endZoneOffset = zoneOffset(endTime)
|
|
451
|
+
endZoneOffset = zoneOffset(endTime),
|
|
452
|
+
metadata = recordMetadata
|
|
318
453
|
)
|
|
319
454
|
client.insertRecords(listOf(record))
|
|
320
455
|
}
|
|
@@ -322,7 +457,8 @@ class HealthManager {
|
|
|
322
457
|
val record = RespiratoryRateRecord(
|
|
323
458
|
time = startTime,
|
|
324
459
|
zoneOffset = zoneOffset(startTime),
|
|
325
|
-
rate = value
|
|
460
|
+
rate = value,
|
|
461
|
+
metadata = recordMetadata
|
|
326
462
|
)
|
|
327
463
|
client.insertRecords(listOf(record))
|
|
328
464
|
}
|
|
@@ -330,7 +466,8 @@ class HealthManager {
|
|
|
330
466
|
val record = OxygenSaturationRecord(
|
|
331
467
|
time = startTime,
|
|
332
468
|
zoneOffset = zoneOffset(startTime),
|
|
333
|
-
percentage = Percentage(value)
|
|
469
|
+
percentage = Percentage(value),
|
|
470
|
+
metadata = recordMetadata
|
|
334
471
|
)
|
|
335
472
|
client.insertRecords(listOf(record))
|
|
336
473
|
}
|
|
@@ -338,7 +475,8 @@ class HealthManager {
|
|
|
338
475
|
val record = RestingHeartRateRecord(
|
|
339
476
|
time = startTime,
|
|
340
477
|
zoneOffset = zoneOffset(startTime),
|
|
341
|
-
beatsPerMinute = value.toBpmLong()
|
|
478
|
+
beatsPerMinute = value.toBpmLong(),
|
|
479
|
+
metadata = recordMetadata
|
|
342
480
|
)
|
|
343
481
|
client.insertRecords(listOf(record))
|
|
344
482
|
}
|
|
@@ -346,7 +484,119 @@ class HealthManager {
|
|
|
346
484
|
val record = HeartRateVariabilityRmssdRecord(
|
|
347
485
|
time = startTime,
|
|
348
486
|
zoneOffset = zoneOffset(startTime),
|
|
349
|
-
heartRateVariabilityMillis = value
|
|
487
|
+
heartRateVariabilityMillis = value,
|
|
488
|
+
metadata = recordMetadata
|
|
489
|
+
)
|
|
490
|
+
client.insertRecords(listOf(record))
|
|
491
|
+
}
|
|
492
|
+
HealthDataType.BLOOD_PRESSURE -> {
|
|
493
|
+
if (systolic == null || diastolic == null) {
|
|
494
|
+
throw IllegalArgumentException("Blood pressure requires both systolic and diastolic values")
|
|
495
|
+
}
|
|
496
|
+
val record = BloodPressureRecord(
|
|
497
|
+
time = startTime,
|
|
498
|
+
zoneOffset = zoneOffset(startTime),
|
|
499
|
+
metadata = recordMetadata,
|
|
500
|
+
systolic = androidx.health.connect.client.units.Pressure.millimetersOfMercury(systolic),
|
|
501
|
+
diastolic = androidx.health.connect.client.units.Pressure.millimetersOfMercury(diastolic)
|
|
502
|
+
)
|
|
503
|
+
client.insertRecords(listOf(record))
|
|
504
|
+
}
|
|
505
|
+
HealthDataType.BLOOD_GLUCOSE -> {
|
|
506
|
+
val record = BloodGlucoseRecord(
|
|
507
|
+
time = startTime,
|
|
508
|
+
zoneOffset = zoneOffset(startTime),
|
|
509
|
+
metadata = recordMetadata,
|
|
510
|
+
level = androidx.health.connect.client.units.BloodGlucose.milligramsPerDeciliter(value)
|
|
511
|
+
)
|
|
512
|
+
client.insertRecords(listOf(record))
|
|
513
|
+
}
|
|
514
|
+
HealthDataType.BODY_TEMPERATURE -> {
|
|
515
|
+
val record = BodyTemperatureRecord(
|
|
516
|
+
time = startTime,
|
|
517
|
+
zoneOffset = zoneOffset(startTime),
|
|
518
|
+
metadata = recordMetadata,
|
|
519
|
+
temperature = androidx.health.connect.client.units.Temperature.celsius(value)
|
|
520
|
+
)
|
|
521
|
+
client.insertRecords(listOf(record))
|
|
522
|
+
}
|
|
523
|
+
HealthDataType.HEIGHT -> {
|
|
524
|
+
val record = HeightRecord(
|
|
525
|
+
time = startTime,
|
|
526
|
+
zoneOffset = zoneOffset(startTime),
|
|
527
|
+
height = Length.meters(value / 100.0), // Convert from centimeters to meters
|
|
528
|
+
metadata = recordMetadata
|
|
529
|
+
)
|
|
530
|
+
client.insertRecords(listOf(record))
|
|
531
|
+
}
|
|
532
|
+
HealthDataType.FLIGHTS_CLIMBED -> {
|
|
533
|
+
val record = FloorsClimbedRecord(
|
|
534
|
+
startTime = startTime,
|
|
535
|
+
startZoneOffset = zoneOffset(startTime),
|
|
536
|
+
endTime = endTime,
|
|
537
|
+
endZoneOffset = zoneOffset(endTime),
|
|
538
|
+
floors = value,
|
|
539
|
+
metadata = recordMetadata
|
|
540
|
+
)
|
|
541
|
+
client.insertRecords(listOf(record))
|
|
542
|
+
}
|
|
543
|
+
HealthDataType.DISTANCE_CYCLING -> {
|
|
544
|
+
val record = DistanceRecord(
|
|
545
|
+
startTime = startTime,
|
|
546
|
+
startZoneOffset = zoneOffset(startTime),
|
|
547
|
+
endTime = endTime,
|
|
548
|
+
endZoneOffset = zoneOffset(endTime),
|
|
549
|
+
distance = Length.meters(value),
|
|
550
|
+
metadata = recordMetadata
|
|
551
|
+
)
|
|
552
|
+
client.insertRecords(listOf(record))
|
|
553
|
+
}
|
|
554
|
+
HealthDataType.BODY_FAT -> {
|
|
555
|
+
val record = BodyFatRecord(
|
|
556
|
+
time = startTime,
|
|
557
|
+
zoneOffset = zoneOffset(startTime),
|
|
558
|
+
percentage = Percentage(value),
|
|
559
|
+
metadata = recordMetadata
|
|
560
|
+
)
|
|
561
|
+
client.insertRecords(listOf(record))
|
|
562
|
+
}
|
|
563
|
+
HealthDataType.BASAL_BODY_TEMPERATURE -> {
|
|
564
|
+
val record = BasalBodyTemperatureRecord(
|
|
565
|
+
time = startTime,
|
|
566
|
+
zoneOffset = zoneOffset(startTime),
|
|
567
|
+
metadata = recordMetadata,
|
|
568
|
+
temperature = androidx.health.connect.client.units.Temperature.celsius(value)
|
|
569
|
+
)
|
|
570
|
+
client.insertRecords(listOf(record))
|
|
571
|
+
}
|
|
572
|
+
HealthDataType.BASAL_CALORIES -> {
|
|
573
|
+
val record = BasalMetabolicRateRecord(
|
|
574
|
+
time = startTime,
|
|
575
|
+
zoneOffset = zoneOffset(startTime),
|
|
576
|
+
basalMetabolicRate = Power.kilocaloriesPerDay(value),
|
|
577
|
+
metadata = recordMetadata
|
|
578
|
+
)
|
|
579
|
+
client.insertRecords(listOf(record))
|
|
580
|
+
}
|
|
581
|
+
HealthDataType.TOTAL_CALORIES -> {
|
|
582
|
+
val record = TotalCaloriesBurnedRecord(
|
|
583
|
+
startTime = startTime,
|
|
584
|
+
startZoneOffset = zoneOffset(startTime),
|
|
585
|
+
endTime = endTime,
|
|
586
|
+
endZoneOffset = zoneOffset(endTime),
|
|
587
|
+
energy = Energy.kilocalories(value),
|
|
588
|
+
metadata = recordMetadata
|
|
589
|
+
)
|
|
590
|
+
client.insertRecords(listOf(record))
|
|
591
|
+
}
|
|
592
|
+
HealthDataType.MINDFULNESS -> {
|
|
593
|
+
val record = MindfulnessSessionRecord(
|
|
594
|
+
startTime = startTime,
|
|
595
|
+
startZoneOffset = zoneOffset(startTime),
|
|
596
|
+
endTime = endTime,
|
|
597
|
+
endZoneOffset = zoneOffset(endTime),
|
|
598
|
+
metadata = recordMetadata,
|
|
599
|
+
mindfulnessSessionType = MindfulnessSessionRecord.MINDFULNESS_SESSION_TYPE_UNKNOWN
|
|
350
600
|
)
|
|
351
601
|
client.insertRecords(listOf(record))
|
|
352
602
|
}
|
|
@@ -245,11 +245,14 @@ class HealthPlugin : Plugin() {
|
|
|
245
245
|
}
|
|
246
246
|
map.takeIf { it.isNotEmpty() }
|
|
247
247
|
}
|
|
248
|
+
|
|
249
|
+
val systolic = call.getDouble("systolic")
|
|
250
|
+
val diastolic = call.getDouble("diastolic")
|
|
248
251
|
|
|
249
252
|
pluginScope.launch {
|
|
250
253
|
val client = getClientOrReject(call) ?: return@launch
|
|
251
254
|
try {
|
|
252
|
-
manager.saveSample(client, dataType, value, startInstant, endInstant, metadata)
|
|
255
|
+
manager.saveSample(client, dataType, value, startInstant, endInstant, metadata, systolic, diastolic)
|
|
253
256
|
call.resolve()
|
|
254
257
|
} catch (e: Exception) {
|
|
255
258
|
call.reject(e.message ?: "Failed to save sample.", null, e)
|