huawei watch addded
parent
c00fe3e10c
commit
532c894e16
@ -0,0 +1,87 @@
|
|||||||
|
package com.cloudsolutions.HMGPatientApp.watch.huawei
|
||||||
|
|
||||||
|
import android.os.Build
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
|
import com.cloudsolutions.HMGPatientApp.watch.samsung_watch.model.Vitals
|
||||||
|
import com.huawei.hms.adapter.AvailableAdapter
|
||||||
|
import com.huawei.hms.adapter.internal.AvailableCode
|
||||||
|
|
||||||
|
import com.samsung.android.sdk.health.data.HealthDataStore
|
||||||
|
import com.samsung.android.sdk.health.data.permission.AccessType
|
||||||
|
import com.samsung.android.sdk.health.data.permission.Permission
|
||||||
|
import com.samsung.android.sdk.health.data.request.DataType
|
||||||
|
import com.samsung.android.sdk.health.data.request.DataTypes
|
||||||
|
import com.samsung.android.sdk.health.data.request.LocalTimeFilter
|
||||||
|
import com.samsung.android.sdk.health.data.request.LocalTimeGroup
|
||||||
|
import com.samsung.android.sdk.health.data.request.LocalTimeGroupUnit
|
||||||
|
import com.samsung.android.sdk.health.data.request.Ordering
|
||||||
|
import io.flutter.embedding.engine.FlutterEngine
|
||||||
|
import io.flutter.plugin.common.MethodCall
|
||||||
|
import io.flutter.plugin.common.MethodChannel
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.SupervisorJob
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
import java.time.LocalTime
|
||||||
|
import com.huawei.hms.hihealth.HuaweiHiHealth
|
||||||
|
import com.cloudsolutions.HMGPatientApp.MainActivity
|
||||||
|
|
||||||
|
|
||||||
|
class HuaweiWatch(
|
||||||
|
private var flutterEngine: FlutterEngine,
|
||||||
|
private var mainActivity: MainActivity
|
||||||
|
) {
|
||||||
|
|
||||||
|
private lateinit var channel: MethodChannel
|
||||||
|
private lateinit var dataStore: HealthDataStore
|
||||||
|
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||||
|
private val TAG = "HuaweiWatch"
|
||||||
|
|
||||||
|
|
||||||
|
private lateinit var vitals: MutableMap<String, List<Vitals>>
|
||||||
|
companion object {
|
||||||
|
private const val CHANNEL = "huawei_watch"
|
||||||
|
|
||||||
|
}
|
||||||
|
init{
|
||||||
|
create()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
|
fun create() {
|
||||||
|
Log.d(TAG, "create: is called")
|
||||||
|
// openTok = OpenTok(mainActivity, flutterEngine)
|
||||||
|
channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
|
||||||
|
channel.setMethodCallHandler { call: MethodCall, result: MethodChannel.Result ->
|
||||||
|
when (call.method) {
|
||||||
|
"init" -> {
|
||||||
|
init(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
result.notImplemented()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun init(result: MethodChannel.Result) {
|
||||||
|
|
||||||
|
Log.d(TAG, "onMethodCall: init called")
|
||||||
|
val mSettingController = HuaweiHiHealth.getSettingController(mainActivity);
|
||||||
|
val availableAdapter = AvailableAdapter(60400312)
|
||||||
|
val results= availableAdapter.isHuaweiMobileServicesAvailable(mainActivity) // context indicates a context object.
|
||||||
|
if (results != AvailableCode.SUCCESS) {
|
||||||
|
availableAdapter.startResolution(mainActivity, object : AvailableAdapter.AvailableCallBack {
|
||||||
|
override fun onComplete(result: Int) {
|
||||||
|
Log.d(TAG, "onComplete result: " + result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
result.success("init success")
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,86 +1,403 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:async/src/result/result.dart';
|
import 'package:async/src/result/result.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:hmg_patient_app_new/features/smartwatch_health_data/model/vitals_data_model.dart';
|
||||||
import 'package:hmg_patient_app_new/features/smartwatch_health_data/watch_connectors/watch_helper.dart';
|
import 'package:hmg_patient_app_new/features/smartwatch_health_data/watch_connectors/watch_helper.dart';
|
||||||
|
import 'package:http/http.dart';
|
||||||
import 'package:huawei_health/huawei_health.dart';
|
import 'package:huawei_health/huawei_health.dart';
|
||||||
|
|
||||||
|
|
||||||
class HuaweiHealthDataConnector extends WatchHelper{
|
class HuaweiHealthDataConnector extends WatchHelper{
|
||||||
final MethodChannel _channel = MethodChannel('huawei_watch');
|
final MethodChannel _channel = MethodChannel('huawei_watch');
|
||||||
@override
|
|
||||||
Future<Result<bool>> initDevice() async{
|
|
||||||
try{
|
|
||||||
await _channel.invokeMethod('init');
|
|
||||||
|
|
||||||
}catch(e){
|
DataController? _dataController;
|
||||||
|
final Map<String, List<Vitals>> mappedData = <String, List<Vitals>>{};
|
||||||
|
|
||||||
|
DateTime get _defaultEndDate => DateTime.now();
|
||||||
|
DateTime get _defaultStartDate => _defaultEndDate.subtract(const Duration(days: 365));
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Result<bool>> initDevice() async {
|
||||||
|
try {
|
||||||
|
await _channel.invokeMethod('init');
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('Huawei channel init failed: $e');
|
||||||
}
|
}
|
||||||
// List of scopes to ask for authorization.
|
|
||||||
// Note: These scopes should also be authorized on the Huawei Developer Console.
|
final List<Scope> scopes = <Scope>[
|
||||||
List<Scope> scopes = [
|
Scope.HEALTHKIT_ACTIVITY_READ,
|
||||||
Scope.HEALTHKIT_STEP_READ, Scope.HEALTHKIT_OXYGEN_SATURATION_READ, // View and store height and weight data in Health Service Kit.
|
Scope.HEALTHKIT_ACTIVITY_WRITE,
|
||||||
Scope.HEALTHKIT_HEARTRATE_READ, Scope.HEALTHKIT_SLEEP_READ,
|
Scope.HEALTHKIT_BLOODGLUCOSE_READ,
|
||||||
Scope.HEALTHKIT_BODYTEMPERATURE_READ, Scope.HEALTHKIT_CALORIES_READ
|
Scope.HEALTHKIT_BLOODGLUCOSE_WRITE,
|
||||||
|
Scope.HEALTHKIT_CALORIES_READ,
|
||||||
|
Scope.HEALTHKIT_CALORIES_WRITE,
|
||||||
|
Scope.HEALTHKIT_DISTANCE_READ,
|
||||||
|
Scope.HEALTHKIT_DISTANCE_WRITE,
|
||||||
|
Scope.HEALTHKIT_HEARTRATE_READ,
|
||||||
|
Scope.HEALTHKIT_HEARTRATE_WRITE,
|
||||||
|
Scope.HEALTHKIT_HEIGHTWEIGHT_READ,
|
||||||
|
Scope.HEALTHKIT_HEIGHTWEIGHT_WRITE,
|
||||||
|
Scope.HEALTHKIT_LOCATION_READ,
|
||||||
|
Scope.HEALTHKIT_LOCATION_WRITE,
|
||||||
|
Scope.HEALTHKIT_PULMONARY_READ,
|
||||||
|
Scope.HEALTHKIT_PULMONARY_WRITE,
|
||||||
|
Scope.HEALTHKIT_SLEEP_READ,
|
||||||
|
Scope.HEALTHKIT_SLEEP_WRITE,
|
||||||
|
Scope.HEALTHKIT_SPEED_READ,
|
||||||
|
Scope.HEALTHKIT_SPEED_WRITE,
|
||||||
|
Scope.HEALTHKIT_STEP_READ,
|
||||||
|
Scope.HEALTHKIT_STEP_WRITE,
|
||||||
|
Scope.HEALTHKIT_STRENGTH_READ,
|
||||||
|
Scope.HEALTHKIT_STRENGTH_WRITE,
|
||||||
|
Scope.HEALTHKIT_BODYFAT_READ,
|
||||||
|
Scope.HEALTHKIT_BODYFAT_WRITE,
|
||||||
|
Scope.HEALTHKIT_NUTRITION_READ,
|
||||||
|
Scope.HEALTHKIT_NUTRITION_WRITE,
|
||||||
|
Scope.HEALTHKIT_BLOODPRESSURE_READ,
|
||||||
|
Scope.HEALTHKIT_BLOODPRESSURE_WRITE,
|
||||||
|
Scope.HEALTHKIT_BODYTEMPERATURE_READ,
|
||||||
|
Scope.HEALTHKIT_BODYTEMPERATURE_WRITE,
|
||||||
|
Scope.HEALTHKIT_OXYGENSTATURATION_READ,
|
||||||
|
Scope.HEALTHKIT_OXYGENSTATURATION_WRITE,
|
||||||
|
Scope.HEALTHKIT_REPRODUCTIVE_READ,
|
||||||
|
Scope.HEALTHKIT_REPRODUCTIVE_WRITE,
|
||||||
|
Scope.HEALTHKIT_ACTIVITY_RECORD_READ,
|
||||||
|
Scope.HEALTHKIT_ACTIVITY_RECORD_WRITE,
|
||||||
|
Scope.HEALTHKIT_HEARTRATE_REALTIME,
|
||||||
|
Scope.HEALTHKIT_STEP_REALTIME,
|
||||||
|
Scope.HEALTHKIT_HEARTHEALTH_WRITE,
|
||||||
|
Scope.HEALTHKIT_HEARTHEALTH_READ,
|
||||||
|
Scope.HEALTHKIT_STRESS_WRITE,
|
||||||
|
Scope.HEALTHKIT_STRESS_READ,
|
||||||
|
Scope.HEALTHKIT_OXYGEN_SATURATION_WRITE,
|
||||||
|
Scope.HEALTHKIT_OXYGEN_SATURATION_READ,
|
||||||
|
Scope.HEALTHKIT_HISTORYDATA_OPEN_WEEK,
|
||||||
|
Scope.HEALTHKIT_HISTORYDATA_OPEN_MONTH,
|
||||||
|
Scope.HEALTHKIT_HISTORYDATA_OPEN_YEAR,
|
||||||
];
|
];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
bool? result = await SettingController.getHealthAppAuthorization();
|
await HealthAuth.signIn(scopes);
|
||||||
debugPrint(
|
_dataController = await DataController.init();
|
||||||
'Granted Scopes for result == is $result}',
|
if (_dataController == null) {
|
||||||
);
|
return Result.error(false);
|
||||||
|
}
|
||||||
return Result.value(true);
|
return Result.value(true);
|
||||||
|
} on PlatformException catch (e) {
|
||||||
|
debugPrint('Error on Huawei authorization: $e');
|
||||||
|
return Result.error(false);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint('Error on authorization, Error:${e.toString()}');
|
debugPrint('Unexpected Huawei init error: $e');
|
||||||
return Result.error(false);
|
return Result.error(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> getActivity() async {
|
Future<void> getActivity() async {
|
||||||
DataType dataTypeResult = await SettingController.readDataType(
|
try {
|
||||||
DataType.DT_CONTINUOUS_STEPS_DELTA.name
|
final ReadReply? data = await readDataType(
|
||||||
);
|
DataType.DT_CONTINUOUS_CALORIES_BURNT,
|
||||||
|
_defaultStartDate,
|
||||||
|
_defaultEndDate,
|
||||||
|
);
|
||||||
|
addDataToMap('activity', data, preferredKeys: const <String>['calories', 'calories_total']);
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('Error getting activity data: $e');
|
||||||
|
mappedData['activity'] = <Vitals>[];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future getBloodOxygen() {
|
Future<void> getBloodOxygen() async {
|
||||||
throw UnimplementedError();
|
try {
|
||||||
|
final ReadReply? data = await readDataType(
|
||||||
|
HealthDataTypes.DT_INSTANTANEOUS_SPO2,
|
||||||
|
_defaultStartDate,
|
||||||
|
_defaultEndDate,
|
||||||
|
);
|
||||||
|
addDataToMap(
|
||||||
|
'bloodOxygen',
|
||||||
|
data,
|
||||||
|
preferredKeys: const <String>['saturation', 'saturation_last', 'saturation_avg'],
|
||||||
|
transform: _normalizeSpo2,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('Error getting blood oxygen data: $e');
|
||||||
|
mappedData['bloodOxygen'] = <Vitals>[];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> getBodyTemperature() async {
|
||||||
|
try {
|
||||||
|
final ReadReply? data = await readDataType(
|
||||||
|
HealthDataTypes.DT_INSTANTANEOUS_BODY_TEMPERATURE,
|
||||||
|
_defaultStartDate,
|
||||||
|
_defaultEndDate,
|
||||||
|
);
|
||||||
|
addDataToMap('bodyTemperature', data, preferredKeys: const <String>['temperature']);
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('Error getting body temperature data: $e');
|
||||||
|
mappedData['bodyTemperature'] = <Vitals>[];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future getBodyTemperature() {
|
FutureOr<void> getHeartRate() async {
|
||||||
|
try {
|
||||||
|
final ReadReply? data = await readDataType(
|
||||||
|
DataType.DT_INSTANTANEOUS_HEART_RATE,
|
||||||
|
_defaultStartDate,
|
||||||
|
_defaultEndDate,
|
||||||
|
);
|
||||||
|
addDataToMap('heartRate', data, preferredKeys: const <String>['bpm']);
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('Error getting heart rate data: $e');
|
||||||
|
mappedData['heartRate'] = <Vitals>[];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
throw UnimplementedError();
|
@override
|
||||||
|
FutureOr<void> getSleep() async {
|
||||||
|
try {
|
||||||
|
final ReadReply? data = await readDataType(
|
||||||
|
DataType.DT_CONTINUOUS_SLEEP,
|
||||||
|
_defaultStartDate,
|
||||||
|
_defaultEndDate,
|
||||||
|
);
|
||||||
|
addSleepDataToMap(data);
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('Error getting sleep data: $e');
|
||||||
|
mappedData['sleep'] = <Vitals>[];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
FutureOr<void> getHeartRate() {
|
FutureOr<void> getSteps() async {
|
||||||
throw UnimplementedError();
|
try {
|
||||||
|
final ReadReply? data = await readDataType(
|
||||||
|
DataType.DT_CONTINUOUS_STEPS_DELTA,
|
||||||
|
_defaultStartDate,
|
||||||
|
_defaultEndDate,
|
||||||
|
);
|
||||||
|
addDataToMap('steps', data, preferredKeys: const <String>['steps_delta', 'steps', 'step_rate']);
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('Error getting steps data: $e');
|
||||||
|
mappedData['steps'] = <Vitals>[];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
FutureOr<void> getSleep() {
|
Future<Result<String>> retrieveData() async {
|
||||||
throw UnimplementedError();
|
return Result.value(getMappedData());
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
FutureOr<void> getSteps() {
|
FutureOr<void> getDistance() async {
|
||||||
throw UnimplementedError();
|
try {
|
||||||
|
final ReadReply? data = await readDataType(
|
||||||
|
DataType.DT_CONTINUOUS_DISTANCE_DELTA,
|
||||||
|
_defaultStartDate,
|
||||||
|
_defaultEndDate,
|
||||||
|
);
|
||||||
|
addDataToMap('distance', data, preferredKeys: const <String>['distance_delta', 'distance']);
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('Error getting distance data: $e');
|
||||||
|
mappedData['distance'] = <Vitals>[];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<ReadReply?> readDataType(
|
||||||
|
DataType dataType,
|
||||||
|
DateTime startTime,
|
||||||
|
DateTime endTime,
|
||||||
|
) async {
|
||||||
|
final DataCollector dataCollector = DataCollector(
|
||||||
|
dataType: dataType,
|
||||||
|
dataGenerateType: DataGenerateType.DATA_TYPE_RAW,
|
||||||
|
);
|
||||||
|
|
||||||
@override
|
final ReadOptions readOptions = ReadOptions(
|
||||||
Future retrieveData() {
|
dataCollectors: <DataCollector>[dataCollector],
|
||||||
throw UnimplementedError();
|
startTime: startTime,
|
||||||
|
endTime: endTime,
|
||||||
|
allowRemoteInquiry: true,
|
||||||
|
)..groupByTime(1, timeUnit: TimeUnit.HOURS);
|
||||||
|
|
||||||
|
try {
|
||||||
|
return await _dataController?.read(readOptions);
|
||||||
|
} on PlatformException catch (e) {
|
||||||
|
debugPrint('Huawei read error for ${dataType.name}: $e');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
void addDataToMap(
|
||||||
FutureOr<void> getDistance() {
|
String key,
|
||||||
// TODO: implement getDistance
|
ReadReply? data, {
|
||||||
throw UnimplementedError();
|
List<String> preferredKeys = const <String>[],
|
||||||
|
double Function(double value)? transform,
|
||||||
|
}) {
|
||||||
|
mappedData[key] = <Vitals>[];
|
||||||
|
if (data == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.groups.isNotEmpty) {
|
||||||
|
for (final Group group in data.groups) {
|
||||||
|
for (final SampleSet sampleSet in group.sampleSets ?? <SampleSet>[]) {
|
||||||
|
for (final SamplePoint samplePoint in sampleSet.samplePoints) {
|
||||||
|
_addNumericSampleToMappedData(
|
||||||
|
key: key,
|
||||||
|
samplePoint: samplePoint,
|
||||||
|
preferredKeys: preferredKeys,
|
||||||
|
groupStartTime: group.startTime,
|
||||||
|
transform: transform,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (final SampleSet sampleSet in data.sampleSets) {
|
||||||
|
for (final SamplePoint samplePoint in sampleSet.samplePoints) {
|
||||||
|
_addNumericSampleToMappedData(
|
||||||
|
key: key,
|
||||||
|
samplePoint: samplePoint,
|
||||||
|
preferredKeys: preferredKeys,
|
||||||
|
transform: transform,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void addSleepDataToMap(ReadReply? data) {
|
||||||
|
mappedData['sleep'] = <Vitals>[];
|
||||||
|
if (data == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void appendSleepPoint(SamplePoint samplePoint, {DateTime? groupStart, DateTime? groupEnd}) {
|
||||||
|
final DateTime? start = samplePoint.startTime ?? groupStart;
|
||||||
|
final DateTime? end = samplePoint.endTime ?? groupEnd;
|
||||||
|
|
||||||
|
double? value;
|
||||||
|
if (start != null && end != null && end.isAfter(start)) {
|
||||||
|
value = end.difference(start).inMinutes / 60;
|
||||||
|
}
|
||||||
|
|
||||||
|
value ??= _extractNumericValue(
|
||||||
|
samplePoint,
|
||||||
|
const <String>['sleep_state', 'all_sleep_time', 'deep_sleep_time', 'light_sleep_time'],
|
||||||
|
);
|
||||||
|
|
||||||
|
if (value == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String timestamp =
|
||||||
|
(start ?? samplePoint.samplingTime ?? DateTime.now()).toIso8601String();
|
||||||
|
|
||||||
|
mappedData['sleep']?.add(
|
||||||
|
Vitals(
|
||||||
|
value: value.toStringAsFixed(2),
|
||||||
|
timestamp: timestamp,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.groups.isNotEmpty) {
|
||||||
|
for (final Group group in data.groups) {
|
||||||
|
for (final SampleSet sampleSet in group.sampleSets ?? <SampleSet>[]) {
|
||||||
|
for (final SamplePoint samplePoint in sampleSet.samplePoints) {
|
||||||
|
appendSleepPoint(
|
||||||
|
samplePoint,
|
||||||
|
groupStart: group.startTime,
|
||||||
|
groupEnd: group.endTime,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (final SampleSet sampleSet in data.sampleSets) {
|
||||||
|
for (final SamplePoint samplePoint in sampleSet.samplePoints) {
|
||||||
|
appendSleepPoint(samplePoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _addNumericSampleToMappedData({
|
||||||
|
required String key,
|
||||||
|
required SamplePoint samplePoint,
|
||||||
|
required List<String> preferredKeys,
|
||||||
|
DateTime? groupStartTime,
|
||||||
|
double Function(double value)? transform,
|
||||||
|
}) {
|
||||||
|
final double? numericValue = _extractNumericValue(samplePoint, preferredKeys);
|
||||||
|
if (numericValue == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final double transformedValue = transform?.call(numericValue) ?? numericValue;
|
||||||
|
final String timestamp =
|
||||||
|
(samplePoint.samplingTime ?? samplePoint.startTime ?? groupStartTime ?? DateTime.now())
|
||||||
|
.toIso8601String();
|
||||||
|
|
||||||
|
mappedData[key]?.add(
|
||||||
|
Vitals(
|
||||||
|
value: transformedValue.toStringAsFixed(2),
|
||||||
|
timestamp: timestamp,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
double? _extractNumericValue(SamplePoint samplePoint, List<String> preferredKeys) {
|
||||||
|
final Map<String, dynamic> values = <String, dynamic>{
|
||||||
|
...samplePoint.pairs,
|
||||||
|
...?samplePoint.fieldValues,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (final String key in preferredKeys) {
|
||||||
|
final dynamic value = values[key];
|
||||||
|
if (value is num) {
|
||||||
|
return value.toDouble();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (final dynamic value in values.values) {
|
||||||
|
if (value is num) {
|
||||||
|
return value.toDouble();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
double _normalizeSpo2(double value) {
|
||||||
|
if (value <= 1) {
|
||||||
|
return value * 100;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getMappedData() {
|
||||||
|
return jsonEncode(<String, dynamic>{
|
||||||
|
'heartRate': (mappedData['heartRate'] ?? <Vitals>[]).map((Vitals v) => v.toJson()).toList(),
|
||||||
|
'sleep': (mappedData['sleep'] ?? <Vitals>[]).map((Vitals v) => v.toJson()).toList(),
|
||||||
|
'steps': (mappedData['steps'] ?? <Vitals>[]).map((Vitals v) => v.toJson()).toList(),
|
||||||
|
'activity': (mappedData['activity'] ?? <Vitals>[]).map((Vitals v) => v.toJson()).toList(),
|
||||||
|
'bloodOxygen':
|
||||||
|
(mappedData['bloodOxygen'] ?? <Vitals>[]).map((Vitals v) => v.toJson()).toList(),
|
||||||
|
'bodyTemperature':
|
||||||
|
(mappedData['bodyTemperature'] ?? <Vitals>[]).map((Vitals v) => v.toJson()).toList(),
|
||||||
|
'distance': (mappedData['distance'] ?? <Vitals>[]).map((Vitals v) => v.toJson()).toList(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue