diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts
index 523a9f8e..6d1321f6 100644
--- a/android/app/build.gradle.kts
+++ b/android/app/build.gradle.kts
@@ -169,6 +169,8 @@ dependencies {
implementation(files("libs/Penguin.aar"))
implementation(files("libs/PenguinRenderer.aar"))
api(files("libs/samsung-health-data-api.aar"))
+ implementation("com.huawei.hms:health:6.11.0.300")
+ implementation("com.huawei.hms:hmscoreinstaller:6.6.0.300")
implementation("com.github.kittinunf.fuel:fuel:2.3.1")
implementation("com.github.kittinunf.fuel:fuel-android:2.3.1")
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 8c1388b5..6ad5e539 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -134,6 +134,9 @@
android:showOnLockScreen="true"
android:usesCleartextTraffic="true"
tools:replace="android:label">
+
= result.authAccount.authorizedScopes
+// if(authorizedScopes.isNotEmpty()) {
+// huaweiWatch?.getHealthAppAuthorization()
+// }
+// }
+// } else {
+// Log.w("MainActivty", "authorization fail, errorCode:" + result.getErrorCode())
+// }
+// }
+// }
}
diff --git a/android/app/src/main/kotlin/com/ejada/hmg/samsung_watch/SamsungWatch.kt b/android/app/src/main/kotlin/com/ejada/hmg/watch/samsung_watch/SamsungWatch.kt
similarity index 79%
rename from android/app/src/main/kotlin/com/ejada/hmg/samsung_watch/SamsungWatch.kt
rename to android/app/src/main/kotlin/com/ejada/hmg/watch/samsung_watch/SamsungWatch.kt
index 09efa394..09aafff2 100644
--- a/android/app/src/main/kotlin/com/ejada/hmg/samsung_watch/SamsungWatch.kt
+++ b/android/app/src/main/kotlin/com/ejada/hmg/watch/samsung_watch/SamsungWatch.kt
@@ -1,4 +1,4 @@
-package com.ejada.hmg.samsung_watch
+package com.ejada.hmg.watch.huawei.samsung_watch
@@ -8,7 +8,7 @@ import android.util.Log
import androidx.annotation.RequiresApi
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodCall
-import com.ejada.hmg.samsung_watch.model.Vitals
+import com.ejada.hmg.watch.huawei.samsung_watch.model.Vitals
import com.samsung.android.sdk.health.data.HealthDataService
import com.samsung.android.sdk.health.data.HealthDataStore
import com.samsung.android.sdk.health.data.data.AggregatedData
@@ -74,8 +74,8 @@ class SamsungWatch(
Permission.of(DataTypes.BLOOD_OXYGEN, AccessType.READ),
Permission.of(DataTypes.ACTIVITY_SUMMARY, AccessType.READ),
Permission.of(DataTypes.SLEEP, AccessType.READ),
- Permission.of(DataTypes.BLOOD_OXYGEN, AccessType.READ),
Permission.of(DataTypes.BODY_TEMPERATURE, AccessType.READ),
+ Permission.of(DataTypes.EXERCISE, AccessType.READ),
// Permission.of(DataTypes.SKIN_TEMPERATURE, AccessType.READ),
// Permission.of(DataTypes.NUTRITION, AccessType.READ),
@@ -160,7 +160,8 @@ class SamsungWatch(
val dateTime = LocalDateTime.now().with(LocalTime.MIDNIGHT).minusDays(365)
val localTimeFilter = LocalTimeFilter.of(dateTime, LocalDateTime.now())
val localTimeGroup = LocalTimeGroup.of(LocalTimeGroupUnit.HOURLY, 1)
- val readRequest = DataType.ActivitySummaryType.TOTAL_CALORIES_BURNED.requestBuilder
+ val readRequest = DataType.ActivitySummaryType.TOTAL_ACTIVE_CALORIES_BURNED
+ .requestBuilder
.setLocalTimeFilterWithGroup(localTimeFilter, localTimeGroup)
.setOrdering(Ordering.DESC)
.build()
@@ -172,6 +173,24 @@ class SamsungWatch(
print("the data is ${vitals}")
result.success("Data is obtained")
}
+
+// val readRequest = DataTypes.EXERCISE.readDataRequestBuilder
+// .setLocalTimeFilter(localTimeFilter)
+// .build()
+//
+// scope.launch{
+// try {
+// val readResult = dataStore.readData(readRequest)
+// val dataPoints = readResult.dataList
+//
+// processActivity(dataPoints)
+//
+//
+// } catch (e: Exception) {
+// e.printStackTrace()
+// }
+// result.success("Data is obtained")
+// }
}
"bloodOxygen"->{
@@ -209,6 +228,24 @@ class SamsungWatch(
}
}
+ "distance"->{
+ val dateTime = LocalDateTime.now().with(LocalTime.MIDNIGHT).minusDays(365)
+ val localTimeFilter = LocalTimeFilter.of(dateTime, LocalDateTime.now())
+ val localTimeGroup = LocalTimeGroup.of(LocalTimeGroupUnit.HOURLY, 1)
+ val readRequest = DataType.ActivitySummaryType.TOTAL_DISTANCE.requestBuilder
+ .setLocalTimeFilterWithGroup(localTimeFilter, localTimeGroup)
+ .setOrdering(Ordering.DESC)
+ .build()
+
+ scope.launch {
+ val activityResult = dataStore.aggregateData(readRequest).dataList
+ processDistance(activityResult)
+ Log.d("TAG"," the data is ${vitals}")
+ print("the data is ${vitals}")
+ result.success("Data is obtained")
+ }
+ }
+
"retrieveData"->{
if(vitals.isEmpty()){
result.error("NoDataFound", "No Data was obtained", null)
@@ -221,7 +258,8 @@ class SamsungWatch(
"sleep": ${vitals["sleep"]},
"activity": ${vitals["activity"]},
"bloodOxygen": ${vitals["bloodOxygen"]},
- "bodyTemperature": ${vitals["bodyTemperature"]}
+ "bodyTemperature": ${vitals["bodyTemperature"]},
+ "distance": ${vitals["distance"]}
}
""".trimIndent())
}
@@ -239,6 +277,18 @@ class SamsungWatch(
}
}
+ private fun CoroutineScope.processDistance(activityResult: List>) {
+ vitals["distance"] = mutableListOf()
+ activityResult.forEach { stepData ->
+ val vitalData = Vitals().apply {
+
+ value = stepData.value.toString()
+ timeStamp = stepData.startTime.toString()
+ }
+ (vitals["distance"] as MutableList).add(vitalData)
+ }
+ }
+
private fun CoroutineScope.processBodyTemperature( bodyTemperatureList :List) {
vitals["bodyTemperature"] = mutableListOf()
bodyTemperatureList.forEach { stepData ->
@@ -262,16 +312,43 @@ class SamsungWatch(
}
+// private fun CoroutineScope.processActivity(activityResult: List>) {
+//
+// vitals["activity"] = mutableListOf()
+// activityResult.forEach { stepData ->
+// val vitalData = Vitals().apply {
+//
+// value = stepData.value.toString()
+// timeStamp = stepData.startTime.toString()
+// }
+// (vitals["activity"] as MutableList).add(vitalData)
+// }
+// }
private fun CoroutineScope.processActivity(activityResult: List>) {
vitals["activity"] = mutableListOf()
activityResult.forEach { stepData ->
val vitalData = Vitals().apply {
+
value = stepData.value.toString()
- timeStamp = stepData.endTime.toString()
+ timeStamp = stepData.startTime.toString()
}
(vitals["activity"] as MutableList).add(vitalData)
}
+
+// dataPoints.forEach { dataPoint ->
+// val sessions = dataPoint.getValue(DataType.ExerciseType.SESSIONS)
+//
+// sessions?.forEach { session ->
+//
+// val exerciseSessionCalories = session.calories
+// val vitalData = Vitals().apply {
+// value = exerciseSessionCalories.toString()
+// timeStamp = session.startTime.toString()
+// }
+// (vitals["activity"] as MutableList).add(vitalData)
+// }
+// }
}
private fun CoroutineScope.processStepsCount(result: DataResponse>) {
@@ -294,7 +371,7 @@ class SamsungWatch(
(vitals["sleep"] as MutableList).add(
Vitals().apply {
timeStamp = it.startTime.toString()
- value = (it.getValue(DataType.SleepType.DURATION)?.toString().toString())
+ value = (it.getValue(DataType.SleepType.DURATION)?.toMillis().toString())
}
)
}
diff --git a/android/app/src/main/kotlin/com/ejada/hmg/samsung_watch/model/Vitals.kt b/android/app/src/main/kotlin/com/ejada/hmg/watch/samsung_watch/model/Vitals.kt
similarity index 81%
rename from android/app/src/main/kotlin/com/ejada/hmg/samsung_watch/model/Vitals.kt
rename to android/app/src/main/kotlin/com/ejada/hmg/watch/samsung_watch/model/Vitals.kt
index 6ab7d37c..3b5cdfe4 100644
--- a/android/app/src/main/kotlin/com/ejada/hmg/samsung_watch/model/Vitals.kt
+++ b/android/app/src/main/kotlin/com/ejada/hmg/watch/samsung_watch/model/Vitals.kt
@@ -1,4 +1,4 @@
-package com.ejada.hmg.samsung_watch.model
+package com.ejada.hmg.watch.huawei.samsung_watch.model
data class Vitals(
var value : String = "",
diff --git a/lib/core/utils/date_util.dart b/lib/core/utils/date_util.dart
index 2b134735..f15214a1 100644
--- a/lib/core/utils/date_util.dart
+++ b/lib/core/utils/date_util.dart
@@ -1,7 +1,10 @@
import 'package:device_calendar/device_calendar.dart';
import 'package:flutter/material.dart';
+import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:intl/intl.dart';
+import '../app_state.dart' show AppState;
+
class DateUtil {
/// convert String To Date function
/// [date] String we want to convert
@@ -198,6 +201,10 @@ class DateUtil {
}
}
+ static getMonthDayAsOfLang(int month){
+ return getIt.get().isArabic()?getMonthArabic(month):getMonth(month);
+ }
+
/// get month by
/// [month] convert month number in to month name in Arabic
static getMonthArabic(int month) {
@@ -268,6 +275,10 @@ class DateUtil {
return date ?? DateTime.now();
}
+ static getWeekDayAsOfLang(int weekDay){
+ return getIt.get().isArabic()?getWeekDayArabic(weekDay):getWeekDayEnglish(weekDay);
+ }
+
/// get month by
/// [weekDay] convert week day in int to week day name
static getWeekDay(int weekDay) {
@@ -580,6 +591,14 @@ class DateUtil {
return weekDayName; // Return as-is if not recognized
}
}
+
+ static String millisToHourMin(int milliseconds) {
+ int totalMinutes = (milliseconds / 60000).floor(); // convert ms → min
+ int hours = totalMinutes ~/ 60; // integer division
+ int minutes = totalMinutes % 60; // remaining minutes
+
+ return '${hours} hr ${minutes} min';
+ }
}
extension OnlyDate on DateTime {
diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart
index 6a009428..2deefa78 100644
--- a/lib/extensions/string_extensions.dart
+++ b/lib/extensions/string_extensions.dart
@@ -18,6 +18,7 @@ extension CapExtension on String {
String get needTranslation => this;
String get capitalizeFirstofEach => trim().isNotEmpty ? trim().toLowerCase().split(" ").map((str) => str.inCaps).join(" ") : "";
+
}
extension EmailValidator on String {
diff --git a/lib/features/smartwatch_health_data/HealthDataTransformation.dart b/lib/features/smartwatch_health_data/HealthDataTransformation.dart
new file mode 100644
index 00000000..d389135d
--- /dev/null
+++ b/lib/features/smartwatch_health_data/HealthDataTransformation.dart
@@ -0,0 +1,128 @@
+import 'dart:math';
+
+import 'package:hmg_patient_app_new/core/common_models/data_points.dart';
+import 'package:intl/intl.dart';
+
+import 'model/Vitals.dart';
+
+enum Durations {
+ daily("daily"),
+ weekly("weekly"),
+ monthly("weekly"),
+ halfYearly("halfYearly"),
+ yearly("yearly");
+
+ final String value;
+ const Durations(this.value);
+}
+
+class HealthDataTransformation {
+ Map> transformVitalsToDataPoints(VitalsWRTType vitals, String filterType, String selectedSection,) {
+ final Map> dataPointMap = {};
+ Map> data = vitals.getVitals();
+ // Group data based on the filter type
+ Map> groupedData = {};
+ // List > items = data.values.toList();
+ List keys = data.keys.toList();
+ var count = 0;
+ List item = data[selectedSection] ?? [];
+ // for(var item in items) {
+ List 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())) {
+ 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');
+ }
+ }
+ 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;
+
+ double finalValue = filterType == 'weekly' ? mean : sum;
+ 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;
+ }
+
+ double smartScale(double number) {
+ // if (number <= 0) return 0;
+ // final _random = Random();
+ // double ratio = number / 100;
+ //
+ // double scalingFactor = ratio > 1 ? 100 / number : 100;
+ //
+ // double result = (number / 100) * scalingFactor;
+ // print("the ratio is ${ratio.toInt()+1}");
+ // double max = (100+_random.nextInt(ratio.toInt()+10)).toDouble();
+ //
+ // return result.clamp(0, max);
+
+ if (number <= 0) return 0;
+
+ final random = Random();
+
+ // Smooth compression scaling
+ 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 result = baseScaled * randomFactor;
+
+ return result.clamp(0, 100);
+ }
+
+ DateTime normalizeToStartOfDay(DateTime date) {
+ return DateTime(date.year, date.month, date.day);
+ }
+ bool isBetweenInclusive(
+ DateTime target,
+ DateTime start,
+ DateTime end,
+ ) {
+ return !normalizeToStartOfDay(target).isBefore(start) && !normalizeToStartOfDay(target).isAfter(end);
+ }
+
+
+}
\ No newline at end of file
diff --git a/lib/features/smartwatch_health_data/health_provider.dart b/lib/features/smartwatch_health_data/health_provider.dart
index 7a175d8e..905c8d5a 100644
--- a/lib/features/smartwatch_health_data/health_provider.dart
+++ b/lib/features/smartwatch_health_data/health_provider.dart
@@ -1,13 +1,18 @@
import 'package:flutter/foundation.dart';
import 'package:health/health.dart';
import 'package:hmg_patient_app_new/core/common_models/smart_watch.dart';
+import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/core/utils/loading_utils.dart';
import 'package:hmg_patient_app_new/features/smartwatch_health_data/health_service.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
+import '../../core/common_models/data_points.dart';
import '../../core/dependencies.dart';
+import '../../presentation/smartwatches/activity_detail.dart' show ActivityDetails;
import '../../presentation/smartwatches/smart_watch_activity.dart' show SmartWatchActivity;
import '../../services/navigation_service.dart' show NavigationService;
+import 'HealthDataTransformation.dart';
+import 'model/Vitals.dart';
class HealthProvider with ChangeNotifier {
final HealthService _healthService = HealthService();
@@ -19,6 +24,19 @@ class HealthProvider with ChangeNotifier {
SmartWatchTypes? selectedWatchType ;
String selectedWatchURL = 'assets/images/png/smartwatches/apple-watch-5.jpg';
+ HealthDataTransformation healthDataTransformation = HealthDataTransformation();
+
+ String selectedSection = "";
+ Map> daily = {};
+ Map> weekly = {};
+ Map> monthly = {};
+ Map> halgyearly = {};
+ Map> yearly = {};
+ Map> selectedData = {};
+ Durations selectedDuration = Durations.daily;
+ VitalsWRTType? vitals;
+ double? averageValue;
+ String? averageValueString;
setSelectedWatchType(SmartWatchTypes type, String imageURL) {
selectedWatchType = type;
@@ -109,18 +127,167 @@ class HealthProvider with ChangeNotifier {
if (result.isError) {
error = 'Error initializing device: ${result.asError}';
} else {
- getVitals();
+ LoaderBottomSheet.hideLoader();
+
+ LoaderBottomSheet.showLoader();
+ await getVitals();
+ LoaderBottomSheet.hideLoader();
+ await Future.delayed(Duration(seconds: 5));
getIt.get().pushPage(page: SmartWatchActivity());
print('Device initialized successfully');
}
notifyListeners();
}
- void getVitals() async{
- isLoading = true;
- notifyListeners();
+ Future getVitals() async {
+
final result = await _healthService.getVitals();
- isLoading = false;
+ vitals = result;
+ LoaderBottomSheet.hideLoader();
+
notifyListeners();
}
+
+ mapValuesForFilters(
+ Durations filter,
+ String selectedSection,
+ ) {
+ if (vitals == null) return {};
+
+ switch (filter) {
+ case Durations.daily:
+ if (daily.isNotEmpty) {
+ selectedData = daily;
+ break;
+ }
+ selectedData = daily = healthDataTransformation.transformVitalsToDataPoints(vitals!, Durations.daily.value, selectedSection);
+ break;
+ case Durations.weekly:
+ if (weekly.isNotEmpty) {
+ selectedData = daily;
+ break;
+ }
+ selectedData = weekly = healthDataTransformation.transformVitalsToDataPoints(vitals!, Durations.weekly.value, selectedSection);
+ break;
+ case Durations.monthly:
+ if (monthly.isNotEmpty) {
+ selectedData = monthly;
+ break;
+ }
+ selectedData = monthly = healthDataTransformation.transformVitalsToDataPoints(vitals!, Durations.monthly.value, selectedSection);
+ break;
+ case Durations.halfYearly:
+ if (halgyearly.isNotEmpty) {
+ selectedData = halgyearly;
+ break;
+ }
+ selectedData = halgyearly = healthDataTransformation.transformVitalsToDataPoints(vitals!, Durations.halfYearly.value, selectedSection);
+ break;
+ case Durations.yearly:
+ if (yearly.isNotEmpty) {
+ selectedData = yearly;
+ break;
+ }
+ selectedData = yearly = healthDataTransformation.transformVitalsToDataPoints(vitals!, Durations.yearly.value, selectedSection);
+ break;
+ default:
+ {}
+ ;
+ }
+ notifyListeners();
+ }
+
+ void navigateToDetails(String value, {required String sectionName, required String uom}) {
+ getIt.get().pushPage(page: ActivityDetails(selectedActivity: value, sectionName:sectionName, uom: uom,));
+ }
+
+ void saveSelectedSection(String value) {
+ // if(selectedSection == value) return;
+ selectedSection = value;
+ }
+
+ void deleteDataIfSectionIsDifferent(String value) {
+ // if(selectedSection == value){
+ // return;
+ // }
+ daily.clear();
+ weekly.clear();
+ halgyearly.clear();
+ monthly.clear();
+ yearly.clear();
+ selectedSection = "";
+ selectedSection = "";
+ averageValue = null;
+ averageValueString = null;
+ selectedDuration = Durations.daily;
+ }
+
+ void fetchData() {
+ // if(selectedSection == value) return;
+ mapValuesForFilters(selectedDuration, selectedSection);
+ getAverageForData();
+ transformValueIfSleepIsSelected();
+ }
+
+ void setDurations(Durations duration) {
+ selectedDuration = duration;
+ }
+
+ void getAverageForData() {
+ if (selectedData.isEmpty) {
+ averageValue = 0.0;
+ notifyListeners();
+ return;
+ }
+ double total = 0;
+ int count = 0;
+ selectedData.forEach((key, dataPoints) {
+ for (var dataPoint in dataPoints) {
+ total += num.parse(dataPoint.actualValue).toInt();
+ count++;
+ }
+ });
+ averageValue = count > 0 ? total / count : null;
+ notifyListeners();
+ }
+
+ void transformValueIfSleepIsSelected() {
+ if (selectedSection != "sleep") return;
+ if (averageValue == null) {
+ averageValueString = null;
+ return;
+ }
+ averageValueString = DateUtil.millisToHourMin(averageValue?.toInt() ?? 0);
+ averageValue = null;
+ notifyListeners();
+ }
+
+ String firstNonEmptyValue(List dataPoints) {
+ try {
+ return dataPoints.firstWhere((dp) => dp.value != null && dp.value!.trim().isNotEmpty).value;
+ } catch (e) {
+ return "0"; // no non-empty value found
+ }
+ }
+
+ String sumOfNonEmptyData(List list) {
+ final now = DateTime.now().toLocal();
+ final today = DateTime(now.year, now.month, now.day);
+
+ var data = double.parse(list
+ .where((dp) {
+ final localDate = DateTime.parse(dp.timestamp);
+ final normalized = DateTime(localDate.year, localDate.month, localDate.day);
+
+ return normalized.isAtSameMomentAs(today);
+ })
+ .fold("0", (sum, dp) => (double.parse(sum) + double.parse(dp.value)).toString())
+ .toString());
+ var formattedString = data.toStringAsFixed(2);
+
+ if (formattedString.endsWith('.00')) {
+ return formattedString.substring(0, formattedString.length - 3);
+ }
+ return formattedString;
+ }
}
diff --git a/lib/features/smartwatch_health_data/health_service.dart b/lib/features/smartwatch_health_data/health_service.dart
index 4d532a5a..1cc42014 100644
--- a/lib/features/smartwatch_health_data/health_service.dart
+++ b/lib/features/smartwatch_health_data/health_service.dart
@@ -183,31 +183,31 @@ class HealthService {
return await watchHelper!.initDevice();
}
- FutureOr getVitals() async {
+ Future getVitals() async {
if (watchHelper == null) {
print('No watch helper found');
- return;
+ return null;
}
try {
+ await watchHelper!.getActivity();
await watchHelper!.getHeartRate();
await watchHelper!.getSleep();
await watchHelper!.getSteps();
await watchHelper!.getActivity();
await watchHelper!.getBodyTemperature();
+ await watchHelper!.getDistance();
await watchHelper!.getBloodOxygen();
Result data = await watchHelper!.retrieveData();
-
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}");
+ return vitals;
}catch(e){
print('Error getting heart rate: $e');
}
-
+ return null;
}
}
diff --git a/lib/features/smartwatch_health_data/model/Vitals.dart b/lib/features/smartwatch_health_data/model/Vitals.dart
index bbcf1035..cea1df4f 100644
--- a/lib/features/smartwatch_health_data/model/Vitals.dart
+++ b/lib/features/smartwatch_health_data/model/Vitals.dart
@@ -1,16 +1,19 @@
class Vitals {
- final String value;
+ String value;
final String timestamp;
+ final String unitOfMeasure;
Vitals({
required this.value,
required this.timestamp,
+ this.unitOfMeasure = "",
});
factory Vitals.fromMap(Map map) {
return Vitals(
value: map['value'] ?? "",
- timestamp: map['timestamp'] ?? "",
+ timestamp: map['timeStamp'] ?? "",
+ unitOfMeasure: map['uom'] ?? "",
);
}
}
@@ -19,11 +22,19 @@ class VitalsWRTType {
final List heartRate;
final List sleep;
final List step;
+ final List distance;
final List activity;
final List bodyOxygen;
final List bodyTemperature;
+ double maxHeartRate = double.negativeInfinity;
+ double maxSleep = double.negativeInfinity;
+ double maxStep= double.negativeInfinity;
+ double maxActivity = double.negativeInfinity;
+ double maxBloodOxygen = double.negativeInfinity;
+ double maxBodyTemperature = double.negativeInfinity;
- VitalsWRTType({required this.bodyOxygen, required this.bodyTemperature, required this.heartRate, required this.sleep, required this.step, required this.activity});
+
+ VitalsWRTType({required this.distance, required this.bodyOxygen, required this.bodyTemperature, required this.heartRate, required this.sleep, required this.step, required this.activity});
factory VitalsWRTType.fromMap(Map map) {
List activity = [];
@@ -31,26 +42,63 @@ class VitalsWRTType {
List sleeps = [];
List heartRate = [];
List bodyOxygen = [];
+ List distance = [];
List bodyTemperature = [];
map["activity"].forEach((element) {
- activity.add(Vitals.fromMap(element));
+ element["uom"] = "Kcal";
+ var data = Vitals.fromMap(element);
+ // data.value = (double.parse(data.value)/1000).toStringAsFixed(2);
+ activity.add(data);
});
map["steps"].forEach((element) {
+ element["uom"] = "";
+
steps.add(Vitals.fromMap(element));
});
map["sleep"].forEach((element) {
+ element["uom"] = "hr";
sleeps.add(Vitals.fromMap(element));
});
map["heartRate"].forEach((element) {
+ element["uom"] = "bpm";
+
heartRate.add(Vitals.fromMap(element));
});
map["bloodOxygen"].forEach((element) {
+ element["uom"] = "";
+
+ bodyOxygen.add(Vitals.fromMap(element));
+ });
+
+ map["distance"].forEach((element) {
+ element["uom"] = "m";
+
bodyOxygen.add(Vitals.fromMap(element));
});
map["bodyTemperature"].forEach((element) {
+ element["uom"] = "C";
bodyTemperature.add(Vitals.fromMap(element));
});
- return VitalsWRTType(bodyTemperature: bodyTemperature, bodyOxygen: bodyOxygen, heartRate: heartRate, sleep: sleeps, step: steps, activity: activity);
+ map["distance"].forEach((element) {
+ element["uom"] = "km";
+ var data = Vitals.fromMap(element);
+ data.value = (double.parse(data.value)/1000).toStringAsFixed(2);
+ distance.add(data);
+ });
+
+ return VitalsWRTType(bodyTemperature: bodyTemperature, bodyOxygen: bodyOxygen, heartRate: heartRate, sleep: sleeps, step: steps, activity: activity, distance: distance);
+ }
+
+ Map> getVitals() {
+ return {
+ "heartRate": heartRate ,
+ "sleep": sleep,
+ "steps": step,
+ "activity": activity,
+ "bodyOxygen": bodyOxygen,
+ "bodyTemperature": bodyTemperature,
+ "distance": distance,
+ };
}
}
diff --git a/lib/features/smartwatch_health_data/platform_channel/samsung_platform_channel.dart b/lib/features/smartwatch_health_data/platform_channel/samsung_platform_channel.dart
index 1e3ef6df..a36cda39 100644
--- a/lib/features/smartwatch_health_data/platform_channel/samsung_platform_channel.dart
+++ b/lib/features/smartwatch_health_data/platform_channel/samsung_platform_channel.dart
@@ -1,4 +1,6 @@
+import 'dart:async';
+
import 'package:async/async.dart';
import 'package:flutter/services.dart';
class SamsungPlatformChannel {
@@ -77,4 +79,12 @@ class SamsungPlatformChannel {
return Result.error(e);
}
}
+
+ Future> getDistance() async {
+ try{
+ return Result.value(await _channel.invokeMethod('distance'));
+ }catch(e){
+ return Result.error(e);
+ }
+ }
}
\ No newline at end of file
diff --git a/lib/features/smartwatch_health_data/watch_connectors/create_watch_helper.dart b/lib/features/smartwatch_health_data/watch_connectors/create_watch_helper.dart
index 9bc6d84d..72f97b69 100644
--- a/lib/features/smartwatch_health_data/watch_connectors/create_watch_helper.dart
+++ b/lib/features/smartwatch_health_data/watch_connectors/create_watch_helper.dart
@@ -1,4 +1,5 @@
import 'package:hmg_patient_app_new/core/common_models/smart_watch.dart';
+import 'package:hmg_patient_app_new/features/smartwatch_health_data/watch_connectors/huawei_watch_connecter.dart';
import 'package:hmg_patient_app_new/features/smartwatch_health_data/watch_connectors/samsung_health.dart';
import 'package:hmg_patient_app_new/features/smartwatch_health_data/watch_connectors/watch_helper.dart';
@@ -7,6 +8,8 @@ class CreateWatchHelper {
switch(watchType){
case SmartWatchTypes.samsung:
return SamsungHealth();
+ case SmartWatchTypes.huawei:
+ return HuaweiHealthDataConnector();
default:
return SamsungHealth();
}
diff --git a/lib/features/smartwatch_health_data/watch_connectors/huawei_watch_connecter.dart b/lib/features/smartwatch_health_data/watch_connectors/huawei_watch_connecter.dart
new file mode 100644
index 00000000..a8300e2d
--- /dev/null
+++ b/lib/features/smartwatch_health_data/watch_connectors/huawei_watch_connecter.dart
@@ -0,0 +1,86 @@
+import 'dart:async';
+
+import 'package:async/src/result/result.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/services.dart';
+import 'package:hmg_patient_app_new/features/smartwatch_health_data/watch_connectors/watch_helper.dart';
+import 'package:huawei_health/huawei_health.dart';
+
+class HuaweiHealthDataConnector extends WatchHelper{
+ final MethodChannel _channel = MethodChannel('huawei_watch');
+ @override
+ Future> initDevice() async{
+ try{
+ await _channel.invokeMethod('init');
+
+ }catch(e){
+
+ }
+ // List of scopes to ask for authorization.
+ // Note: These scopes should also be authorized on the Huawei Developer Console.
+ List scopes = [
+ Scope.HEALTHKIT_STEP_READ, Scope.HEALTHKIT_OXYGEN_SATURATION_READ, // View and store height and weight data in Health Service Kit.
+ Scope.HEALTHKIT_HEARTRATE_READ, Scope.HEALTHKIT_SLEEP_READ,
+ Scope.HEALTHKIT_BODYTEMPERATURE_READ, Scope.HEALTHKIT_CALORIES_READ
+ ];
+ try {
+ bool? result = await SettingController.getHealthAppAuthorization();
+ debugPrint(
+ 'Granted Scopes for result == is $result}',
+ );
+ return Result.value(true);
+ } catch (e) {
+ debugPrint('Error on authorization, Error:${e.toString()}');
+ return Result.error(false);
+ }
+ }
+
+ @override
+ Future getActivity() async {
+ DataType dataTypeResult = await SettingController.readDataType(
+ DataType.DT_CONTINUOUS_STEPS_DELTA.name
+ );
+
+
+ }
+
+ @override
+ Future getBloodOxygen() {
+ throw UnimplementedError();
+
+ }
+
+ @override
+ Future getBodyTemperature() {
+
+ throw UnimplementedError();
+ }
+
+ @override
+ FutureOr getHeartRate() {
+ throw UnimplementedError();
+ }
+
+ @override
+ FutureOr getSleep() {
+ throw UnimplementedError();
+ }
+
+ @override
+ FutureOr getSteps() {
+ throw UnimplementedError();
+ }
+
+
+ @override
+ Future retrieveData() {
+ throw UnimplementedError();
+ }
+
+ @override
+ FutureOr getDistance() {
+ // TODO: implement getDistance
+ throw UnimplementedError();
+ }
+
+}
\ No newline at end of file
diff --git a/lib/features/smartwatch_health_data/watch_connectors/samsung_health.dart b/lib/features/smartwatch_health_data/watch_connectors/samsung_health.dart
index c2e8fd3e..d3cbcfc9 100644
--- a/lib/features/smartwatch_health_data/watch_connectors/samsung_health.dart
+++ b/lib/features/smartwatch_health_data/watch_connectors/samsung_health.dart
@@ -84,6 +84,15 @@ class SamsungHealth extends WatchHelper {
}
}
+ @override
+ FutureOr getDistance() async{
+ try {
+ return await platformChannel.getDistance();
+ }catch(e){
+ print('Error getting heart rate: $e');
+ }
+ }
+
}
\ No newline at end of file
diff --git a/lib/features/smartwatch_health_data/watch_connectors/watch_helper.dart b/lib/features/smartwatch_health_data/watch_connectors/watch_helper.dart
index fe62fdc0..a09064d8 100644
--- a/lib/features/smartwatch_health_data/watch_connectors/watch_helper.dart
+++ b/lib/features/smartwatch_health_data/watch_connectors/watch_helper.dart
@@ -5,6 +5,7 @@ abstract class WatchHelper {
FutureOr getHeartRate();
FutureOr getSleep();
FutureOr getSteps();
+ FutureOr getDistance();
Future getActivity();
Future retrieveData();
Future getBodyTemperature();
diff --git a/lib/presentation/hmg_services/services_page.dart b/lib/presentation/hmg_services/services_page.dart
index 9aaf1591..00c78721 100644
--- a/lib/presentation/hmg_services/services_page.dart
+++ b/lib/presentation/hmg_services/services_page.dart
@@ -303,11 +303,11 @@ class ServicesPage extends StatelessWidget {
true,
route: null,
onTap: () async {
- if (getIt.get().isAuthenticated) {
+ // if (getIt.get().isAuthenticated) {
getIt.get().pushPageRoute(AppRoutes.smartWatches);
- } else {
- await getIt.get().onLoginPressed();
- }
+ // } else {
+ // await getIt.get().onLoginPressed();
+ // }
},
// route: AppRoutes.huaweiHealthExample,
),
diff --git a/lib/presentation/smartwatches/activity_detail.dart b/lib/presentation/smartwatches/activity_detail.dart
index cc006a61..21d168ad 100644
--- a/lib/presentation/smartwatches/activity_detail.dart
+++ b/lib/presentation/smartwatches/activity_detail.dart
@@ -6,17 +6,25 @@ import 'package:hmg_patient_app_new/core/app_export.dart';
import 'package:hmg_patient_app_new/core/common_models/data_points.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
+import 'package:hmg_patient_app_new/features/smartwatch_health_data/health_provider.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart';
import 'package:hmg_patient_app_new/widgets/graph/CustomBarGraph.dart';
import 'package:intl/intl.dart' show DateFormat;
+import 'package:provider/provider.dart';
+import 'package:hmg_patient_app_new/features/smartwatch_health_data/HealthDataTransformation.dart' as durations;
+import 'package:dartz/dartz.dart' show Tuple2;
+
+import '../../core/utils/date_util.dart';
class ActivityDetails extends StatefulWidget {
final String selectedActivity;
+ final String sectionName;
+ final String uom;
- const ActivityDetails({super.key, required this.selectedActivity});
+ const ActivityDetails({super.key, required this.selectedActivity, required this.sectionName, required this.uom});
@override
State createState() => _ActivityDetailsState();
@@ -25,6 +33,11 @@ class ActivityDetails extends StatefulWidget {
class _ActivityDetailsState extends State {
int index = 0;
+ @override
+ void initState() {
+ super.initState();
+ }
+
@override
Widget build(BuildContext context) {
return Scaffold(
@@ -65,10 +78,24 @@ class _ActivityDetailsState extends State {
],
shouldTabExpanded: true,
onTabChange: (index) {
- //todo handle the selection from viewmodel
- setState(() {
- this.index = index;
- });
+ switch (index) {
+ case 0:
+ context.read().setDurations(durations.Durations.daily);
+ break;
+ case 1:
+ context.read().setDurations(durations.Durations.weekly);
+ break;
+ case 2:
+ context.read().setDurations(durations.Durations.monthly);
+ break;
+ case 3:
+ context.read().setDurations(durations.Durations.halfYearly);
+ break;
+ case 4:
+ context.read().setDurations(durations.Durations.yearly);
+ break;
+ }
+ context.read().fetchData();
},
),
),
@@ -81,167 +108,235 @@ class _ActivityDetailsState extends State {
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 12.h, hasShadow: true),
child: Column(
spacing: 8.h,
+ crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
- children: [widget.selectedActivity.toText32(weight: FontWeight.w600, color: AppColors.textColor), "Average".toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor)],
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ widget.sectionName.capitalizeFirstofEach.toText32(weight: FontWeight.w600, color: AppColors.textColor),
+ "Average".toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor)
+ ],
),
- Row(
- children: ["3232".toText24(color: AppColors.textGreenColor, fontWeight: FontWeight.w600), "(20-21)".toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500)],
- )
+ Selector>(
+ selector: (_, model) => Tuple2(model.averageValue, model.averageValueString),
+ builder: (_, data, __) {
+ var averageAsDouble = data.value1;
+ var averageAsString = data.value2;
+ return Row(
+ crossAxisAlignment: CrossAxisAlignment.baseline,
+ textBaseline: TextBaseline.alphabetic,
+ children: [
+ (averageAsDouble?.toStringAsFixed(2) ?? averageAsString ?? "N/A").toText24(color: AppColors.textGreenColor, fontWeight: FontWeight.w600),
+ Visibility(
+ visible: averageAsDouble != null || averageAsString != null,
+ child: widget.uom.toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500)
+ )
+ ],
+ );
+ })
],
).paddingSymmetrical(16.w, 16.h));
}
Widget activityGraph() {
- final _random = Random();
-
- int randomBP() => 100 + _random.nextInt(51); // 100–150
- final List data6Months = List.generate(6, (index) {
- final date = DateTime.now().subtract(Duration(days: 30 * (5 - index)));
-
- final value = randomBP();
-
- return DataPoint(
- value: value.toDouble(),
- label: value.toString(),
- actualValue: value.toString(),
- displayTime: DateFormat('MMM').format(date),
- unitOfMeasurement: 'mmHg',
- time: date,
- );
- });
- final List data12Months = List.generate(12, (index) {
- final date = DateTime.now().subtract(Duration(days: 30 * (11 - index)));
-
- final value = randomBP();
-
- return DataPoint(
- value: value.toDouble(),
- label: value.toString(),
- actualValue: value.toString(),
- displayTime: DateFormat('MMM').format(date),
- unitOfMeasurement: 'mmHg',
- time: date,
- );
- });
-
- List data =[];
- if(index == 0){
- data = data6Months;
- } else if(index == 1){
- data = data12Months;
- } else
- data = [
- DataPoint(
- value: 128,
- label: "128",
- actualValue: '128',
- displayTime: 'Sun',
- unitOfMeasurement: 'mmHg',
- time: DateTime.now().subtract(const Duration(days: 6)),
- ),
- DataPoint(
- value: 135,
- label: "135",
- actualValue: '135',
- displayTime: 'Mon',
- unitOfMeasurement: 'mmHg',
- time: DateTime.now().subtract(const Duration(days: 5)),
- ),
- DataPoint(
- value: 122,
- label: "122",
- actualValue: '122',
- displayTime: 'Tue',
- unitOfMeasurement: 'mmHg',
- time: DateTime.now().subtract(const Duration(days: 4)),
- ),
- DataPoint(
- value: 140,
- label: "140",
- actualValue: '140',
- displayTime: 'Wed',
- unitOfMeasurement: 'mmHg',
- time: DateTime.now().subtract(const Duration(days: 3)),
- ),
- DataPoint(
- value: 118,
- label: "118",
- actualValue: '118',
- displayTime: 'Thu',
- unitOfMeasurement: 'mmHg',
- time: DateTime.now().subtract(const Duration(days: 2)),
- ),
- DataPoint(
- value: 125,
- label: "125",
- actualValue: '125',
- displayTime: 'Fri',
- unitOfMeasurement: 'mmHg',
- time: DateTime.now().subtract(const Duration(days: 1)),
- ),
- DataPoint(
- value: 130,
- label: "130",
- actualValue: '130',
- displayTime: 'Sat',
- unitOfMeasurement: 'mmHg',
- time: DateTime.now(),
- ),
- ];
-
- return CustomBarChart(
- dataPoints: data,
- height: 300.h,
- maxY: 150,
- barColor: AppColors.bgGreenColor,
- barWidth: 26.w,
- barRadius: BorderRadius.circular(8),
- bottomLabelColor: Colors.black,
- bottomLabelSize: 12,
- leftLabelInterval: .1,
- leftLabelReservedSize: 40,
- // Left axis label formatter (Y-axis)
- leftLabelFormatter: (value) {
- var labelValue = double.tryParse(value.toStringAsFixed(0));
-
- if (labelValue == null) return SizedBox.shrink();
- if (labelValue == 0 || labelValue == 150 / 2 || labelValue == 150) {
- return Text(
- labelValue.toStringAsFixed(0),
- style: const TextStyle(
- color: Colors.black26,
- fontSize: 11,
- ),
- );
- }
+ // final _random = Random();
+ //
+ // int randomBP() => 100 + _random.nextInt(51); // 100–150
+ // final List data6Months = List.generate(6, (index) {
+ // final date = DateTime.now().subtract(Duration(days: 30 * (5 - index)));
+ //
+ // final value = randomBP();
+ //
+ // return DataPoint(
+ // value: value.toDouble(),
+ // label: value.toString(),
+ // actualValue: value.toString(),
+ // displayTime: DateFormat('MMM').format(date),
+ // unitOfMeasurement: 'mmHg',
+ // time: date,
+ // );
+ // });
+ // final List data12Months = List.generate(12, (index) {
+ // final date = DateTime.now().subtract(Duration(days: 30 * (11 - index)));
+ //
+ // final value = randomBP();
+ //
+ // return DataPoint(
+ // value: value.toDouble(),
+ // label: value.toString(),
+ // actualValue: value.toString(),
+ // displayTime: DateFormat('MMM').format(date),
+ // unitOfMeasurement: 'mmHg',
+ // time: date,
+ // );
+ // });
+ //
+ // List data =[];
+ // if(index == 0){
+ // data = data6Months;
+ // } else if(index == 1){
+ // data = data12Months;
+ // } else
+ // data = [
+ // DataPoint(
+ // value: 128,
+ // label: "128",
+ // actualValue: '128',
+ // displayTime: 'Sun',
+ // unitOfMeasurement: 'mmHg',
+ // time: DateTime.now().subtract(const Duration(days: 6)),
+ // ),
+ // DataPoint(
+ // value: 135,
+ // label: "135",
+ // actualValue: '135',
+ // displayTime: 'Mon',
+ // unitOfMeasurement: 'mmHg',
+ // time: DateTime.now().subtract(const Duration(days: 5)),
+ // ),
+ // DataPoint(
+ // value: 122,
+ // label: "122",
+ // actualValue: '122',
+ // displayTime: 'Tue',
+ // unitOfMeasurement: 'mmHg',
+ // time: DateTime.now().subtract(const Duration(days: 4)),
+ // ),
+ // DataPoint(
+ // value: 140,
+ // label: "140",
+ // actualValue: '140',
+ // displayTime: 'Wed',
+ // unitOfMeasurement: 'mmHg',
+ // time: DateTime.now().subtract(const Duration(days: 3)),
+ // ),
+ // DataPoint(
+ // value: 118,
+ // label: "118",
+ // actualValue: '118',
+ // displayTime: 'Thu',
+ // unitOfMeasurement: 'mmHg',
+ // time: DateTime.now().subtract(const Duration(days: 2)),
+ // ),
+ // DataPoint(
+ // value: 125,
+ // label: "125",
+ // actualValue: '125',
+ // displayTime: 'Fri',
+ // unitOfMeasurement: 'mmHg',
+ // time: DateTime.now().subtract(const Duration(days: 1)),
+ // ),
+ // DataPoint(
+ // value: 130,
+ // label: "130",
+ // actualValue: '130',
+ // displayTime: 'Sat',
+ // unitOfMeasurement: 'mmHg',
+ // time: DateTime.now(),
+ // ),23
+ // ];
+ return Selector>?>(
+ selector: (_, model) => model.selectedData,
+ builder: (_, data, __) {
+ if (context.read().selectedData.values.toList().first?.isEmpty == true) return SizedBox();
- return SizedBox.shrink();
- },
+ return CustomBarChart(
+ dataPoints: context.read().selectedData.values.toList().first,
+ height: 300.h,
+ maxY: 150,
+ barColor: AppColors.bgGreenColor,
+ barWidth: context.read().selectedData.values.toList().first.length < 10 ? 26.w : 20.w,
+ barRadius: BorderRadius.circular(8),
+ bottomLabelColor: Colors.black,
+ bottomLabelSize: 12,
+ leftLabelInterval: .1,
+ leftLabelReservedSize: 20,
+ // Left axis label formatter (Y-axis)
+ leftLabelFormatter: (value) {
+ var labelValue = double.tryParse(value.toStringAsFixed(0));
+
+ if (labelValue == null) return SizedBox.shrink();
+ // if (labelValue == 0 || labelValue == 150 / 2 || labelValue == 150) {
+ // return Text(
+ // labelValue.toStringAsFixed(0),
+ // style: const TextStyle(
+ // color: Colors.black26,
+ // fontSize: 11,
+ // ),
+ // );
+ // }
- // Bottom axis label formatter (X-axis - Days)
+ return SizedBox.shrink();
+ },
+
+ /// for the handling of the sleep time
+ getTooltipItem: (widget.selectedActivity == "sleep")
+ ? (data) {
+ return BarTooltipItem(
+ '${DateUtil. millisToHourMin(num.parse(data.actualValue).toInt())}\n${DateFormat('dd MMM, yyyy').format(data.time)}',
+ TextStyle(
+ color: Colors.black,
+ fontSize: 12.f,
+ fontWeight: FontWeight.w500,
+ ),
+ );
+ }
+ : null,
+
+ // Bottom axis label formatter (X-axis - Days)
bottomLabelFormatter: (value, dataPoints) {
final index = value.toInt();
- if (index >= 0 && index < dataPoints.length) {
- return Text(
- dataPoints[index].displayTime,
- style: const TextStyle(
- color: Colors.grey,
- fontSize: 11,
- ),
- );
- }
- return const Text('');
- },
- verticalInterval: 1/data.length,
- getDrawingVerticalLine: (value) {
- return FlLine(
- color: AppColors.greyColor,
- strokeWidth: 1,
+ print("value is $value");
+ print("the index is $index");
+ print("the dataPoints.length is ${dataPoints.length}");
+
+ var bottomText = "";
+ var date = dataPoints[index].time;
+ print("the time is ${date}");
+ switch (context.read().selectedDuration) {
+ case durations.Durations.daily:
+ bottomText = getHour(date).toString();
+ break;
+ case durations.Durations.weekly:
+ bottomText = getDayName(date)[0];
+ break;
+ case durations.Durations.monthly:
+ case durations.Durations.halfYearly:
+ case durations.Durations.yearly:
+ bottomText = getMonthName(date)[0];
+ }
+
+ return Text(
+ bottomText,
+ style: const TextStyle(
+ color: Colors.grey,
+ fontSize: 11,
+ ),
+ );
+ return const Text('');
+ },
+ verticalInterval: 1 / context.read().selectedData.values.toList().first.length,
+ getDrawingVerticalLine: (value) {
+ return FlLine(
+ color: AppColors.greyColor,
+ strokeWidth: 1,
);
},
- showGridLines: true,
- );
+ showGridLines: true);
+ });
+ }
+
+ //todo remove these from here
+ String getDayName(DateTime date) {
+ return DateUtil.getWeekDayAsOfLang(date.weekday);
+ }
+
+ String getHour(DateTime date) {
+ return date.hour.toString().padLeft(2, '0').toString();
+ }
+
+ static String getMonthName(DateTime date) {
+ return DateUtil.getMonthDayAsOfLang(date.month);
}
}
diff --git a/lib/presentation/smartwatches/smart_watch_activity.dart b/lib/presentation/smartwatches/smart_watch_activity.dart
index 531139b8..cffb802d 100644
--- a/lib/presentation/smartwatches/smart_watch_activity.dart
+++ b/lib/presentation/smartwatches/smart_watch_activity.dart
@@ -5,10 +5,15 @@ import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
+import 'package:hmg_patient_app_new/features/smartwatch_health_data/health_provider.dart';
import 'package:hmg_patient_app_new/presentation/smartwatches/activity_detail.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
+import 'package:provider/provider.dart';
+import 'package:hmg_patient_app_new/features/smartwatch_health_data/HealthDataTransformation.dart' as durations;
+
+import '../../core/utils/date_util.dart' show DateUtil;
class SmartWatchActivity extends StatelessWidget {
@override
@@ -22,64 +27,164 @@ class SmartWatchActivity extends StatelessWidget {
children: [
resultItem(
leadingIcon: AppAssets.watchActivity,
- title: "Activity".needTranslation,
+ title: "Activity Calories".needTranslation,
description: "Activity rings give you a quick visual reference of how active you are each day. ".needTranslation,
trailingIcon: AppAssets.watchActivityTrailing,
- result: "21",
+ result: context.read().sumOfNonEmptyData(context.read().vitals?.activity??[]),
unitsOfMeasure: "Cal"
).onPress((){
- getIt.get().pushPage(page: ActivityDetails( selectedActivity: "Steps"));
+ // Map> getVitals() {
+ // return {
+ // "heartRate": heartRate ,
+ // "sleep": sleep,
+ // "steps": step,
+ // "activity": activity,
+ // "bodyOxygen": bodyOxygen,
+ // "bodyTemperature": bodyTemperature,
+ // };
+ // }
+ context.read().setDurations(durations.Durations.daily);
+
+ context.read().deleteDataIfSectionIsDifferent("activity");
+ context.read().saveSelectedSection("activity");
+ context.read().fetchData();
+ context.read().navigateToDetails("activity", sectionName:"Activity Calories", uom: "cal");
+
}),
resultItem(
leadingIcon: AppAssets.watchSteps,
title: "Steps".needTranslation,
description: "Step count is the number of steps you take throughout the day.".needTranslation,
trailingIcon: AppAssets.watchStepsTrailing,
- result: "21",
+ result: context.read().sumOfNonEmptyData(context.read().vitals?.step??[]),
unitsOfMeasure: "Steps"
- ),
+ ).onPress((){
+ // Map> getVitals() {
+ // return {
+ // "heartRate": heartRate ,
+ // "sleep": sleep,
+ // "steps": step,
+ // "activity": activity,
+ // "bodyOxygen": bodyOxygen,
+ // "bodyTemperature": bodyTemperature,
+ // };
+ // }
+ context.read().setDurations(durations.Durations.daily);
+
+ context.read().deleteDataIfSectionIsDifferent("steps");
+ context.read().saveSelectedSection("steps");
+ context.read().fetchData();
+ context.read().navigateToDetails("steps", sectionName: "Steps", uom: "Steps");
+
+ }),
resultItem(
leadingIcon: AppAssets.watchSteps,
- title: "Walking + Running Distance".needTranslation,
- description: "This will show you the total distance you covered in a day".needTranslation,
+ title: "Distance Covered".needTranslation,
+ description: "Step count is the distance you take throughout the day.".needTranslation,
trailingIcon: AppAssets.watchStepsTrailing,
- result: "21",
- unitsOfMeasure: "km"
- ),
+ result: context.read().sumOfNonEmptyData(context.read().vitals?.distance??[]),
+ unitsOfMeasure: "Km"
+ ).onPress((){
+ // Map> getVitals() {
+ // return {
+ // "heartRate": heartRate ,
+ // "sleep": sleep,
+ // "steps": step,
+ // "activity": activity,
+ // "bodyOxygen": bodyOxygen,
+ // "bodyTemperature": bodyTemperature,
+ // };
+ // }
+ context.read().setDurations(durations.Durations.daily);
+
+ context.read().deleteDataIfSectionIsDifferent("distance");
+ context.read().saveSelectedSection("distance");
+ context.read().fetchData();
+ context.read().navigateToDetails("distance", sectionName: "Distance Covered", uom: "km");
+
+ }),
+
resultItem(
leadingIcon: AppAssets.watchSleep,
title: "Sleep Score".needTranslation,
description: "This will keep track of how much hours you sleep in a day".needTranslation,
trailingIcon: AppAssets.watchSleepTrailing,
- result: "21",
+ result: DateUtil.millisToHourMin(int.parse(context.read().firstNonEmptyValue(context.read().vitals?.sleep??[]))).split(" ")[0],
unitsOfMeasure: "hr",
- resultSecondValue: "2",
+ resultSecondValue: DateUtil.millisToHourMin(int.parse(context.read().firstNonEmptyValue(context.read().vitals?.sleep??[]))).split(" ")[2],
unitOfSecondMeasure: "min"
- ),
- resultItem(
- leadingIcon: AppAssets.watchBmi,
- title: "Body Mass Index".needTranslation,
- description: "This will calculate your weight and height ratio to analyze the ".needTranslation,
- trailingIcon: AppAssets.watchBmiTrailing,
- result: "21",
- unitsOfMeasure: "BMI"
- ),
+ ).onPress((){
+ // Map> getVitals() {
+ // return {
+ // "heartRate": heartRate ,
+ // "sleep": sleep,
+ // "steps": step,
+ // "activity": activity,
+ // "bodyOxygen": bodyOxygen,
+ // "bodyTemperature": bodyTemperature,
+ // };
+ // }
+ context.read().setDurations(durations.Durations.daily);
+
+ context.read().deleteDataIfSectionIsDifferent("sleep");
+ context.read().saveSelectedSection("sleep");
+ context.read().fetchData();
+ context.read().navigateToDetails("sleep", sectionName:"Sleep Score",uom:"");
+
+ }),
+
resultItem(
leadingIcon: AppAssets.watchWeight,
- title: "Weight".needTranslation,
- description: "This will calculate your weight to keep track and update history".needTranslation,
+ title: "Blood Oxygen".needTranslation,
+ description: "This will calculate your Blood Oxygen to keep track and update history".needTranslation,
trailingIcon: AppAssets.watchWeightTrailing,
- result: "21",
- unitsOfMeasure: "Kg"
- ),
+ result: context.read().firstNonEmptyValue(context.read().vitals?.bodyOxygen??[], ),
+ unitsOfMeasure: "%"
+ ).onPress((){
+ // Map> getVitals() {
+ // return {
+ // "heartRate": heartRate ,
+ // "sleep": sleep,
+ // "steps": step,
+ // "activity": activity,
+ // "bodyOxygen": bodyOxygen,
+ // "bodyTemperature": bodyTemperature,
+ // };
+ // }
+ context.read().setDurations(durations.Durations.daily);
+
+ context.read().deleteDataIfSectionIsDifferent("bodyOxygen");
+ context.read().saveSelectedSection("bodyOxygen");
+ context.read().fetchData();
+ context.read().navigateToDetails("bodyOxygen", uom: "%", sectionName:"Blood Oxygen" );
+
+ }),
resultItem(
leadingIcon: AppAssets.watchWeight,
- title: "Height".needTranslation,
- description: "This will calculate your height to keep track and update history".needTranslation,
+ title: "Body temperature".needTranslation,
+ description: "This will calculate your Body temprerature to keep track and update history".needTranslation,
trailingIcon: AppAssets.watchWeightTrailing,
- result: "21",
- unitsOfMeasure: "ft"
- )
+ result: context.read().firstNonEmptyValue(context.read().vitals?.bodyTemperature??[]),
+ unitsOfMeasure: "C"
+ ).onPress((){
+ // Map> getVitals() {
+ // return {
+ // "heartRate": heartRate ,
+ // "sleep": sleep,
+ // "steps": step,
+ // "activity": activity,
+ // "bodyOxygen": bodyOxygen,
+ // "bodyTemperature": bodyTemperature,
+ // };
+ // }
+ context.read().setDurations(durations.Durations.daily);
+
+ context.read().deleteDataIfSectionIsDifferent("bodyTemperature");
+ context.read().saveSelectedSection("bodyTemperature");
+ context.read().fetchData();
+ context.read().navigateToDetails("bodyTemperature" , sectionName: "Body temperature".capitalizeFirstofEach, uom: "C");
+
+ }),
],
).paddingSymmetrical(24.w, 24.h),
));
diff --git a/lib/widgets/graph/CustomBarGraph.dart b/lib/widgets/graph/CustomBarGraph.dart
index b90ddb00..bacdf0c7 100644
--- a/lib/widgets/graph/CustomBarGraph.dart
+++ b/lib/widgets/graph/CustomBarGraph.dart
@@ -63,6 +63,7 @@ class CustomBarChart extends StatelessWidget {
final double? minY;
final BorderRadius? barRadius;
final double barWidth;
+ final BarTooltipItem Function(DataPoint)? getTooltipItem;
/// Creates the left label and provides it to the chart
final Widget Function(double) leftLabelFormatter;
@@ -100,7 +101,9 @@ class CustomBarChart extends StatelessWidget {
this.verticalInterval,
this.minY,
this.barRadius,
- this.barWidth = 16});
+ this.barWidth = 16,
+ this.getTooltipItem
+ });
@override
Widget build(BuildContext context) {
@@ -124,6 +127,10 @@ class CustomBarChart extends StatelessWidget {
getTooltipColor: (_)=>AppColors.tooltipColor,
getTooltipItem: (group, groupIndex, rod, rodIndex) {
final dataPoint = dataPoints[groupIndex];
+ if(getTooltipItem != null) {
+ return getTooltipItem!(dataPoint);
+ }
+
return BarTooltipItem(
'${dataPoint.actualValue} ${dataPoint.unitOfMeasurement ?? ""}\n${DateFormat('dd MMM, yyyy').format(dataPoint.time)}',
TextStyle(
diff --git a/pubspec.yaml b/pubspec.yaml
index 19acdf31..17860c67 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -88,7 +88,7 @@ dependencies:
location: ^8.0.1
gms_check: ^1.0.4
huawei_location: ^6.14.2+301
- huawei_health: ^6.16.0+300
+ huawei_health: ^6.15.0+300
intl: ^0.20.2
flutter_widget_from_html: ^0.17.1
huawei_map: