Commit e41da12768beaa89120034b07210bdc5a0699c24

Authored by 权海
1 parent 80b26c33

feat(ui):数据流转统一成一个日志

@@ -14,12 +14,14 @@ import '../../data/local/user_account_storage.dart'; @@ -14,12 +14,14 @@ import '../../data/local/user_account_storage.dart';
14 import '../../data/local/user_preferences_storage.dart'; 14 import '../../data/local/user_preferences_storage.dart';
15 import '../../core/services/loading_service.dart'; 15 import '../../core/services/loading_service.dart';
16 import '../../core/services/native_health_data_update_bridge.dart'; 16 import '../../core/services/native_health_data_update_bridge.dart';
  17 +import '../../core/services/raw_data_service/platform_ios/apple_health_raw_data_core_service.dart';
17 import '../bindings/initial_binding.dart'; 18 import '../bindings/initial_binding.dart';
18 19
19 /// Application startup before [runApp]. 20 /// Application startup before [runApp].
20 abstract final class AppBootstrap { 21 abstract final class AppBootstrap {
21 static Future<void> init() async { 22 static Future<void> init() async {
22 WidgetsFlutterBinding.ensureInitialized(); 23 WidgetsFlutterBinding.ensureInitialized();
  24 + AppleHealthRawDataCoreService.setExecutionEngineType('main');
23 AppLogger.init(); 25 AppLogger.init();
24 26
25 await UserAgentProvider.init(); 27 await UserAgentProvider.init();
@@ -55,6 +57,7 @@ abstract final class AppBootstrap { @@ -55,6 +57,7 @@ abstract final class AppBootstrap {
55 57
56 static Future<void> initForBackground() async { 58 static Future<void> initForBackground() async {
57 WidgetsFlutterBinding.ensureInitialized(); 59 WidgetsFlutterBinding.ensureInitialized();
  60 + AppleHealthRawDataCoreService.setExecutionEngineType('temporary');
58 AppLogger.init(); 61 AppLogger.init();
59 62
60 await UserAgentProvider.init(); 63 await UserAgentProvider.init();
@@ -35,9 +35,7 @@ class DeveloperOptionsItem { @@ -35,9 +35,7 @@ class DeveloperOptionsItem {
35 enum DeveloperOptionsAction { 35 enum DeveloperOptionsAction {
36 shareAppDiagnosticLog, 36 shareAppDiagnosticLog,
37 shareHealthRawDataDatabase, 37 shareHealthRawDataDatabase,
38 - shareNativeAppleHealthObserverRecord,  
39 - shareFlutterObserverRecord,  
40 - shareLocalNotificationDebugRecord, 38 + shareHealthPipelineRecord,
41 sendTestHrvLocalNotification, 39 sendTestHrvLocalNotification,
42 pullOhosRawData, 40 pullOhosRawData,
43 clearHealthRawDataDatabaseAndUploadLog, 41 clearHealthRawDataDatabaseAndUploadLog,
@@ -85,16 +83,8 @@ class DeveloperOptionsController extends GetxController { @@ -85,16 +83,8 @@ class DeveloperOptionsController extends GetxController {
85 action: DeveloperOptionsAction.shareHealthRawDataDatabase, 83 action: DeveloperOptionsAction.shareHealthRawDataDatabase,
86 ), 84 ),
87 DeveloperOptionsItem( 85 DeveloperOptionsItem(
88 - title: '分享原生响应AppleHealth记录',  
89 - action: DeveloperOptionsAction.shareNativeAppleHealthObserverRecord,  
90 - ),  
91 - DeveloperOptionsItem(  
92 - title: '分享Flutter 响应AppleHealth回调记录',  
93 - action: DeveloperOptionsAction.shareFlutterObserverRecord,  
94 - ),  
95 - DeveloperOptionsItem(  
96 - title: '分享本地推送诊断日志',  
97 - action: DeveloperOptionsAction.shareLocalNotificationDebugRecord, 86 + title: '分享 Health 数据流程日志',
  87 + action: DeveloperOptionsAction.shareHealthPipelineRecord,
98 ), 88 ),
99 DeveloperOptionsItem( 89 DeveloperOptionsItem(
100 title: '测试本地推送 HRV', 90 title: '测试本地推送 HRV',
@@ -124,16 +114,8 @@ class DeveloperOptionsController extends GetxController { @@ -124,16 +114,8 @@ class DeveloperOptionsController extends GetxController {
124 action: DeveloperOptionsAction.shareHealthRawDataDatabase, 114 action: DeveloperOptionsAction.shareHealthRawDataDatabase,
125 ), 115 ),
126 DeveloperOptionsItem( 116 DeveloperOptionsItem(
127 - title: '分享原生响应AppleHealth记录',  
128 - action: DeveloperOptionsAction.shareNativeAppleHealthObserverRecord,  
129 - ),  
130 - DeveloperOptionsItem(  
131 - title: '分享Flutter 响应AppleHealth回调记录',  
132 - action: DeveloperOptionsAction.shareFlutterObserverRecord,  
133 - ),  
134 - DeveloperOptionsItem(  
135 - title: '分享本地推送诊断日志',  
136 - action: DeveloperOptionsAction.shareLocalNotificationDebugRecord, 117 + title: '分享 Health 数据流程日志',
  118 + action: DeveloperOptionsAction.shareHealthPipelineRecord,
137 ), 119 ),
138 DeveloperOptionsItem( 120 DeveloperOptionsItem(
139 title: '主动拉取 OHOS 原始数据', 121 title: '主动拉取 OHOS 原始数据',
@@ -161,14 +143,8 @@ class DeveloperOptionsController extends GetxController { @@ -161,14 +143,8 @@ class DeveloperOptionsController extends GetxController {
161 case DeveloperOptionsAction.shareHealthRawDataDatabase: 143 case DeveloperOptionsAction.shareHealthRawDataDatabase:
162 await shareHealthRawDataDatabase(); 144 await shareHealthRawDataDatabase();
163 break; 145 break;
164 - case DeveloperOptionsAction.shareNativeAppleHealthObserverRecord:  
165 - await shareNativeAppleHealthObserverRecord();  
166 - break;  
167 - case DeveloperOptionsAction.shareFlutterObserverRecord:  
168 - await shareFlutterObserverRecord();  
169 - break;  
170 - case DeveloperOptionsAction.shareLocalNotificationDebugRecord:  
171 - await shareLocalNotificationDebugRecord(); 146 + case DeveloperOptionsAction.shareHealthPipelineRecord:
  147 + await shareHealthPipelineRecord();
172 break; 148 break;
173 case DeveloperOptionsAction.sendTestHrvLocalNotification: 149 case DeveloperOptionsAction.sendTestHrvLocalNotification:
174 await sendTestHrvLocalNotification(); 150 await sendTestHrvLocalNotification();
@@ -248,32 +224,6 @@ class DeveloperOptionsController extends GetxController { @@ -248,32 +224,6 @@ class DeveloperOptionsController extends GetxController {
248 } 224 }
249 } 225 }
250 226
251 - Future<void> shareNativeAppleHealthObserverRecord() async {  
252 - try {  
253 - await _healthRawDataCoreService.shareNativeAppleHealthObserverRecord();  
254 - } catch (error, stackTrace) {  
255 - AppLogger.e(  
256 - 'Share native AppleHealthObserver record failed',  
257 - error,  
258 - stackTrace,  
259 - );  
260 - Get.snackbar('Share failed', error.toString());  
261 - }  
262 - }  
263 -  
264 - Future<void> shareFlutterObserverRecord() async {  
265 - try {  
266 - await _healthRawDataCoreService.shareFlutterObserverRecord();  
267 - } catch (error, stackTrace) {  
268 - AppLogger.e(  
269 - 'Share Flutter Observer-Caculate record failed',  
270 - error,  
271 - stackTrace,  
272 - );  
273 - Get.snackbar('Share failed', error.toString());  
274 - }  
275 - }  
276 -  
277 Future<void> shareUploadTaskRecord() async { 227 Future<void> shareUploadTaskRecord() async {
278 try { 228 try {
279 await _healthRawDataCoreService.shareUploadTaskRecord(); 229 await _healthRawDataCoreService.shareUploadTaskRecord();
@@ -287,17 +237,15 @@ class DeveloperOptionsController extends GetxController { @@ -287,17 +237,15 @@ class DeveloperOptionsController extends GetxController {
287 } 237 }
288 } 238 }
289 239
290 - Future<void> shareLocalNotificationDebugRecord() async { 240 + Future<void> shareHealthPipelineRecord() async {
291 try { 241 try {
292 final sharedByNative = 242 final sharedByNative =
293 await _healthRawDataCoreService.shareLocalNotificationDebugRecord(); 243 await _healthRawDataCoreService.shareLocalNotificationDebugRecord();
294 if (sharedByNative) return; 244 if (sharedByNative) return;
295 - final text =  
296 - await _healthRawDataCoreService.readLocalNotificationDebugLogText();  
297 - await _platformHostApi.shareText(text); 245 + Get.snackbar('Share failed', 'Health data pipeline log is unavailable');
298 } catch (error, stackTrace) { 246 } catch (error, stackTrace) {
299 AppLogger.e( 247 AppLogger.e(
300 - 'Share local notification debug record failed', 248 + 'Share health pipeline record failed',
301 error, 249 error,
302 stackTrace, 250 stackTrace,
303 ); 251 );
@@ -54,7 +54,7 @@ class CustomThemeCard extends StatelessWidget { @@ -54,7 +54,7 @@ class CustomThemeCard extends StatelessWidget {
54 : SizedBox( 54 : SizedBox(
55 height: 112, 55 height: 112,
56 child: ListView.separated( 56 child: ListView.separated(
57 - scrollDirection: Axis.horizontal, 57 + scrollDirection: Axis.vertical,
58 padding: EdgeInsets.zero, 58 padding: EdgeInsets.zero,
59 itemBuilder: (context, index) { 59 itemBuilder: (context, index) {
60 return _CustomThemeItem( 60 return _CustomThemeItem(
@@ -24,6 +24,11 @@ import '../health_sleep_calculator.dart'; @@ -24,6 +24,11 @@ import '../health_sleep_calculator.dart';
24 class AppleHealthRawDataCoreService { 24 class AppleHealthRawDataCoreService {
25 static const int defaultLookbackDays = 183; 25 static const int defaultLookbackDays = 183;
26 static const int defaultReadChunkDays = 7; 26 static const int defaultReadChunkDays = 7;
  27 + static String _executionEngineType = 'main';
  28 +
  29 + static void setExecutionEngineType(String value) {
  30 + _executionEngineType = value;
  31 + }
27 32
28 AppleHealthRawDataCoreService({ 33 AppleHealthRawDataCoreService({
29 HealthKitHostApi? healthApi, 34 HealthKitHostApi? healthApi,
@@ -107,6 +112,11 @@ class AppleHealthRawDataCoreService { @@ -107,6 +112,11 @@ class AppleHealthRawDataCoreService {
107 _healthDataUpdatedController.stream; 112 _healthDataUpdatedController.stream;
108 113
109 Future<bool> onHealthDataUpdated({List<int>? dataTypes}) async { 114 Future<bool> onHealthDataUpdated({List<int>? dataTypes}) async {
  115 + await _saveLocalNotificationDebugEvent(<String, Object?>{
  116 + 'event': 'flutterHealthDataReceived',
  117 + 'identifier': 'flutter.health_data.received',
  118 + 'data_types': dataTypes ?? const <int>[],
  119 + });
110 await _saveNativeCallFlutterChange(dataTypes); 120 await _saveNativeCallFlutterChange(dataTypes);
111 if (_healthDataUpdatedCalculation != null) { 121 if (_healthDataUpdatedCalculation != null) {
112 _pendingHealthDataUpdatedTypes = _mergeHealthDataTypes( 122 _pendingHealthDataUpdatedTypes = _mergeHealthDataTypes(
@@ -282,6 +292,11 @@ class AppleHealthRawDataCoreService { @@ -282,6 +292,11 @@ class AppleHealthRawDataCoreService {
282 } 292 }
283 293
284 final userId = _userId; 294 final userId = _userId;
  295 + await _saveLocalNotificationDebugEvent(<String, Object?>{
  296 + 'event': 'flutterCalculationStarted',
  297 + 'identifier': 'flutter.calculation.started',
  298 + 'user_id': userId,
  299 + });
285 final hasAuthorization = await _hasHealthReadAuthorization(); 300 final hasAuthorization = await _hasHealthReadAuthorization();
286 if (!hasAuthorization) { 301 if (!hasAuthorization) {
287 return HealthRawStressCalculationResult( 302 return HealthRawStressCalculationResult(
@@ -392,6 +407,21 @@ class AppleHealthRawDataCoreService { @@ -392,6 +407,21 @@ class AppleHealthRawDataCoreService {
392 ), 407 ),
393 ); 408 );
394 await _localStore.upsertResult(newResult); 409 await _localStore.upsertResult(newResult);
  410 + await _saveLocalNotificationDebugEvent(<String, Object?>{
  411 + 'event': 'flutterCalculationResultStored',
  412 + 'identifier': 'flutter.calculation.result_stored',
  413 + 'user_id': userId,
  414 + 'hrv_count': newResult.hrvStressPoints.length,
  415 + 'realtime_count': newResult.realtimeStressPoints.length,
  416 + 'latest_hrv': _latestHrvStressPointPayload(newResult.hrvStressPoints),
  417 + 'latest_realtime':
  418 + _latestRealtimeStressPointPayload(newResult.realtimeStressPoints),
  419 + });
  420 + await _sendLocalNotificationsAfterCalculation(
  421 + result: newResult,
  422 + hasExistingHrv: latestHrvRawEndTime != null,
  423 + hasExistingSleep: latestSleepResultTime != null,
  424 + );
395 _scheduleResultUpload('upload hrv results', _uploadHrvResults); 425 _scheduleResultUpload('upload hrv results', _uploadHrvResults);
396 _scheduleResultUpload( 426 _scheduleResultUpload(
397 'upload realtime stress results', 427 'upload realtime stress results',
@@ -417,7 +447,10 @@ class AppleHealthRawDataCoreService { @@ -417,7 +447,10 @@ class AppleHealthRawDataCoreService {
417 sleepResults: sleepResults, 447 sleepResults: sleepResults,
418 ); 448 );
419 await _sendLocalNotificationsAfterCalculation( 449 await _sendLocalNotificationsAfterCalculation(
420 - result: storedResult, 450 + result: storedResult.copyWith(
  451 + hrvStressPoints: const <HealthRawHrvStressPoint>[],
  452 + realtimeStressPoints: const <HealthRawRealtimeStressPoint>[],
  453 + ),
421 hasExistingHrv: latestHrvRawEndTime != null, 454 hasExistingHrv: latestHrvRawEndTime != null,
422 hasExistingSleep: latestSleepResultTime != null, 455 hasExistingSleep: latestSleepResultTime != null,
423 ); 456 );
@@ -425,6 +458,15 @@ class AppleHealthRawDataCoreService { @@ -425,6 +458,15 @@ class AppleHealthRawDataCoreService {
425 _logInfo( 458 _logInfo(
426 'calculateEndAt=${_formatDateTimeMilliseconds(calculateFinishedAt)}', 459 'calculateEndAt=${_formatDateTimeMilliseconds(calculateFinishedAt)}',
427 ); 460 );
  461 + await _saveLocalNotificationDebugEvent(<String, Object?>{
  462 + 'event': 'flutterCalculationFinished',
  463 + 'identifier': 'flutter.calculation.finished',
  464 + 'user_id': userId,
  465 + 'hrv_count': storedResult.hrvStressPoints.length,
  466 + 'realtime_count': storedResult.realtimeStressPoints.length,
  467 + 'daily_stress_count': storedResult.dailyStressPoints.length,
  468 + 'sleep_count': storedResult.sleepResults.length,
  469 + });
428 return storedResult; 470 return storedResult;
429 } 471 }
430 472
@@ -436,6 +478,7 @@ class AppleHealthRawDataCoreService { @@ -436,6 +478,7 @@ class AppleHealthRawDataCoreService {
436 await _saveLocalNotificationDebugEvent( 478 await _saveLocalNotificationDebugEvent(
437 <String, Object?>{ 479 <String, Object?>{
438 'event': 'flutterNotificationCalculationFinished', 480 'event': 'flutterNotificationCalculationFinished',
  481 + 'identifier': 'flutter.notification.calculation_finished',
439 'user_id': result.userId, 482 'user_id': result.userId,
440 'has_existing_hrv': hasExistingHrv, 483 'has_existing_hrv': hasExistingHrv,
441 'has_existing_sleep': hasExistingSleep, 484 'has_existing_sleep': hasExistingSleep,
@@ -452,6 +495,7 @@ class AppleHealthRawDataCoreService { @@ -452,6 +495,7 @@ class AppleHealthRawDataCoreService {
452 await _saveLocalNotificationDebugEvent( 495 await _saveLocalNotificationDebugEvent(
453 <String, Object?>{ 496 <String, Object?>{
454 'event': 'flutterNotificationRecordRead', 497 'event': 'flutterNotificationRecordRead',
  498 + 'identifier': 'flutter.notification.record_read',
455 'user_id': result.userId, 499 'user_id': result.userId,
456 'record': _localNotificationRecordPayload(record), 500 'record': _localNotificationRecordPayload(record),
457 }, 501 },
@@ -463,6 +507,7 @@ class AppleHealthRawDataCoreService { @@ -463,6 +507,7 @@ class AppleHealthRawDataCoreService {
463 await _saveLocalNotificationDebugEvent( 507 await _saveLocalNotificationDebugEvent(
464 <String, Object?>{ 508 <String, Object?>{
465 'event': 'flutterNotificationLatestDataTimeChecked', 509 'event': 'flutterNotificationLatestDataTimeChecked',
  510 + 'identifier': 'flutter.notification.latest_data_checked',
466 'user_id': result.userId, 511 'user_id': result.userId,
467 'record': _localNotificationRecordPayload(record), 512 'record': _localNotificationRecordPayload(record),
468 'latest_data_time': candidateData.latestDataTimePayload, 513 'latest_data_time': candidateData.latestDataTimePayload,
@@ -475,6 +520,7 @@ class AppleHealthRawDataCoreService { @@ -475,6 +520,7 @@ class AppleHealthRawDataCoreService {
475 await _saveLocalNotificationDebugEvent( 520 await _saveLocalNotificationDebugEvent(
476 <String, Object?>{ 521 <String, Object?>{
477 'event': 'flutterNotificationNotBuilt', 522 'event': 'flutterNotificationNotBuilt',
  523 + 'identifier': 'flutter.notification.not_built',
478 'reason': 'empty_calculation_result', 524 'reason': 'empty_calculation_result',
479 'user_id': result.userId, 525 'user_id': result.userId,
480 }, 526 },
@@ -522,6 +568,7 @@ class AppleHealthRawDataCoreService { @@ -522,6 +568,7 @@ class AppleHealthRawDataCoreService {
522 await _saveLocalNotificationDebugEvent( 568 await _saveLocalNotificationDebugEvent(
523 <String, Object?>{ 569 <String, Object?>{
524 'event': 'flutterNotificationBuildDecisions', 570 'event': 'flutterNotificationBuildDecisions',
  571 + 'identifier': 'flutter.notification.selection_result',
525 'user_id': candidateResult.userId, 572 'user_id': candidateResult.userId,
526 'decisions': _localNotificationDecisionPayloads( 573 'decisions': _localNotificationDecisionPayloads(
527 result: notificationResult, 574 result: notificationResult,
@@ -542,6 +589,7 @@ class AppleHealthRawDataCoreService { @@ -542,6 +589,7 @@ class AppleHealthRawDataCoreService {
542 await _saveLocalNotificationDebugEvent( 589 await _saveLocalNotificationDebugEvent(
543 <String, Object?>{ 590 <String, Object?>{
544 'event': 'flutterNotificationBuildFinished', 591 'event': 'flutterNotificationBuildFinished',
  592 + 'identifier': 'flutter.notification.selection_finished',
545 'user_id': candidateResult.userId, 593 'user_id': candidateResult.userId,
546 'notification_count': notifications.length, 594 'notification_count': notifications.length,
547 'notifications': notifications.map(_localNotificationPayload).toList(), 595 'notifications': notifications.map(_localNotificationPayload).toList(),
@@ -551,6 +599,7 @@ class AppleHealthRawDataCoreService { @@ -551,6 +599,7 @@ class AppleHealthRawDataCoreService {
551 await _saveLocalNotificationDebugEvent( 599 await _saveLocalNotificationDebugEvent(
552 <String, Object?>{ 600 <String, Object?>{
553 'event': 'flutterNotificationNotSent', 601 'event': 'flutterNotificationNotSent',
  602 + 'identifier': 'flutter.notification.not_triggered',
554 'reason': 'no_built_notifications', 603 'reason': 'no_built_notifications',
555 'user_id': candidateResult.userId, 604 'user_id': candidateResult.userId,
556 }, 605 },
@@ -563,6 +612,7 @@ class AppleHealthRawDataCoreService { @@ -563,6 +612,7 @@ class AppleHealthRawDataCoreService {
563 await _saveLocalNotificationDebugEvent( 612 await _saveLocalNotificationDebugEvent(
564 <String, Object?>{ 613 <String, Object?>{
565 'event': 'flutterNotificationSendStart', 614 'event': 'flutterNotificationSendStart',
  615 + 'identifier': 'flutter.notification.triggered',
566 'user_id': candidateResult.userId, 616 'user_id': candidateResult.userId,
567 'notification': _localNotificationPayload(notification), 617 'notification': _localNotificationPayload(notification),
568 }, 618 },
@@ -574,6 +624,7 @@ class AppleHealthRawDataCoreService { @@ -574,6 +624,7 @@ class AppleHealthRawDataCoreService {
574 await _saveLocalNotificationDebugEvent( 624 await _saveLocalNotificationDebugEvent(
575 <String, Object?>{ 625 <String, Object?>{
576 'event': 'flutterNotificationSendFinished', 626 'event': 'flutterNotificationSendFinished',
  627 + 'identifier': 'flutter.notification.trigger_finished',
577 'user_id': candidateResult.userId, 628 'user_id': candidateResult.userId,
578 'sent': sent, 629 'sent': sent,
579 'reason': sent ? null : 'native_or_dispatcher_returned_false', 630 'reason': sent ? null : 'native_or_dispatcher_returned_false',
@@ -1325,6 +1376,11 @@ class AppleHealthRawDataCoreService { @@ -1325,6 +1376,11 @@ class AppleHealthRawDataCoreService {
1325 if (!_uploadResultsAfterCalculation) { 1376 if (!_uploadResultsAfterCalculation) {
1326 return; 1377 return;
1327 } 1378 }
  1379 + unawaited(_saveLocalNotificationDebugEvent(<String, Object?>{
  1380 + 'event': 'flutterCalculationResultUploadTriggered',
  1381 + 'identifier': 'flutter.calculation_result_upload.triggered',
  1382 + 'upload_action': action,
  1383 + }));
1328 unawaited( 1384 unawaited(
1329 (() async { 1385 (() async {
1330 try { 1386 try {
@@ -1505,14 +1561,21 @@ class AppleHealthRawDataCoreService { @@ -1505,14 +1561,21 @@ class AppleHealthRawDataCoreService {
1505 1561
1506 Future<void> _saveLocalNotificationDebugEvent( 1562 Future<void> _saveLocalNotificationDebugEvent(
1507 Map<String, Object?> event, 1563 Map<String, Object?> event,
1508 - ) async {  
1509 - try {  
1510 - await _localNotificationDebugStore.append(event);  
1511 - await _saveNativeLocalNotificationDebugEvent(event);  
1512 - } catch (error, stackTrace) {  
1513 - _logError(  
1514 - 'save local notification debug event failed', error, stackTrace);  
1515 - } 1564 + ) {
  1565 + final pipelineEvent = <String, Object?>{
  1566 + ...event,
  1567 + 'engine_type': _executionEngineType,
  1568 + };
  1569 + unawaited(() async {
  1570 + try {
  1571 + await _saveNativeLocalNotificationDebugEvent(pipelineEvent).timeout(
  1572 + const Duration(seconds: 1),
  1573 + );
  1574 + } catch (error, stackTrace) {
  1575 + _logError('save health pipeline event failed', error, stackTrace);
  1576 + }
  1577 + }());
  1578 + return Future<void>.value();
1516 } 1579 }
1517 1580
1518 Future<bool> _saveNativeLocalNotificationDebugEvent( 1581 Future<bool> _saveNativeLocalNotificationDebugEvent(
@@ -12,6 +12,7 @@ import 'core/services/raw_data_service/health_raw_data_core_service.dart'; @@ -12,6 +12,7 @@ import 'core/services/raw_data_service/health_raw_data_core_service.dart';
12 import 'core/services/raw_data_service/health_raw_models.dart'; 12 import 'core/services/raw_data_service/health_raw_models.dart';
13 import 'core/theme/app_theme.dart'; 13 import 'core/theme/app_theme.dart';
14 import 'data/local/user_preferences_storage.dart'; 14 import 'data/local/user_preferences_storage.dart';
  15 +import 'pigeon/health_kit_raw_data_api.g.dart';
15 16
16 Future<void> main() async { 17 Future<void> main() async {
17 runZonedGuarded<Future<Null>>( 18 runZonedGuarded<Future<Null>>(
@@ -77,6 +78,7 @@ Future<void> _runPendingHealthTasks( @@ -77,6 +78,7 @@ Future<void> _runPendingHealthTasks(
77 channel, 78 channel,
78 'tasks.received count=${tasks.length} ids=${tasks.map((e) => e.taskId).join(',')}', 79 'tasks.received count=${tasks.length} ids=${tasks.map((e) => e.taskId).join(',')}',
79 ); 80 );
  81 + await _recordTemporaryHealthTaskReceived(tasks);
80 if (tasks.isEmpty) return; 82 if (tasks.isEmpty) return;
81 83
82 for (final task in tasks) { 84 for (final task in tasks) {
@@ -133,6 +135,25 @@ Future<void> _runPendingHealthTasks( @@ -133,6 +135,25 @@ Future<void> _runPendingHealthTasks(
133 } 135 }
134 } 136 }
135 137
  138 +Future<void> _recordTemporaryHealthTaskReceived(
  139 + List<_BackgroundHealthTask> tasks,
  140 +) async {
  141 + try {
  142 + await HealthKitRawDataHostApi().saveLocalNotificationDebugEvent(
  143 + event: <String, Object?>{
  144 + 'event': 'flutterHealthDataReceived',
  145 + 'identifier': 'flutter.health_data.received',
  146 + 'engine_type': 'temporary',
  147 + 'task_ids': tasks.map((task) => task.taskId).toList(growable: false),
  148 + 'data_types': tasks.expand((task) => task.dataTypes).toSet().toList()
  149 + ..sort(),
  150 + },
  151 + ).timeout(const Duration(seconds: 1));
  152 + } catch (_) {
  153 + // Pipeline telemetry must never delay a HealthKit background task.
  154 + }
  155 +}
  156 +
136 bool _hasNewHealthCalculationResult(HealthRawStressCalculationResult result) { 157 bool _hasNewHealthCalculationResult(HealthRawStressCalculationResult result) {
137 return result.hrvStressPoints.isNotEmpty || 158 return result.hrvStressPoints.isNotEmpty ||
138 result.realtimeStressPoints.isNotEmpty || 159 result.realtimeStressPoints.isNotEmpty ||
@@ -164,13 +185,22 @@ List<_BackgroundHealthTask> _backgroundHealthTasksFromArguments( @@ -164,13 +185,22 @@ List<_BackgroundHealthTask> _backgroundHealthTasksFromArguments(
164 } 185 }
165 186
166 class _BackgroundHealthTask { 187 class _BackgroundHealthTask {
167 - const _BackgroundHealthTask({required this.taskId}); 188 + const _BackgroundHealthTask({
  189 + required this.taskId,
  190 + required this.dataTypes,
  191 + });
168 192
169 final String taskId; 193 final String taskId;
  194 + final List<int> dataTypes;
170 195
171 static _BackgroundHealthTask? fromMap(Map<Object?, Object?> map) { 196 static _BackgroundHealthTask? fromMap(Map<Object?, Object?> map) {
172 final taskId = map['taskId']; 197 final taskId = map['taskId'];
173 if (taskId is! String || taskId.isEmpty) return null; 198 if (taskId is! String || taskId.isEmpty) return null;
174 - return _BackgroundHealthTask(taskId: taskId); 199 + final dataTypes = (map['dataTypes'] as Iterable<Object?>?)
  200 + ?.whereType<num>()
  201 + .map((value) => value.toInt())
  202 + .toList(growable: false) ??
  203 + const <int>[];
  204 + return _BackgroundHealthTask(taskId: taskId, dataTypes: dataTypes);
175 } 205 }
176 } 206 }