Fixes for Apple Watch integration

haroon_dev
Haroon Amjad 15 hours ago
parent e867de7b71
commit 13f2898a8b

@ -13,80 +13,90 @@ enum Durations {
yearly("yearly");
final String value;
const Durations(this.value);
}
class HealthDataTransformation {
Map<String, List<DataPoint>> transformVitalsToDataPoints(VitalsWRTType vitals, String filterType, String selectedSection,) {
final Map<String, List<DataPoint>> dataPointMap = {};
Map<String, List<DataPoint>> transformVitalsToDataPoints(
VitalsWRTType vitals,
String filterType,
String selectedSection,
) {
final Map<String, List<DataPoint>> dataPointMap = {};
Map<String, List<Vitals>> data = vitals.getVitals();
// Group data based on the filter type
Map<String, List<Vitals>> groupedData = {};
// List<List<Vitals> > items = data.values.toList();
List<String> keys = data.keys.toList();
var count = 0;
List<Vitals> item = data[selectedSection] ?? [];
List<Vitals> item = data[selectedSection] ?? [];
// for(var item in items) {
List<DataPoint> dataPoints = [];
for (var vital in item) {
String key = "";
if (vital.value == "" || vital.timestamp == "") continue;
var parseDate = DateTime.parse(vital.timestamp);
var currentDate = normalizeToStartOfDay(DateTime.now());
if (filterType == Durations.daily.value) {
if(isBetweenInclusive(parseDate, currentDate, DateTime.now())) {
key = DateFormat('yyyy-MM-dd HH').format(DateTime.parse(vital.timestamp));
groupedData.putIfAbsent(key, () => []).add(vital);
}// Group by hour
} else if (filterType == Durations.weekly.value) {
if(isBetweenInclusive(parseDate, currentDate.subtract(Duration(days: 7)), DateTime.now())) {
key = DateFormat('yyyy-MM-dd').format(DateTime.parse(vital.timestamp));
groupedData.putIfAbsent(key, () => []).add(vital);
} // Group by day
} else if (filterType == Durations.monthly.value) {
if(isBetweenInclusive(parseDate, currentDate.subtract(Duration(days: 30)), DateTime.now())) {
print("the value for the monthly filter is ${vital.value} with the timestamp ${vital.timestamp} and the current date is $currentDate and the parse date is $parseDate");
key = DateFormat('yyyy-MM-dd').format(DateTime.parse(vital.timestamp));
groupedData.putIfAbsent(key, () => []).add(vital);
} // Group by day
} else if (filterType == Durations.halfYearly.value || filterType == Durations.yearly.value) {
if(isBetweenInclusive(parseDate, currentDate.subtract(Duration(days: filterType == Durations.halfYearly.value?180: 365)), DateTime.now())) {
key = DateFormat('yyyy-MM').format(DateTime.parse(vital.timestamp));
groupedData.putIfAbsent(key, () => []).add(vital);
} // Group by month
} else {
throw ArgumentError('Invalid filter type');
}
List<DataPoint> dataPoints = [];
for (var vital in item) {
String key = "";
if (vital.value == "" || vital.timestamp == "") continue;
DateTime parseDate;
try {
parseDate = DateTime.parse(vital.timestamp);
} catch (e) {
print('Error parsing timestamp "${vital.timestamp}": $e');
continue; // Skip this vital if timestamp is invalid
}
var currentDate = normalizeToStartOfDay(DateTime.now());
if (filterType == Durations.daily.value) {
if (isBetweenInclusive(parseDate, currentDate, DateTime.now())) {
key = DateFormat('yyyy-MM-dd HH').format(parseDate);
groupedData.putIfAbsent(key, () => []).add(vital);
} // Group by hour
} else if (filterType == Durations.weekly.value) {
if (isBetweenInclusive(parseDate, currentDate.subtract(Duration(days: 7)), DateTime.now())) {
key = DateFormat('yyyy-MM-dd').format(parseDate);
groupedData.putIfAbsent(key, () => []).add(vital);
} // Group by day
} else if (filterType == Durations.monthly.value) {
if (isBetweenInclusive(parseDate, currentDate.subtract(Duration(days: 30)), DateTime.now())) {
print("the value for the monthly filter is ${vital.value} with the timestamp ${vital.timestamp} and the current date is $currentDate and the parse date is $parseDate");
key = DateFormat('yyyy-MM-dd').format(parseDate);
groupedData.putIfAbsent(key, () => []).add(vital);
} // Group by day
} else if (filterType == Durations.halfYearly.value || filterType == Durations.yearly.value) {
if (isBetweenInclusive(parseDate, currentDate.subtract(Duration(days: filterType == Durations.halfYearly.value ? 180 : 365)), DateTime.now())) {
key = DateFormat('yyyy-MM').format(parseDate);
groupedData.putIfAbsent(key, () => []).add(vital);
} // Group by month
} else {
throw ArgumentError('Invalid filter type');
}
print("the size of groupData is ${groupedData.values.length}");
// Process grouped data
groupedData.forEach((key, values) {
double sum = values.fold(0, (acc, v) => acc + num.parse(v.value));
double mean = sum / values.length;
if(selectedSection == "bodyOxygen" || selectedSection == "bodyTemperature") {
mean = sum / values.length;
}else {
mean = sum;
}
double finalValue = mean;
print("the final value is $finalValue for the key $key with the original values ${values.map((v) => v.value).toList()} and uom is ${values.first.unitOfMeasure}");
dataPoints.add(DataPoint(
value: smartScale(finalValue),
label: key,
actualValue: finalValue.toStringAsFixed(2),
displayTime: key,
unitOfMeasurement:values.first.unitOfMeasure ,
time: DateTime.parse(values.first.timestamp),
));
});
dataPointMap[filterType] = dataPoints;
}
print("the size of groupData is ${groupedData.values.length}");
// Process grouped data
groupedData.forEach((key, values) {
double sum = values.fold(0, (acc, v) => acc + num.parse(v.value));
double mean = sum / values.length;
if (selectedSection == "bodyOxygen" || selectedSection == "bodyTemperature") {
mean = sum / values.length;
} else {
mean = sum;
}
double finalValue = mean;
print("the final value is $finalValue for the key $key with the original values ${values.map((v) => v.value).toList()} and uom is ${values.first.unitOfMeasure}");
dataPoints.add(DataPoint(
value: smartScale(finalValue),
label: key,
actualValue: finalValue.toStringAsFixed(2),
displayTime: key,
unitOfMeasurement: values.first.unitOfMeasure,
time: DateTime.parse(values.first.timestamp),
));
});
dataPointMap[filterType] = dataPoints;
// }
return dataPointMap;
}
@ -109,10 +119,10 @@ class HealthDataTransformation {
final random = Random();
// Smooth compression scaling
double baseScaled = number <20 ? number:100 * (number / (number + 100));
double baseScaled = number < 20 ? number : 100 * (number / (number + 100));
// Random factor between 0.9 and 1.1 (±10%)
double randomFactor = number <20 ? random.nextDouble() * 1.5: 0.9 + random.nextDouble() * 0.2;
double randomFactor = number < 20 ? random.nextDouble() * 1.5 : 0.9 + random.nextDouble() * 0.2;
double result = baseScaled * randomFactor;
@ -122,13 +132,12 @@ class HealthDataTransformation {
DateTime normalizeToStartOfDay(DateTime date) {
return DateTime(date.year, date.month, date.day);
}
bool isBetweenInclusive(
DateTime target,
DateTime start,
DateTime end,
) {
DateTime target,
DateTime start,
DateTime end,
) {
return !normalizeToStartOfDay(target).isBefore(start) && !normalizeToStartOfDay(target).isAfter(end);
}
}
}

@ -172,12 +172,12 @@ class HealthService {
}
}
void addWatchHelper(SmartWatchTypes watchType){
watchHelper = CreateWatchHelper.getWatchName(watchType) ;
void addWatchHelper(SmartWatchTypes watchType) {
watchHelper = CreateWatchHelper.getWatchName(watchType);
}
Future<Result<bool>> initDevice() async {
if(watchHelper == null){
if (watchHelper == null) {
return Result.error('No watch helper found');
}
return await watchHelper!.initDevice();
@ -197,14 +197,14 @@ class HealthService {
await watchHelper!.getDistance();
await watchHelper!.getBloodOxygen();
Result<dynamic> data = await watchHelper!.retrieveData();
if(data.isError) {
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}");
var response = jsonDecode(data.asValue?.value?.toString()?.trim().replaceAll("\n", "") ?? "");
VitalsWRTType vitals = VitalsWRTType.fromMap(response);
// log("the data is ${vitals}");
return vitals;
}catch(e){
} catch (e) {
print('Error getting heart rate: $e');
}
return null;

@ -9,10 +9,22 @@ class Vitals {
this.unitOfMeasure = "",
});
Map<String, dynamic> toJson() {
return {
'value': value,
'timestamp': timestamp,
'uom': unitOfMeasure,
};
}
factory Vitals.fromMap(Map<dynamic, dynamic> map) {
String timestamp = map['timestamp'] ?? map['timeStamp'] ?? "";
if (timestamp.isEmpty) {
timestamp = DateTime.now().toIso8601String();
}
return Vitals(
value: map['value'] ?? "",
timestamp: map['timeStamp'] ?? "",
timestamp: timestamp,
unitOfMeasure: map['uom'] ?? "",
);
}

@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:async/src/result/result.dart';
import 'package:flutter/material.dart';
@ -80,7 +81,10 @@ class HealthConnectHelper extends WatchHelper {
@override
Future<dynamic> retrieveData() async {
// String retData;
// retData = Result.value(getMappedData()) as String;
return Result.value(getMappedData());
// return retData;
}
@override
@ -151,9 +155,22 @@ class HealthConnectHelper extends WatchHelper {
for (var point in data) {
if (point.value is NumericHealthValue) {
final numericValue = (point.value as NumericHealthValue).numericValue;
String timestamp = DateTime.now().toIso8601String();
try {
if (point.dateFrom != null) {
String tempTimestamp = point.dateFrom.toIso8601String();
if (tempTimestamp.isNotEmpty) {
timestamp = tempTimestamp;
}
}
} catch (e) {
debugPrint('Error converting dateFrom to ISO8601 for $s: $e');
}
Vitals vitals = Vitals(
value: (point.value as NumericHealthValue).numericValue.toStringAsFixed(2),
timestamp: point.dateFrom.toString()
value: numericValue.toStringAsFixed(2),
timestamp: timestamp
);
mappedData[s]?.add(vitals);
}
@ -165,17 +182,41 @@ class HealthConnectHelper extends WatchHelper {
for (var point in data) {
if (point.value is NumericHealthValue) {
final numericValue = (point.value as NumericHealthValue).numericValue;
String timestamp = DateTime.now().toIso8601String();
try {
if (point.dateFrom != null) {
String tempTimestamp = point.dateFrom.toIso8601String();
if (tempTimestamp.isNotEmpty) {
timestamp = tempTimestamp;
}
}
} catch (e) {
debugPrint('Error converting dateFrom to ISO8601 for $s: $e');
}
point.value = NumericHealthValue(
numericValue: numericValue * 100,
);
Vitals vitals = Vitals(value: (point.value as NumericHealthValue).numericValue.toStringAsFixed(2), timestamp: point.dateFrom.toString());
Vitals vitals = Vitals(
value: (point.value as NumericHealthValue).numericValue.toStringAsFixed(2),
timestamp: timestamp
);
mappedData[s]?.add(vitals);
}
}
}
getMappedData() {
return " { \"heartRate\": ${mappedData["heartRate"] ?? []}, \"sleep\": ${mappedData["sleep"] ?? []}, \"steps\": ${mappedData["steps"] ?? []}, \"activity\": ${mappedData["activity"] ?? []}, \"bloodOxygen\": ${mappedData["bloodOxygen"] ?? []}, \"bodyTemperature\": ${mappedData["bodyTemperature"] ?? []}, \"distance\": ${mappedData["distance"] ?? []} }";
return jsonEncode({
"heartRate": (mappedData["heartRate"] ?? []).map((v) => v.toJson()).toList(),
"sleep": (mappedData["sleep"] ?? []).map((v) => v.toJson()).toList(),
"steps": (mappedData["steps"] ?? []).map((v) => v.toJson()).toList(),
"activity": (mappedData["activity"] ?? []).map((v) => v.toJson()).toList(),
"bloodOxygen": (mappedData["bloodOxygen"] ?? []).map((v) => v.toJson()).toList(),
"bodyTemperature": (mappedData["bodyTemperature"] ?? []).map((v) => v.toJson()).toList(),
"distance": (mappedData["distance"] ?? []).map((v) => v.toJson()).toList(),
});
}
getHeartData(DateTime startDate, DateTime endDate, HealthDataType types) async {

@ -240,7 +240,8 @@ class _ActivityDetailsState extends State<ActivityDetails> {
return Selector<HealthProvider, Map<String, List<DataPoint>>?>(
selector: (_, model) => model.selectedData,
builder: (_, data, __) {
if (context.read<HealthProvider>().selectedData.values.toList().first?.isEmpty == true) return SizedBox();
// if (context.read<HealthProvider>().selectedData.values.toList().first.isEmpty == true) return SizedBox();
if (context.read<HealthProvider>().selectedData.values.toList().isEmpty == true) return SizedBox();
return CustomBarChart(
dataPoints: context.read<HealthProvider>().selectedData.values.toList().first,

Loading…
Cancel
Save