Commit 471b4a3cd14d042534fcc15ea8b981fb72cffacd

Authored by 权海
1 parent 83c5279f

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();
@@ -54,6 +56,7 @@ abstract final class AppBootstrap { @@ -54,6 +56,7 @@ abstract final class AppBootstrap {
54 56
55 static Future<void> initForBackground() async { 57 static Future<void> initForBackground() async {
56 WidgetsFlutterBinding.ensureInitialized(); 58 WidgetsFlutterBinding.ensureInitialized();
  59 + AppleHealthRawDataCoreService.setExecutionEngineType('temporary');
57 AppLogger.init(); 60 AppLogger.init();
58 61
59 await UserAgentProvider.init(); 62 await UserAgentProvider.init();
@@ -33,9 +33,7 @@ class DeveloperOptionsItem { @@ -33,9 +33,7 @@ class DeveloperOptionsItem {
33 enum DeveloperOptionsAction { 33 enum DeveloperOptionsAction {
34 shareAppDiagnosticLog, 34 shareAppDiagnosticLog,
35 shareHealthRawDataDatabase, 35 shareHealthRawDataDatabase,
36 - shareNativeAppleHealthObserverRecord,  
37 - shareFlutterObserverRecord,  
38 - shareLocalNotificationDebugRecord, 36 + shareHealthPipelineRecord,
39 sendTestHrvLocalNotification, 37 sendTestHrvLocalNotification,
40 clearHealthRawDataDatabaseAndUploadLog, 38 clearHealthRawDataDatabaseAndUploadLog,
41 globalEnv, 39 globalEnv,
@@ -82,16 +80,8 @@ class DeveloperOptionsController extends GetxController { @@ -82,16 +80,8 @@ class DeveloperOptionsController extends GetxController {
82 action: DeveloperOptionsAction.shareHealthRawDataDatabase, 80 action: DeveloperOptionsAction.shareHealthRawDataDatabase,
83 ), 81 ),
84 DeveloperOptionsItem( 82 DeveloperOptionsItem(
85 - title: '分享原生响应AppleHealth记录',  
86 - action: DeveloperOptionsAction.shareNativeAppleHealthObserverRecord,  
87 - ),  
88 - DeveloperOptionsItem(  
89 - title: '分享Flutter 响应AppleHealth回调记录',  
90 - action: DeveloperOptionsAction.shareFlutterObserverRecord,  
91 - ),  
92 - DeveloperOptionsItem(  
93 - title: '分享本地推送诊断日志',  
94 - action: DeveloperOptionsAction.shareLocalNotificationDebugRecord, 83 + title: '分享 Health 数据流程日志',
  84 + action: DeveloperOptionsAction.shareHealthPipelineRecord,
95 ), 85 ),
96 DeveloperOptionsItem( 86 DeveloperOptionsItem(
97 title: '测试本地推送 HRV', 87 title: '测试本地推送 HRV',
@@ -117,16 +107,8 @@ class DeveloperOptionsController extends GetxController { @@ -117,16 +107,8 @@ class DeveloperOptionsController extends GetxController {
117 action: DeveloperOptionsAction.shareHealthRawDataDatabase, 107 action: DeveloperOptionsAction.shareHealthRawDataDatabase,
118 ), 108 ),
119 DeveloperOptionsItem( 109 DeveloperOptionsItem(
120 - title: '分享原生响应AppleHealth记录',  
121 - action: DeveloperOptionsAction.shareNativeAppleHealthObserverRecord,  
122 - ),  
123 - DeveloperOptionsItem(  
124 - title: '分享Flutter 响应AppleHealth回调记录',  
125 - action: DeveloperOptionsAction.shareFlutterObserverRecord,  
126 - ),  
127 - DeveloperOptionsItem(  
128 - title: '分享本地推送诊断日志',  
129 - action: DeveloperOptionsAction.shareLocalNotificationDebugRecord, 110 + title: '分享 Health 数据流程日志',
  111 + action: DeveloperOptionsAction.shareHealthPipelineRecord,
130 ), 112 ),
131 DeveloperOptionsItem( 113 DeveloperOptionsItem(
132 title: '清除本地数据库、上传日志', 114 title: '清除本地数据库、上传日志',
@@ -150,14 +132,8 @@ class DeveloperOptionsController extends GetxController { @@ -150,14 +132,8 @@ class DeveloperOptionsController extends GetxController {
150 case DeveloperOptionsAction.shareHealthRawDataDatabase: 132 case DeveloperOptionsAction.shareHealthRawDataDatabase:
151 await shareHealthRawDataDatabase(); 133 await shareHealthRawDataDatabase();
152 break; 134 break;
153 - case DeveloperOptionsAction.shareNativeAppleHealthObserverRecord:  
154 - await shareNativeAppleHealthObserverRecord();  
155 - break;  
156 - case DeveloperOptionsAction.shareFlutterObserverRecord:  
157 - await shareFlutterObserverRecord();  
158 - break;  
159 - case DeveloperOptionsAction.shareLocalNotificationDebugRecord:  
160 - await shareLocalNotificationDebugRecord(); 135 + case DeveloperOptionsAction.shareHealthPipelineRecord:
  136 + await shareHealthPipelineRecord();
161 break; 137 break;
162 case DeveloperOptionsAction.sendTestHrvLocalNotification: 138 case DeveloperOptionsAction.sendTestHrvLocalNotification:
163 await sendTestHrvLocalNotification(); 139 await sendTestHrvLocalNotification();
@@ -234,32 +210,6 @@ class DeveloperOptionsController extends GetxController { @@ -234,32 +210,6 @@ class DeveloperOptionsController extends GetxController {
234 } 210 }
235 } 211 }
236 212
237 - Future<void> shareNativeAppleHealthObserverRecord() async {  
238 - try {  
239 - await _healthRawDataCoreService.shareNativeAppleHealthObserverRecord();  
240 - } catch (error, stackTrace) {  
241 - AppLogger.e(  
242 - 'Share native AppleHealthObserver record failed',  
243 - error,  
244 - stackTrace,  
245 - );  
246 - Get.snackbar('Share failed', error.toString());  
247 - }  
248 - }  
249 -  
250 - Future<void> shareFlutterObserverRecord() async {  
251 - try {  
252 - await _healthRawDataCoreService.shareFlutterObserverRecord();  
253 - } catch (error, stackTrace) {  
254 - AppLogger.e(  
255 - 'Share Flutter Observer-Caculate record failed',  
256 - error,  
257 - stackTrace,  
258 - );  
259 - Get.snackbar('Share failed', error.toString());  
260 - }  
261 - }  
262 -  
263 Future<void> shareUploadTaskRecord() async { 213 Future<void> shareUploadTaskRecord() async {
264 try { 214 try {
265 await _healthRawDataCoreService.shareUploadTaskRecord(); 215 await _healthRawDataCoreService.shareUploadTaskRecord();
@@ -273,17 +223,15 @@ class DeveloperOptionsController extends GetxController { @@ -273,17 +223,15 @@ class DeveloperOptionsController extends GetxController {
273 } 223 }
274 } 224 }
275 225
276 - Future<void> shareLocalNotificationDebugRecord() async { 226 + Future<void> shareHealthPipelineRecord() async {
277 try { 227 try {
278 final sharedByNative = 228 final sharedByNative =
279 await _healthRawDataCoreService.shareLocalNotificationDebugRecord(); 229 await _healthRawDataCoreService.shareLocalNotificationDebugRecord();
280 if (sharedByNative) return; 230 if (sharedByNative) return;
281 - final text =  
282 - await _healthRawDataCoreService.readLocalNotificationDebugLogText();  
283 - await _platformHostApi.shareText(text); 231 + Get.snackbar('Share failed', 'Health data pipeline log is unavailable');
284 } catch (error, stackTrace) { 232 } catch (error, stackTrace) {
285 AppLogger.e( 233 AppLogger.e(
286 - 'Share local notification debug record failed', 234 + 'Share health pipeline record failed',
287 error, 235 error,
288 stackTrace, 236 stackTrace,
289 ); 237 );
@@ -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(
@@ -25,6 +25,11 @@ import '../health_sleep_calculator.dart'; @@ -25,6 +25,11 @@ import '../health_sleep_calculator.dart';
25 class AppleHealthRawDataCoreService { 25 class AppleHealthRawDataCoreService {
26 static const int defaultLookbackDays = 183; 26 static const int defaultLookbackDays = 183;
27 static const int defaultReadChunkDays = 7; 27 static const int defaultReadChunkDays = 7;
  28 + static String _executionEngineType = 'main';
  29 +
  30 + static void setExecutionEngineType(String value) {
  31 + _executionEngineType = value;
  32 + }
28 33
29 AppleHealthRawDataCoreService({ 34 AppleHealthRawDataCoreService({
30 HealthKitHostApi? healthApi, 35 HealthKitHostApi? healthApi,
@@ -108,6 +113,11 @@ class AppleHealthRawDataCoreService { @@ -108,6 +113,11 @@ class AppleHealthRawDataCoreService {
108 _healthDataUpdatedController.stream; 113 _healthDataUpdatedController.stream;
109 114
110 Future<bool> onHealthDataUpdated({List<int>? dataTypes}) async { 115 Future<bool> onHealthDataUpdated({List<int>? dataTypes}) async {
  116 + await _saveLocalNotificationDebugEvent(<String, Object?>{
  117 + 'event': 'flutterHealthDataReceived',
  118 + 'identifier': 'flutter.health_data.received',
  119 + 'data_types': dataTypes ?? const <int>[],
  120 + });
111 await _saveNativeCallFlutterChange(dataTypes); 121 await _saveNativeCallFlutterChange(dataTypes);
112 if (_healthDataUpdatedCalculation != null) { 122 if (_healthDataUpdatedCalculation != null) {
113 _pendingHealthDataUpdatedTypes = _mergeHealthDataTypes( 123 _pendingHealthDataUpdatedTypes = _mergeHealthDataTypes(
@@ -283,6 +293,11 @@ class AppleHealthRawDataCoreService { @@ -283,6 +293,11 @@ class AppleHealthRawDataCoreService {
283 } 293 }
284 294
285 final userId = _userId; 295 final userId = _userId;
  296 + await _saveLocalNotificationDebugEvent(<String, Object?>{
  297 + 'event': 'flutterCalculationStarted',
  298 + 'identifier': 'flutter.calculation.started',
  299 + 'user_id': userId,
  300 + });
286 final hasAuthorization = await _hasHealthReadAuthorization(); 301 final hasAuthorization = await _hasHealthReadAuthorization();
287 if (!hasAuthorization) { 302 if (!hasAuthorization) {
288 return HealthRawStressCalculationResult( 303 return HealthRawStressCalculationResult(
@@ -393,6 +408,21 @@ class AppleHealthRawDataCoreService { @@ -393,6 +408,21 @@ class AppleHealthRawDataCoreService {
393 ), 408 ),
394 ); 409 );
395 await _localStore.upsertResult(newResult); 410 await _localStore.upsertResult(newResult);
  411 + await _saveLocalNotificationDebugEvent(<String, Object?>{
  412 + 'event': 'flutterCalculationResultStored',
  413 + 'identifier': 'flutter.calculation.result_stored',
  414 + 'user_id': userId,
  415 + 'hrv_count': newResult.hrvStressPoints.length,
  416 + 'realtime_count': newResult.realtimeStressPoints.length,
  417 + 'latest_hrv': _latestHrvStressPointPayload(newResult.hrvStressPoints),
  418 + 'latest_realtime':
  419 + _latestRealtimeStressPointPayload(newResult.realtimeStressPoints),
  420 + });
  421 + await _sendLocalNotificationsAfterCalculation(
  422 + result: newResult,
  423 + hasExistingHrv: latestHrvRawEndTime != null,
  424 + hasExistingSleep: latestSleepResultTime != null,
  425 + );
396 _scheduleResultUpload('upload hrv results', _uploadHrvResults); 426 _scheduleResultUpload('upload hrv results', _uploadHrvResults);
397 _scheduleResultUpload( 427 _scheduleResultUpload(
398 'upload realtime stress results', 428 'upload realtime stress results',
@@ -418,7 +448,10 @@ class AppleHealthRawDataCoreService { @@ -418,7 +448,10 @@ class AppleHealthRawDataCoreService {
418 sleepResults: sleepResults, 448 sleepResults: sleepResults,
419 ); 449 );
420 await _sendLocalNotificationsAfterCalculation( 450 await _sendLocalNotificationsAfterCalculation(
421 - result: storedResult, 451 + result: storedResult.copyWith(
  452 + hrvStressPoints: const <HealthRawHrvStressPoint>[],
  453 + realtimeStressPoints: const <HealthRawRealtimeStressPoint>[],
  454 + ),
422 hasExistingHrv: latestHrvRawEndTime != null, 455 hasExistingHrv: latestHrvRawEndTime != null,
423 hasExistingSleep: latestSleepResultTime != null, 456 hasExistingSleep: latestSleepResultTime != null,
424 ); 457 );
@@ -426,6 +459,15 @@ class AppleHealthRawDataCoreService { @@ -426,6 +459,15 @@ class AppleHealthRawDataCoreService {
426 _logInfo( 459 _logInfo(
427 'calculateEndAt=${_formatDateTimeMilliseconds(calculateFinishedAt)}', 460 'calculateEndAt=${_formatDateTimeMilliseconds(calculateFinishedAt)}',
428 ); 461 );
  462 + await _saveLocalNotificationDebugEvent(<String, Object?>{
  463 + 'event': 'flutterCalculationFinished',
  464 + 'identifier': 'flutter.calculation.finished',
  465 + 'user_id': userId,
  466 + 'hrv_count': storedResult.hrvStressPoints.length,
  467 + 'realtime_count': storedResult.realtimeStressPoints.length,
  468 + 'daily_stress_count': storedResult.dailyStressPoints.length,
  469 + 'sleep_count': storedResult.sleepResults.length,
  470 + });
429 return storedResult; 471 return storedResult;
430 } 472 }
431 473
@@ -437,6 +479,7 @@ class AppleHealthRawDataCoreService { @@ -437,6 +479,7 @@ class AppleHealthRawDataCoreService {
437 await _saveLocalNotificationDebugEvent( 479 await _saveLocalNotificationDebugEvent(
438 <String, Object?>{ 480 <String, Object?>{
439 'event': 'flutterNotificationCalculationFinished', 481 'event': 'flutterNotificationCalculationFinished',
  482 + 'identifier': 'flutter.notification.calculation_finished',
440 'user_id': result.userId, 483 'user_id': result.userId,
441 'has_existing_hrv': hasExistingHrv, 484 'has_existing_hrv': hasExistingHrv,
442 'has_existing_sleep': hasExistingSleep, 485 'has_existing_sleep': hasExistingSleep,
@@ -453,6 +496,7 @@ class AppleHealthRawDataCoreService { @@ -453,6 +496,7 @@ class AppleHealthRawDataCoreService {
453 await _saveLocalNotificationDebugEvent( 496 await _saveLocalNotificationDebugEvent(
454 <String, Object?>{ 497 <String, Object?>{
455 'event': 'flutterNotificationRecordRead', 498 'event': 'flutterNotificationRecordRead',
  499 + 'identifier': 'flutter.notification.record_read',
456 'user_id': result.userId, 500 'user_id': result.userId,
457 'record': _localNotificationRecordPayload(record), 501 'record': _localNotificationRecordPayload(record),
458 }, 502 },
@@ -464,6 +508,7 @@ class AppleHealthRawDataCoreService { @@ -464,6 +508,7 @@ class AppleHealthRawDataCoreService {
464 await _saveLocalNotificationDebugEvent( 508 await _saveLocalNotificationDebugEvent(
465 <String, Object?>{ 509 <String, Object?>{
466 'event': 'flutterNotificationLatestDataTimeChecked', 510 'event': 'flutterNotificationLatestDataTimeChecked',
  511 + 'identifier': 'flutter.notification.latest_data_checked',
467 'user_id': result.userId, 512 'user_id': result.userId,
468 'record': _localNotificationRecordPayload(record), 513 'record': _localNotificationRecordPayload(record),
469 'latest_data_time': candidateData.latestDataTimePayload, 514 'latest_data_time': candidateData.latestDataTimePayload,
@@ -476,6 +521,7 @@ class AppleHealthRawDataCoreService { @@ -476,6 +521,7 @@ class AppleHealthRawDataCoreService {
476 await _saveLocalNotificationDebugEvent( 521 await _saveLocalNotificationDebugEvent(
477 <String, Object?>{ 522 <String, Object?>{
478 'event': 'flutterNotificationNotBuilt', 523 'event': 'flutterNotificationNotBuilt',
  524 + 'identifier': 'flutter.notification.not_built',
479 'reason': 'empty_calculation_result', 525 'reason': 'empty_calculation_result',
480 'user_id': result.userId, 526 'user_id': result.userId,
481 }, 527 },
@@ -523,6 +569,7 @@ class AppleHealthRawDataCoreService { @@ -523,6 +569,7 @@ class AppleHealthRawDataCoreService {
523 await _saveLocalNotificationDebugEvent( 569 await _saveLocalNotificationDebugEvent(
524 <String, Object?>{ 570 <String, Object?>{
525 'event': 'flutterNotificationBuildDecisions', 571 'event': 'flutterNotificationBuildDecisions',
  572 + 'identifier': 'flutter.notification.selection_result',
526 'user_id': candidateResult.userId, 573 'user_id': candidateResult.userId,
527 'decisions': _localNotificationDecisionPayloads( 574 'decisions': _localNotificationDecisionPayloads(
528 result: notificationResult, 575 result: notificationResult,
@@ -543,6 +590,7 @@ class AppleHealthRawDataCoreService { @@ -543,6 +590,7 @@ class AppleHealthRawDataCoreService {
543 await _saveLocalNotificationDebugEvent( 590 await _saveLocalNotificationDebugEvent(
544 <String, Object?>{ 591 <String, Object?>{
545 'event': 'flutterNotificationBuildFinished', 592 'event': 'flutterNotificationBuildFinished',
  593 + 'identifier': 'flutter.notification.selection_finished',
546 'user_id': candidateResult.userId, 594 'user_id': candidateResult.userId,
547 'notification_count': notifications.length, 595 'notification_count': notifications.length,
548 'notifications': notifications.map(_localNotificationPayload).toList(), 596 'notifications': notifications.map(_localNotificationPayload).toList(),
@@ -552,6 +600,7 @@ class AppleHealthRawDataCoreService { @@ -552,6 +600,7 @@ class AppleHealthRawDataCoreService {
552 await _saveLocalNotificationDebugEvent( 600 await _saveLocalNotificationDebugEvent(
553 <String, Object?>{ 601 <String, Object?>{
554 'event': 'flutterNotificationNotSent', 602 'event': 'flutterNotificationNotSent',
  603 + 'identifier': 'flutter.notification.not_triggered',
555 'reason': 'no_built_notifications', 604 'reason': 'no_built_notifications',
556 'user_id': candidateResult.userId, 605 'user_id': candidateResult.userId,
557 }, 606 },
@@ -564,6 +613,7 @@ class AppleHealthRawDataCoreService { @@ -564,6 +613,7 @@ class AppleHealthRawDataCoreService {
564 await _saveLocalNotificationDebugEvent( 613 await _saveLocalNotificationDebugEvent(
565 <String, Object?>{ 614 <String, Object?>{
566 'event': 'flutterNotificationSendStart', 615 'event': 'flutterNotificationSendStart',
  616 + 'identifier': 'flutter.notification.triggered',
567 'user_id': candidateResult.userId, 617 'user_id': candidateResult.userId,
568 'notification': _localNotificationPayload(notification), 618 'notification': _localNotificationPayload(notification),
569 }, 619 },
@@ -575,6 +625,7 @@ class AppleHealthRawDataCoreService { @@ -575,6 +625,7 @@ class AppleHealthRawDataCoreService {
575 await _saveLocalNotificationDebugEvent( 625 await _saveLocalNotificationDebugEvent(
576 <String, Object?>{ 626 <String, Object?>{
577 'event': 'flutterNotificationSendFinished', 627 'event': 'flutterNotificationSendFinished',
  628 + 'identifier': 'flutter.notification.trigger_finished',
578 'user_id': candidateResult.userId, 629 'user_id': candidateResult.userId,
579 'sent': sent, 630 'sent': sent,
580 'reason': sent ? null : 'native_or_dispatcher_returned_false', 631 'reason': sent ? null : 'native_or_dispatcher_returned_false',
@@ -1326,6 +1377,11 @@ class AppleHealthRawDataCoreService { @@ -1326,6 +1377,11 @@ class AppleHealthRawDataCoreService {
1326 if (!_uploadResultsAfterCalculation) { 1377 if (!_uploadResultsAfterCalculation) {
1327 return; 1378 return;
1328 } 1379 }
  1380 + unawaited(_saveLocalNotificationDebugEvent(<String, Object?>{
  1381 + 'event': 'flutterCalculationResultUploadTriggered',
  1382 + 'identifier': 'flutter.calculation_result_upload.triggered',
  1383 + 'upload_action': action,
  1384 + }));
1329 unawaited( 1385 unawaited(
1330 (() async { 1386 (() async {
1331 try { 1387 try {
@@ -1506,14 +1562,21 @@ class AppleHealthRawDataCoreService { @@ -1506,14 +1562,21 @@ class AppleHealthRawDataCoreService {
1506 1562
1507 Future<void> _saveLocalNotificationDebugEvent( 1563 Future<void> _saveLocalNotificationDebugEvent(
1508 Map<String, Object?> event, 1564 Map<String, Object?> event,
1509 - ) async {  
1510 - try {  
1511 - await _localNotificationDebugStore.append(event);  
1512 - await _saveNativeLocalNotificationDebugEvent(event);  
1513 - } catch (error, stackTrace) {  
1514 - _logError(  
1515 - 'save local notification debug event failed', error, stackTrace);  
1516 - } 1565 + ) {
  1566 + final pipelineEvent = <String, Object?>{
  1567 + ...event,
  1568 + 'engine_type': _executionEngineType,
  1569 + };
  1570 + unawaited(() async {
  1571 + try {
  1572 + await _saveNativeLocalNotificationDebugEvent(pipelineEvent).timeout(
  1573 + const Duration(seconds: 1),
  1574 + );
  1575 + } catch (error, stackTrace) {
  1576 + _logError('save health pipeline event failed', error, stackTrace);
  1577 + }
  1578 + }());
  1579 + return Future<void>.value();
1517 } 1580 }
1518 1581
1519 Future<bool> _saveNativeLocalNotificationDebugEvent( 1582 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 }