Showing
3 changed files
with
113 additions
and
7 deletions
| @@ -1345,7 +1345,7 @@ class HealthRawDailyStressCalculator { | @@ -1345,7 +1345,7 @@ class HealthRawDailyStressCalculator { | ||
| 1345 | .toList() | 1345 | .toList() |
| 1346 | ..sort(); | 1346 | ..sort(); |
| 1347 | if (values.isEmpty) return null; | 1347 | if (values.isEmpty) return null; |
| 1348 | - final stressValue = _average(values); | 1348 | + final stressValue = _integerDouble(_average(values)); |
| 1349 | final stressScore = _dailyStressScore(values).round().clamp(1, 100).toInt(); | 1349 | final stressScore = _dailyStressScore(values).round().clamp(1, 100).toInt(); |
| 1350 | return HealthRawDailyStressPoint( | 1350 | return HealthRawDailyStressPoint( |
| 1351 | userId: userId, | 1351 | userId: userId, |
| @@ -1423,6 +1423,8 @@ class HealthRawDailyStressCalculator { | @@ -1423,6 +1423,8 @@ class HealthRawDailyStressCalculator { | ||
| 1423 | math.max(min.toDouble(), math.min(max.toDouble(), value.toDouble())); | 1423 | math.max(min.toDouble(), math.min(max.toDouble(), value.toDouble())); |
| 1424 | 1424 | ||
| 1425 | static double _round(double value) => (value * 10).round() / 10; | 1425 | static double _round(double value) => (value * 10).round() / 10; |
| 1426 | + | ||
| 1427 | + static double _integerDouble(num value) => value.toInt().toDouble(); | ||
| 1426 | } | 1428 | } |
| 1427 | 1429 | ||
| 1428 | HealthRawStressState healthRawRealtimeStressState(num value) { | 1430 | HealthRawStressState healthRawRealtimeStressState(num value) { |
| @@ -1447,6 +1449,8 @@ double? _positiveDouble(Object? value) { | @@ -1447,6 +1449,8 @@ double? _positiveDouble(Object? value) { | ||
| 1447 | return result; | 1449 | return result; |
| 1448 | } | 1450 | } |
| 1449 | 1451 | ||
| 1452 | +double _integerDouble(num value) => value.toInt().toDouble(); | ||
| 1453 | + | ||
| 1450 | HealthRawStressState? _stressStateFromDb(Object? value) { | 1454 | HealthRawStressState? _stressStateFromDb(Object? value) { |
| 1451 | final stateValue = value as int?; | 1455 | final stateValue = value as int?; |
| 1452 | if (stateValue == null) return null; | 1456 | if (stateValue == null) return null; |
| @@ -2096,7 +2100,7 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( | @@ -2096,7 +2100,7 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( | ||
| 2096 | 'raw_end_time': point.rawEndTime, | 2100 | 'raw_end_time': point.rawEndTime, |
| 2097 | 'user_id': point.userId, | 2101 | 'user_id': point.userId, |
| 2098 | 'raw_hrv': point.rawHrv, | 2102 | 'raw_hrv': point.rawHrv, |
| 2099 | - 'result': point.result, | 2103 | + 'result': _integerDouble(point.result), |
| 2100 | 'source_start_time': point.sourceStartTime, | 2104 | 'source_start_time': point.sourceStartTime, |
| 2101 | 'source_end_time': point.sourceEndTime, | 2105 | 'source_end_time': point.sourceEndTime, |
| 2102 | 'state': point.state.value, | 2106 | 'state': point.state.value, |
| @@ -2114,7 +2118,7 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( | @@ -2114,7 +2118,7 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( | ||
| 2114 | 'raw_end_time': point.rawEndTime, | 2118 | 'raw_end_time': point.rawEndTime, |
| 2115 | 'user_id': point.userId, | 2119 | 'user_id': point.userId, |
| 2116 | 'raw_hr': point.rawHr, | 2120 | 'raw_hr': point.rawHr, |
| 2117 | - 'result': point.result, | 2121 | + 'result': _integerDouble(point.result), |
| 2118 | 'source_start_time': point.sourceStartTime, | 2122 | 'source_start_time': point.sourceStartTime, |
| 2119 | 'source_end_time': point.sourceEndTime, | 2123 | 'source_end_time': point.sourceEndTime, |
| 2120 | ..._flagsRow(point.flags), | 2124 | ..._flagsRow(point.flags), |
| @@ -2126,7 +2130,7 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( | @@ -2126,7 +2130,7 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( | ||
| 2126 | return <String, Object?>{ | 2130 | return <String, Object?>{ |
| 2127 | 'date': point.date, | 2131 | 'date': point.date, |
| 2128 | 'user_id': point.userId, | 2132 | 'user_id': point.userId, |
| 2129 | - 'stress_value': point.stressValue, | 2133 | + 'stress_value': _integerDouble(point.stressValue), |
| 2130 | 'stress_score': point.stressScore, | 2134 | 'stress_score': point.stressScore, |
| 2131 | 'state': point.state.value, | 2135 | 'state': point.state.value, |
| 2132 | 'data_time': point.dataTime, | 2136 | 'data_time': point.dataTime, |
| @@ -226,15 +226,16 @@ class HealthRawStressCalculator { | @@ -226,15 +226,16 @@ class HealthRawStressCalculator { | ||
| 226 | ...hrWindow, | 226 | ...hrWindow, |
| 227 | ...restingHistory, | 227 | ...restingHistory, |
| 228 | ]); | 228 | ]); |
| 229 | + final normalizedTrendHrv = _integerDouble(trendHrv); | ||
| 229 | results.add( | 230 | results.add( |
| 230 | HealthRawHrvStressPoint( | 231 | HealthRawHrvStressPoint( |
| 231 | userId: userId, | 232 | userId: userId, |
| 232 | rawEndTime: point.endTime, | 233 | rawEndTime: point.endTime, |
| 233 | rawHrv: pointValue, | 234 | rawHrv: pointValue, |
| 234 | - result: trendHrv, | 235 | + result: normalizedTrendHrv, |
| 235 | sourceStartTime: sourceRange.start, | 236 | sourceStartTime: sourceRange.start, |
| 236 | sourceEndTime: sourceRange.end, | 237 | sourceEndTime: sourceRange.end, |
| 237 | - state: _hrvState(trendHrv), | 238 | + state: _hrvState(normalizedTrendHrv), |
| 238 | baselineHrv: baselineHrv, | 239 | baselineHrv: baselineHrv, |
| 239 | baselineAwakeHrv: baselineAwakeHrv, | 240 | baselineAwakeHrv: baselineAwakeHrv, |
| 240 | baselineSleepHrv: isSleep ? baselineSleepHrv : null, | 241 | baselineSleepHrv: isSleep ? baselineSleepHrv : null, |
| @@ -355,7 +356,7 @@ class HealthRawStressCalculator { | @@ -355,7 +356,7 @@ class HealthRawStressCalculator { | ||
| 355 | } | 356 | } |
| 356 | } | 357 | } |
| 357 | 358 | ||
| 358 | - final stress = _round(_clamp(adjustment.value, 1, 100), 1); | 359 | + final stress = _integerDouble(_clamp(adjustment.value, 1, 100)); |
| 359 | final sourceRange = _sourceRangeForRealtime( | 360 | final sourceRange = _sourceRangeForRealtime( |
| 360 | hrWindow: hrWindow, | 361 | hrWindow: hrWindow, |
| 361 | restingHistory: restingHistory, | 362 | restingHistory: restingHistory, |
| @@ -571,6 +572,8 @@ class HealthRawStressCalculator { | @@ -571,6 +572,8 @@ class HealthRawStressCalculator { | ||
| 571 | return (value * scale).round() / scale; | 572 | return (value * scale).round() / scale; |
| 572 | } | 573 | } |
| 573 | 574 | ||
| 575 | + static double _integerDouble(num value) => value.toInt().toDouble(); | ||
| 576 | + | ||
| 574 | static List<int> _daysInRange(int startTime, int endTime) { | 577 | static List<int> _daysInRange(int startTime, int endTime) { |
| 575 | final start = DateTime.fromMillisecondsSinceEpoch(startTime * 1000); | 578 | final start = DateTime.fromMillisecondsSinceEpoch(startTime * 1000); |
| 576 | final end = DateTime.fromMillisecondsSinceEpoch(endTime * 1000); | 579 | final end = DateTime.fromMillisecondsSinceEpoch(endTime * 1000); |
| @@ -3,11 +3,14 @@ import 'dart:async'; | @@ -3,11 +3,14 @@ import 'dart:async'; | ||
| 3 | import 'package:flutter/foundation.dart'; | 3 | import 'package:flutter/foundation.dart'; |
| 4 | import 'package:flutter/material.dart'; | 4 | import 'package:flutter/material.dart'; |
| 5 | import 'package:flutter/services.dart'; | 5 | import 'package:flutter/services.dart'; |
| 6 | +import 'package:get/get.dart'; | ||
| 6 | import 'package:intl/date_symbol_data_local.dart'; | 7 | import 'package:intl/date_symbol_data_local.dart'; |
| 7 | 8 | ||
| 8 | import 'app/bootstrap/app_bootstrap.dart'; | 9 | import 'app/bootstrap/app_bootstrap.dart'; |
| 9 | import 'app/double_feel_app.dart'; | 10 | import 'app/double_feel_app.dart'; |
| 11 | +import 'core/services/health_raw_data_core_service.dart'; | ||
| 10 | import 'core/theme/app_theme.dart'; | 12 | import 'core/theme/app_theme.dart'; |
| 13 | +import 'data/local/user_preferences_storage.dart'; | ||
| 11 | 14 | ||
| 12 | Future<void> main() async { | 15 | Future<void> main() async { |
| 13 | runZonedGuarded<Future<Null>>( | 16 | runZonedGuarded<Future<Null>>( |
| @@ -29,3 +32,99 @@ Future<void> main() async { | @@ -29,3 +32,99 @@ Future<void> main() async { | ||
| 29 | }, | 32 | }, |
| 30 | ); | 33 | ); |
| 31 | } | 34 | } |
| 35 | + | ||
| 36 | +@pragma('vm:entry-point') | ||
| 37 | +Future<void> healthBackgroundMain() async { | ||
| 38 | + WidgetsFlutterBinding.ensureInitialized(); | ||
| 39 | + const channel = MethodChannel('doublefeel_flutter_health_background_channel'); | ||
| 40 | + | ||
| 41 | + await AppBootstrap.init(); | ||
| 42 | + | ||
| 43 | + channel.setMethodCallHandler((call) async { | ||
| 44 | + switch (call.method) { | ||
| 45 | + case 'runPendingHealthTasks': | ||
| 46 | + await _runPendingHealthTasks(channel, call.arguments); | ||
| 47 | + return true; | ||
| 48 | + default: | ||
| 49 | + return null; | ||
| 50 | + } | ||
| 51 | + }); | ||
| 52 | + | ||
| 53 | + await channel.invokeMethod('healthBackgroundTaskReady', { | ||
| 54 | + 'timestamp': DateTime.now().millisecondsSinceEpoch ~/ 1000, | ||
| 55 | + }); | ||
| 56 | +} | ||
| 57 | + | ||
| 58 | +Future<void> _runPendingHealthTasks( | ||
| 59 | + MethodChannel channel, | ||
| 60 | + Object? arguments, | ||
| 61 | +) async { | ||
| 62 | + final tasks = _backgroundHealthTasksFromArguments(arguments); | ||
| 63 | + if (tasks.isEmpty) return; | ||
| 64 | + | ||
| 65 | + for (final task in tasks) { | ||
| 66 | + await channel.invokeMethod('healthBackgroundTaskReceived', { | ||
| 67 | + 'taskId': task.taskId, | ||
| 68 | + }); | ||
| 69 | + } | ||
| 70 | + | ||
| 71 | + final userPrefs = Get.find<UserPreferencesStorage>(); | ||
| 72 | + final userId = userPrefs.preferences.value.meUserInfo?.id ?? 0; | ||
| 73 | + final token = userPrefs.accessToken; | ||
| 74 | + if (userId <= 0 || token.isEmpty) { | ||
| 75 | + for (final task in tasks) { | ||
| 76 | + await channel.invokeMethod('healthBackgroundTaskFinished', { | ||
| 77 | + 'taskId': task.taskId, | ||
| 78 | + 'success': false, | ||
| 79 | + 'retryable': true, | ||
| 80 | + 'error': 'missingLogin', | ||
| 81 | + }); | ||
| 82 | + } | ||
| 83 | + return; | ||
| 84 | + } | ||
| 85 | + | ||
| 86 | + try { | ||
| 87 | + await Get.find<HealthRawDataCoreService>().startCoreCaculate(); | ||
| 88 | + for (final task in tasks) { | ||
| 89 | + await channel.invokeMethod('healthBackgroundTaskFinished', { | ||
| 90 | + 'taskId': task.taskId, | ||
| 91 | + 'success': true, | ||
| 92 | + }); | ||
| 93 | + } | ||
| 94 | + } catch (error) { | ||
| 95 | + for (final task in tasks) { | ||
| 96 | + await channel.invokeMethod('healthBackgroundTaskFinished', { | ||
| 97 | + 'taskId': task.taskId, | ||
| 98 | + 'success': false, | ||
| 99 | + 'retryable': true, | ||
| 100 | + 'error': error.toString(), | ||
| 101 | + }); | ||
| 102 | + } | ||
| 103 | + } | ||
| 104 | +} | ||
| 105 | + | ||
| 106 | +List<_BackgroundHealthTask> _backgroundHealthTasksFromArguments( | ||
| 107 | + Object? arguments, | ||
| 108 | +) { | ||
| 109 | + final rawTasksValue = | ||
| 110 | + arguments is Map<Object?, Object?> ? arguments['tasks'] : null; | ||
| 111 | + final rawTasks = | ||
| 112 | + rawTasksValue is List ? rawTasksValue.cast<Object?>() : const <Object?>[]; | ||
| 113 | + return rawTasks | ||
| 114 | + .whereType<Map<Object?, Object?>>() | ||
| 115 | + .map(_BackgroundHealthTask.fromMap) | ||
| 116 | + .whereType<_BackgroundHealthTask>() | ||
| 117 | + .toList(growable: false); | ||
| 118 | +} | ||
| 119 | + | ||
| 120 | +class _BackgroundHealthTask { | ||
| 121 | + const _BackgroundHealthTask({required this.taskId}); | ||
| 122 | + | ||
| 123 | + final String taskId; | ||
| 124 | + | ||
| 125 | + static _BackgroundHealthTask? fromMap(Map<Object?, Object?> map) { | ||
| 126 | + final taskId = map['taskId']; | ||
| 127 | + if (taskId is! String || taskId.isEmpty) return null; | ||
| 128 | + return _BackgroundHealthTask(taskId: taskId); | ||
| 129 | + } | ||
| 130 | +} |
-
Please register or login to post a comment