From 8ff36b05e544ad6c75d9d64125cbb12617615a2d Mon Sep 17 00:00:00 2001 From: Rafhaan Shah Date: Mon, 29 Apr 2024 22:28:10 +0100 Subject: [PATCH] Update Health Record parsing --- app/proguard-rules.pro | 1 + .../HealthConnectDataTypes.kt | 38 ------------- .../HealthConnectRepository.kt | 57 ++++++------------- .../healthdata/HealthDataInput.kt | 2 +- 4 files changed, 20 insertions(+), 78 deletions(-) diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 7b99a48..9a85726 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -50,6 +50,7 @@ # health connect classes via reflection -keep class androidx.health.connect.client.records.** { *; } +-keep class androidx.health.connect.client.units.** { *; } -dontwarn android.** -dontwarn com.google.** diff --git a/app/src/main/java/com/rafapps/taskerhealthconnect/HealthConnectDataTypes.kt b/app/src/main/java/com/rafapps/taskerhealthconnect/HealthConnectDataTypes.kt index 47de19a..0424a2a 100644 --- a/app/src/main/java/com/rafapps/taskerhealthconnect/HealthConnectDataTypes.kt +++ b/app/src/main/java/com/rafapps/taskerhealthconnect/HealthConnectDataTypes.kt @@ -82,44 +82,6 @@ val aggregateRecordTypes = setOf( WheelchairPushesRecord::class, ) -// fields common to most record types -val commonFields = setOf("time", "startTime", "endTime") - -// fields that return a list of records -val listFields = setOf("samples") - -// fields used by list record values -val listValueFields = setOf( - "beatsPerMinute", - "power", - "rate", - "revolutionsPerMinute", - "speed", -) - -// fields that return a value for a record -val recordValueFields = setOf( - "appearance", - "count", - "diastolic", - "flow", - "heartRateVariabilityMillis", - "level", - "mass", - "mealType", - "measurementLocation", - "percentage", - "protectionUsed", - "rate", - "relationToMeal", - "result", - "sensation", - "specimenSource", - "systolic", - "temperature", - "vo2MillilitersPerMinuteKilogram", -) - // from androidx/health/connect/client/impl/platform/records/AggregationMappings val doubleAggregations = mapOf( "FloorsClimbedRecord_FLOORS_CLIMBED_TOTAL" to FloorsClimbedRecord.FLOORS_CLIMBED_TOTAL, diff --git a/app/src/main/java/com/rafapps/taskerhealthconnect/HealthConnectRepository.kt b/app/src/main/java/com/rafapps/taskerhealthconnect/HealthConnectRepository.kt index c364b9f..96d7a96 100644 --- a/app/src/main/java/com/rafapps/taskerhealthconnect/HealthConnectRepository.kt +++ b/app/src/main/java/com/rafapps/taskerhealthconnect/HealthConnectRepository.kt @@ -2,6 +2,7 @@ package com.rafapps.taskerhealthconnect import android.content.Context import android.content.Intent +import android.health.connect.datatypes.Metadata import android.net.Uri import android.util.Log import androidx.health.connect.client.HealthConnectClient @@ -24,6 +25,7 @@ import org.json.JSONArray import org.json.JSONObject import java.time.Duration import java.time.Instant +import java.time.ZoneOffset import kotlin.reflect.KClass import kotlin.reflect.KVisibility import kotlin.reflect.full.memberProperties @@ -83,8 +85,10 @@ class HealthConnectRepository(private val context: Context) { // check for each data type in each bucket aggregateMetricTypes.forEach { metricType -> aggregationResult.result[metricType.value]?.let { dataPoint -> - dataPointSize++ - data[metricType.key] = extractDataPointValue(dataPoint) + extractDataPointValue(dataPoint)?.let { + dataPointSize++ + data[metricType.key] = it + } } } @@ -124,13 +128,15 @@ class HealthConnectRepository(private val context: Context) { } // use the actual data type to get the correct value - private fun extractDataPointValue(dataPoint: Comparable<*>): Any { + private fun extractDataPointValue(dataPoint: Comparable<*>): Any? { return when (dataPoint) { + is Boolean -> dataPoint is BloodGlucose -> dataPoint.inMilligramsPerDeciliter is Double -> dataPoint is Duration -> dataPoint.toMinutes() is Energy -> dataPoint.inCalories is Length -> dataPoint.inMeters + is Instant -> dataPoint.toString() is Int -> dataPoint is Long -> dataPoint is Mass -> dataPoint.inKilograms @@ -141,11 +147,7 @@ class HealthConnectRepository(private val context: Context) { is Temperature -> dataPoint.inCelsius is Velocity -> dataPoint.inMetersPerSecond is Volume -> dataPoint.inLiters - else -> { - Log.w(TAG, "unexpected data type:" + - " ${dataPoint::class.qualifiedName} -> $dataPoint") - dataPoint.toString() - } + else -> null } } @@ -153,37 +155,18 @@ class HealthConnectRepository(private val context: Context) { @Suppress("UNCHECKED_CAST") private fun extractDataRecordValue(record: Record): Map { val result = mutableMapOf() - val properties = (record::class as KClass) .memberProperties.filter { it.visibility == KVisibility.PUBLIC } for (prop in properties) { - val key = prop.name val value = prop.get(record) ?: continue + val key = prop.name - if (commonFields.contains(key)) { - result[key] = value - continue - } - - if (recordValueFields.contains(key)) { - if (value is Comparable<*>) { - result[key] = extractDataPointValue(value) - } else { - Log.w(TAG, "value was not a Comparable: ${value::class.qualifiedName}") - } - continue - } - - if (listFields.contains(key)) { - if (value is List<*>) { - result[key] = extractListData(value) - } else { - Log.w(TAG, "value was not a List: ${value::class.qualifiedName}") - } + when(value) { + is Comparable<*> -> extractDataPointValue(value)?.let { result[key] = it } + is List<*> -> result[key] = extractListData(value) } } - return result } @@ -194,23 +177,19 @@ class HealthConnectRepository(private val context: Context) { for (sample in samples) { if (sample == null) continue - + val obj = mutableMapOf() val properties = (sample::class as KClass) .memberProperties.filter { it.visibility == KVisibility.PUBLIC } for (prop in properties) { - val key = prop.name val value = prop.get(sample) ?: continue - if (!listValueFields.contains(key)) continue - + val key = prop.name if (value is Comparable<*>) { - result.add(extractDataPointValue(value)) - } else { - Log.w(TAG, "value was not a Comparable: ${value::class.qualifiedName}") + extractDataPointValue(value)?.let { obj[key] = it } } } + result.add(obj) } - return result } } diff --git a/app/src/main/java/com/rafapps/taskerhealthconnect/healthdata/HealthDataInput.kt b/app/src/main/java/com/rafapps/taskerhealthconnect/healthdata/HealthDataInput.kt index 5c8b4de..6e1e9c9 100644 --- a/app/src/main/java/com/rafapps/taskerhealthconnect/healthdata/HealthDataInput.kt +++ b/app/src/main/java/com/rafapps/taskerhealthconnect/healthdata/HealthDataInput.kt @@ -19,7 +19,7 @@ class HealthDataInput @JvmOverloads constructor( labelResId = R.string.from_time_milliseconds, descriptionResId = R.string.from_time_milliseconds_description ) var fromTimeMillis: String = Instant.now() - .minusSeconds(60 * 60) + .minusSeconds(60 * 60 * 24) .toEpochMilli() .toString() ) {