Skip to content

Commit

Permalink
Update Health Record parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
RafhaanShah committed Apr 29, 2024
1 parent aeb371d commit 8ff36b0
Show file tree
Hide file tree
Showing 4 changed files with 20 additions and 78 deletions.
1 change: 1 addition & 0 deletions app/proguard-rules.pro
Original file line number Diff line number Diff line change
Expand Up @@ -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.**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
}
}
}

Expand Down Expand Up @@ -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
Expand All @@ -141,49 +147,26 @@ 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
}
}

// cast to each record type to process individually
@Suppress("UNCHECKED_CAST")
private fun extractDataRecordValue(record: Record): Map<String, Any> {
val result = mutableMapOf<String, Any>()

val properties = (record::class as KClass<Record>)
.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
}

Expand All @@ -194,23 +177,19 @@ class HealthConnectRepository(private val context: Context) {

for (sample in samples) {
if (sample == null) continue

val obj = mutableMapOf<String, Any>()
val properties = (sample::class as KClass<Any>)
.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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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()
) {
Expand Down

0 comments on commit 8ff36b0

Please sign in to comment.