|
@@ -16,7 +16,6 @@ import '../../../logging/app_logger.dart'; |
|
@@ -16,7 +16,6 @@ import '../../../logging/app_logger.dart'; |
|
16
|
import '../../../network/api/health_api.dart';
|
16
|
import '../../../network/api/health_api.dart';
|
|
17
|
import '../../../result/app_result.dart';
|
17
|
import '../../../result/app_result.dart';
|
|
18
|
import '../health_raw_models.dart';
|
18
|
import '../health_raw_models.dart';
|
|
19
|
-import 'apple_health_raw_local_notification_debug_store.dart';
|
|
|
|
20
|
import 'apple_health_raw_local_notification.dart';
|
19
|
import 'apple_health_raw_local_notification.dart';
|
|
21
|
import '../health_raw_stress_calculator.dart';
|
20
|
import '../health_raw_stress_calculator.dart';
|
|
22
|
import '../health_sleep_calculator.dart';
|
21
|
import '../health_sleep_calculator.dart';
|
|
@@ -38,7 +37,6 @@ class AppleHealthRawDataCoreService { |
|
@@ -38,7 +37,6 @@ class AppleHealthRawDataCoreService { |
|
38
|
int Function()? userIdProvider,
|
37
|
int Function()? userIdProvider,
|
|
39
|
bool uploadResultsAfterCalculation = true,
|
38
|
bool uploadResultsAfterCalculation = true,
|
|
40
|
HealthRawLocalNotificationDispatcher? localNotificationDispatcher,
|
39
|
HealthRawLocalNotificationDispatcher? localNotificationDispatcher,
|
|
41
|
- HealthRawLocalNotificationDebugStore? localNotificationDebugStore,
|
|
|
|
42
|
HealthApi? serverHealthApi,
|
40
|
HealthApi? serverHealthApi,
|
|
43
|
}) : _healthApi = AppHealthKitHostApi(api: healthApi),
|
41
|
}) : _healthApi = AppHealthKitHostApi(api: healthApi),
|
|
44
|
_rawDataApi = AppHealthKitRawDataHostApi(api: rawDataApi),
|
42
|
_rawDataApi = AppHealthKitRawDataHostApi(api: rawDataApi),
|
|
@@ -48,8 +46,6 @@ class AppleHealthRawDataCoreService { |
|
@@ -48,8 +46,6 @@ class AppleHealthRawDataCoreService { |
|
48
|
_uploadResultsAfterCalculation = uploadResultsAfterCalculation,
|
46
|
_uploadResultsAfterCalculation = uploadResultsAfterCalculation,
|
|
49
|
_localNotificationDispatcher = localNotificationDispatcher ??
|
47
|
_localNotificationDispatcher = localNotificationDispatcher ??
|
|
50
|
HealthRawLocalNotificationDispatcher(),
|
48
|
HealthRawLocalNotificationDispatcher(),
|
|
51
|
- _localNotificationDebugStore = localNotificationDebugStore ??
|
|
|
|
52
|
- HealthRawLocalNotificationDebugStore(),
|
|
|
|
53
|
_serverHealthApi = serverHealthApi;
|
49
|
_serverHealthApi = serverHealthApi;
|
|
54
|
|
50
|
|
|
55
|
final AppHealthKitHostApi _healthApi;
|
51
|
final AppHealthKitHostApi _healthApi;
|
|
@@ -59,7 +55,6 @@ class AppleHealthRawDataCoreService { |
|
@@ -59,7 +55,6 @@ class AppleHealthRawDataCoreService { |
|
59
|
final int Function()? _userIdProvider;
|
55
|
final int Function()? _userIdProvider;
|
|
60
|
final bool _uploadResultsAfterCalculation;
|
56
|
final bool _uploadResultsAfterCalculation;
|
|
61
|
final HealthRawLocalNotificationDispatcher _localNotificationDispatcher;
|
57
|
final HealthRawLocalNotificationDispatcher _localNotificationDispatcher;
|
|
62
|
- final HealthRawLocalNotificationDebugStore _localNotificationDebugStore;
|
|
|
|
63
|
final HealthApi? _serverHealthApi;
|
58
|
final HealthApi? _serverHealthApi;
|
|
64
|
final StreamController<HealthRawDataUpdatedEvent>
|
59
|
final StreamController<HealthRawDataUpdatedEvent>
|
|
65
|
_healthDataUpdatedController =
|
60
|
_healthDataUpdatedController =
|
|
@@ -218,7 +213,7 @@ class AppleHealthRawDataCoreService { |
|
@@ -218,7 +213,7 @@ class AppleHealthRawDataCoreService { |
|
218
|
}
|
213
|
}
|
|
219
|
|
214
|
|
|
220
|
Future<String> readLocalNotificationDebugLogText() {
|
215
|
Future<String> readLocalNotificationDebugLogText() {
|
|
221
|
- return _localNotificationDebugStore.readText();
|
216
|
+ return Future.value('');
|
|
222
|
}
|
217
|
}
|
|
223
|
|
218
|
|
|
224
|
Future<bool> shareLocalNotificationDebugRecord() async {
|
219
|
Future<bool> shareLocalNotificationDebugRecord() async {
|
|
@@ -536,6 +531,12 @@ class AppleHealthRawDataCoreService { |
|
@@ -536,6 +531,12 @@ class AppleHealthRawDataCoreService { |
|
536
|
);
|
531
|
);
|
|
537
|
var notificationResult = candidateResult;
|
532
|
var notificationResult = candidateResult;
|
|
538
|
var realtimeWindow = const <HealthRawRealtimeStressPoint>[];
|
533
|
var realtimeWindow = const <HealthRawRealtimeStressPoint>[];
|
|
|
|
534
|
+ final lastRealtimeStressPushSendTime =
|
|
|
|
535
|
+ await _localStore.latestRealtimeStressPushSendTime(
|
|
|
|
536
|
+ candidateResult.userId,
|
|
|
|
537
|
+ );
|
|
|
|
538
|
+ final notificationBuildTime = _unixSecondsNow();
|
|
|
|
539
|
+ var realtimeSleepIntervals = const <HealthRawSleepInterval>[];
|
|
539
|
if (latestRealtimeStressPoint != null) {
|
540
|
if (latestRealtimeStressPoint != null) {
|
|
540
|
final shouldResetRealtimeStressPushTime =
|
541
|
final shouldResetRealtimeStressPushTime =
|
|
541
|
latestRealtimeStressPoint.isWorkout ||
|
542
|
latestRealtimeStressPoint.isWorkout ||
|
|
@@ -550,10 +551,6 @@ class AppleHealthRawDataCoreService { |
|
@@ -550,10 +551,6 @@ class AppleHealthRawDataCoreService { |
|
550
|
_realtimeStressPointPayload(latestRealtimeStressPoint),
|
551
|
_realtimeStressPointPayload(latestRealtimeStressPoint),
|
|
551
|
},
|
552
|
},
|
|
552
|
);
|
553
|
);
|
|
553
|
- await _recordRealtimeStressTimeSafely(
|
|
|
|
554
|
- userId: candidateResult.userId,
|
|
|
|
555
|
- recordTime: latestRealtimeStressPoint.rawEndTime,
|
|
|
|
556
|
- );
|
|
|
|
557
|
notificationResult = candidateResult.copyWith(
|
554
|
notificationResult = candidateResult.copyWith(
|
|
558
|
realtimeStressPoints: const <HealthRawRealtimeStressPoint>[],
|
555
|
realtimeStressPoints: const <HealthRawRealtimeStressPoint>[],
|
|
559
|
);
|
556
|
);
|
|
@@ -562,6 +559,11 @@ class AppleHealthRawDataCoreService { |
|
@@ -562,6 +559,11 @@ class AppleHealthRawDataCoreService { |
|
562
|
userId: candidateResult.userId,
|
559
|
userId: candidateResult.userId,
|
|
563
|
latestRawEndTime: latestRealtimeStressPoint.rawEndTime,
|
560
|
latestRawEndTime: latestRealtimeStressPoint.rawEndTime,
|
|
564
|
);
|
561
|
);
|
|
|
|
562
|
+ realtimeSleepIntervals =
|
|
|
|
563
|
+ await _queryRealtimeStressSleepIntervalsForNotificationSafely(
|
|
|
|
564
|
+ userId: candidateResult.userId,
|
|
|
|
565
|
+ latestRawEndTime: latestRealtimeStressPoint.rawEndTime,
|
|
|
|
566
|
+ );
|
|
565
|
}
|
567
|
}
|
|
566
|
}
|
568
|
}
|
|
567
|
|
569
|
|
|
@@ -575,7 +577,10 @@ class AppleHealthRawDataCoreService { |
|
@@ -575,7 +577,10 @@ class AppleHealthRawDataCoreService { |
|
575
|
hasExistingHrv: candidateData.hasExistingHrvDataTime,
|
577
|
hasExistingHrv: candidateData.hasExistingHrvDataTime,
|
|
576
|
hasExistingSleep: candidateData.hasExistingSleepDataTime,
|
578
|
hasExistingSleep: candidateData.hasExistingSleepDataTime,
|
|
577
|
realtimeWindow: realtimeWindow,
|
579
|
realtimeWindow: realtimeWindow,
|
|
|
|
580
|
+ realtimeSleepIntervals: realtimeSleepIntervals,
|
|
578
|
record: record,
|
581
|
record: record,
|
|
|
|
582
|
+ lastRealtimeStressPushSendTime: lastRealtimeStressPushSendTime,
|
|
|
|
583
|
+ notificationBuildTime: notificationBuildTime,
|
|
579
|
),
|
584
|
),
|
|
580
|
},
|
585
|
},
|
|
581
|
);
|
586
|
);
|
|
@@ -584,7 +589,10 @@ class AppleHealthRawDataCoreService { |
|
@@ -584,7 +589,10 @@ class AppleHealthRawDataCoreService { |
|
584
|
hasExistingHrv: candidateData.hasExistingHrvDataTime,
|
589
|
hasExistingHrv: candidateData.hasExistingHrvDataTime,
|
|
585
|
hasExistingSleep: candidateData.hasExistingSleepDataTime,
|
590
|
hasExistingSleep: candidateData.hasExistingSleepDataTime,
|
|
586
|
realtimeWindow: realtimeWindow,
|
591
|
realtimeWindow: realtimeWindow,
|
|
|
|
592
|
+ realtimeSleepIntervals: realtimeSleepIntervals,
|
|
587
|
record: record,
|
593
|
record: record,
|
|
|
|
594
|
+ lastRealtimeStressPushSendTime: lastRealtimeStressPushSendTime,
|
|
|
|
595
|
+ notificationBuildTime: notificationBuildTime,
|
|
588
|
);
|
596
|
);
|
|
589
|
await _saveLocalNotificationDebugEvent(
|
597
|
await _saveLocalNotificationDebugEvent(
|
|
590
|
<String, Object?>{
|
598
|
<String, Object?>{
|
|
@@ -633,6 +641,10 @@ class AppleHealthRawDataCoreService { |
|
@@ -633,6 +641,10 @@ class AppleHealthRawDataCoreService { |
|
633
|
);
|
641
|
);
|
|
634
|
if (sent) {
|
642
|
if (sent) {
|
|
635
|
sentNotifications.add(notification);
|
643
|
sentNotifications.add(notification);
|
|
|
|
644
|
+ await _recordLocalNotificationPushSendTimeSafely(
|
|
|
|
645
|
+ userId: candidateResult.userId,
|
|
|
|
646
|
+ notification: notification,
|
|
|
|
647
|
+ );
|
|
636
|
}
|
648
|
}
|
|
637
|
}
|
649
|
}
|
|
638
|
_scheduleRealtimeStressServerPush(sentNotifications);
|
650
|
_scheduleRealtimeStressServerPush(sentNotifications);
|
|
@@ -758,17 +770,20 @@ class AppleHealthRawDataCoreService { |
|
@@ -758,17 +770,20 @@ class AppleHealthRawDataCoreService { |
|
758
|
}
|
770
|
}
|
|
759
|
}
|
771
|
}
|
|
760
|
|
772
|
|
|
761
|
- Future<void> _recordRealtimeStressTimeSafely({
|
773
|
+ Future<void> _recordLocalNotificationPushSendTimeSafely({
|
|
762
|
required int userId,
|
774
|
required int userId,
|
|
763
|
- required int recordTime,
|
775
|
+ required HealthRawLocalNotification notification,
|
|
764
|
}) async {
|
776
|
}) async {
|
|
765
|
try {
|
777
|
try {
|
|
766
|
- await _localNotificationDispatcher.recordRealtimeStressTime(
|
778
|
+ await _localStore.markLocalNotificationPushed(
|
|
767
|
userId: userId,
|
779
|
userId: userId,
|
|
768
|
- recordTime: recordTime,
|
780
|
+ recordType: notification.recordType,
|
|
|
|
781
|
+ recordTime: notification.recordTime,
|
|
|
|
782
|
+ pushSendTime: _unixSecondsNow(),
|
|
769
|
);
|
783
|
);
|
|
770
|
} catch (error, stackTrace) {
|
784
|
} catch (error, stackTrace) {
|
|
771
|
- _logError('record realtime stress push time failed', error, stackTrace);
|
785
|
+ _logError(
|
|
|
|
786
|
+ 'record local notification push send time failed', error, stackTrace);
|
|
772
|
}
|
787
|
}
|
|
773
|
}
|
788
|
}
|
|
774
|
|
789
|
|
|
@@ -814,6 +829,73 @@ class AppleHealthRawDataCoreService { |
|
@@ -814,6 +829,73 @@ class AppleHealthRawDataCoreService { |
|
814
|
}
|
829
|
}
|
|
815
|
}
|
830
|
}
|
|
816
|
|
831
|
|
|
|
|
832
|
+ Future<List<HealthRawSleepInterval>>
|
|
|
|
833
|
+ _queryRealtimeStressSleepIntervalsForNotificationSafely({
|
|
|
|
834
|
+ required int userId,
|
|
|
|
835
|
+ required int latestRawEndTime,
|
|
|
|
836
|
+ }) async {
|
|
|
|
837
|
+ try {
|
|
|
|
838
|
+ final intervals = await _queryRealtimeStressSleepIntervalsForNotification(
|
|
|
|
839
|
+ userId: userId,
|
|
|
|
840
|
+ latestRawEndTime: latestRawEndTime,
|
|
|
|
841
|
+ );
|
|
|
|
842
|
+ await _saveLocalNotificationDebugEvent(
|
|
|
|
843
|
+ <String, Object?>{
|
|
|
|
844
|
+ 'event': 'flutterRealtimeStressSleepIntervalsQueried',
|
|
|
|
845
|
+ 'user_id': userId,
|
|
|
|
846
|
+ 'latest_raw_end_time': latestRawEndTime,
|
|
|
|
847
|
+ 'sleep_intervals': intervals.map(_sleepIntervalPayload).toList(),
|
|
|
|
848
|
+ },
|
|
|
|
849
|
+ );
|
|
|
|
850
|
+ return intervals;
|
|
|
|
851
|
+ } catch (error, stackTrace) {
|
|
|
|
852
|
+ _logError(
|
|
|
|
853
|
+ 'query realtime stress sleep intervals failed',
|
|
|
|
854
|
+ error,
|
|
|
|
855
|
+ stackTrace,
|
|
|
|
856
|
+ );
|
|
|
|
857
|
+ final fallback = [_defaultSleepIntervalForUnixSeconds(latestRawEndTime)];
|
|
|
|
858
|
+ await _saveLocalNotificationDebugEvent(
|
|
|
|
859
|
+ <String, Object?>{
|
|
|
|
860
|
+ 'event': 'flutterRealtimeStressSleepIntervalsQueryFailed',
|
|
|
|
861
|
+ 'user_id': userId,
|
|
|
|
862
|
+ 'latest_raw_end_time': latestRawEndTime,
|
|
|
|
863
|
+ 'sleep_intervals': fallback.map(_sleepIntervalPayload).toList(),
|
|
|
|
864
|
+ 'error': error.toString(),
|
|
|
|
865
|
+ 'stack_trace': stackTrace.toString(),
|
|
|
|
866
|
+ },
|
|
|
|
867
|
+ );
|
|
|
|
868
|
+ return fallback;
|
|
|
|
869
|
+ }
|
|
|
|
870
|
+ }
|
|
|
|
871
|
+
|
|
|
|
872
|
+ Future<List<HealthRawSleepInterval>>
|
|
|
|
873
|
+ _queryRealtimeStressSleepIntervalsForNotification({
|
|
|
|
874
|
+ required int userId,
|
|
|
|
875
|
+ required int latestRawEndTime,
|
|
|
|
876
|
+ }) async {
|
|
|
|
877
|
+ final dayRange = _dayRangeFromUnixSeconds(latestRawEndTime);
|
|
|
|
878
|
+ final sleepResults = await _localStore.querySleepResults(
|
|
|
|
879
|
+ userId: userId,
|
|
|
|
880
|
+ startTime: dayRange.$1,
|
|
|
|
881
|
+ endTime: dayRange.$2,
|
|
|
|
882
|
+ );
|
|
|
|
883
|
+ final intervals = sleepResults
|
|
|
|
884
|
+ .where((result) => result.startDate <= result.date)
|
|
|
|
885
|
+ .map(
|
|
|
|
886
|
+ (result) => (
|
|
|
|
887
|
+ startTime: result.startDate,
|
|
|
|
888
|
+ endTime: result.date,
|
|
|
|
889
|
+ ),
|
|
|
|
890
|
+ )
|
|
|
|
891
|
+ .toList()
|
|
|
|
892
|
+ ..sort((a, b) => a.startTime.compareTo(b.startTime));
|
|
|
|
893
|
+ if (intervals.isNotEmpty) {
|
|
|
|
894
|
+ return intervals;
|
|
|
|
895
|
+ }
|
|
|
|
896
|
+ return [_defaultSleepIntervalForUnixSeconds(latestRawEndTime)];
|
|
|
|
897
|
+ }
|
|
|
|
898
|
+
|
|
817
|
Future<bool> _sendLocalNotificationSafely({
|
899
|
Future<bool> _sendLocalNotificationSafely({
|
|
818
|
required int userId,
|
900
|
required int userId,
|
|
819
|
required HealthRawLocalNotification notification,
|
901
|
required HealthRawLocalNotification notification,
|
|
@@ -843,7 +925,10 @@ class AppleHealthRawDataCoreService { |
|
@@ -843,7 +925,10 @@ class AppleHealthRawDataCoreService { |
|
843
|
required bool hasExistingHrv,
|
925
|
required bool hasExistingHrv,
|
|
844
|
required bool hasExistingSleep,
|
926
|
required bool hasExistingSleep,
|
|
845
|
required List<HealthRawRealtimeStressPoint> realtimeWindow,
|
927
|
required List<HealthRawRealtimeStressPoint> realtimeWindow,
|
|
|
|
928
|
+ required List<HealthRawSleepInterval> realtimeSleepIntervals,
|
|
846
|
required HealthRawLocalNotificationRecord record,
|
929
|
required HealthRawLocalNotificationRecord record,
|
|
|
|
930
|
+ required int? lastRealtimeStressPushSendTime,
|
|
|
|
931
|
+ required int notificationBuildTime,
|
|
847
|
}) {
|
932
|
}) {
|
|
848
|
return <Map<String, Object?>>[
|
933
|
return <Map<String, Object?>>[
|
|
849
|
_sleepNotificationDecisionPayload(
|
934
|
_sleepNotificationDecisionPayload(
|
|
@@ -858,7 +943,10 @@ class AppleHealthRawDataCoreService { |
|
@@ -858,7 +943,10 @@ class AppleHealthRawDataCoreService { |
|
858
|
_realtimeStressNotificationDecisionPayload(
|
943
|
_realtimeStressNotificationDecisionPayload(
|
|
859
|
result.realtimeStressPoints,
|
944
|
result.realtimeStressPoints,
|
|
860
|
realtimeWindow: realtimeWindow,
|
945
|
realtimeWindow: realtimeWindow,
|
|
|
|
946
|
+ realtimeSleepIntervals: realtimeSleepIntervals,
|
|
861
|
record: record,
|
947
|
record: record,
|
|
|
|
948
|
+ lastPushSendTime: lastRealtimeStressPushSendTime,
|
|
|
|
949
|
+ notificationBuildTime: notificationBuildTime,
|
|
862
|
),
|
950
|
),
|
|
863
|
];
|
951
|
];
|
|
864
|
}
|
952
|
}
|
|
@@ -936,20 +1024,23 @@ class AppleHealthRawDataCoreService { |
|
@@ -936,20 +1024,23 @@ class AppleHealthRawDataCoreService { |
|
936
|
}
|
1024
|
}
|
|
937
|
final sorted = [...hrvPoints]
|
1025
|
final sorted = [...hrvPoints]
|
|
938
|
..sort((a, b) => a.rawEndTime.compareTo(b.rawEndTime));
|
1026
|
..sort((a, b) => a.rawEndTime.compareTo(b.rawEndTime));
|
|
939
|
- final latest = sorted.last;
|
|
|
|
940
|
return <String, Object?>{
|
1027
|
return <String, Object?>{
|
|
941
|
'type': HealthRawLocalNotificationRecordType.hrv.name,
|
1028
|
'type': HealthRawLocalNotificationRecordType.hrv.name,
|
|
942
|
'will_build': true,
|
1029
|
'will_build': true,
|
|
943
|
'reason': 'candidate',
|
1030
|
'reason': 'candidate',
|
|
944
|
'last_hrv_time': record.lastHrvTime,
|
1031
|
'last_hrv_time': record.lastHrvTime,
|
|
945
|
- 'latest_hrv': _hrvStressPointPayload(latest),
|
1032
|
+ 'candidate_count': sorted.length,
|
|
|
|
1033
|
+ 'latest_hrv': _hrvStressPointPayload(sorted.last),
|
|
946
|
};
|
1034
|
};
|
|
947
|
}
|
1035
|
}
|
|
948
|
|
1036
|
|
|
949
|
Map<String, Object?> _realtimeStressNotificationDecisionPayload(
|
1037
|
Map<String, Object?> _realtimeStressNotificationDecisionPayload(
|
|
950
|
List<HealthRawRealtimeStressPoint> realtimePoints, {
|
1038
|
List<HealthRawRealtimeStressPoint> realtimePoints, {
|
|
951
|
required List<HealthRawRealtimeStressPoint> realtimeWindow,
|
1039
|
required List<HealthRawRealtimeStressPoint> realtimeWindow,
|
|
|
|
1040
|
+ required List<HealthRawSleepInterval> realtimeSleepIntervals,
|
|
952
|
required HealthRawLocalNotificationRecord record,
|
1041
|
required HealthRawLocalNotificationRecord record,
|
|
|
|
1042
|
+ required int? lastPushSendTime,
|
|
|
|
1043
|
+ required int notificationBuildTime,
|
|
953
|
}) {
|
1044
|
}) {
|
|
954
|
if (realtimePoints.isEmpty) {
|
1045
|
if (realtimePoints.isEmpty) {
|
|
955
|
return <String, Object?>{
|
1046
|
return <String, Object?>{
|
|
@@ -969,6 +1060,19 @@ class AppleHealthRawDataCoreService { |
|
@@ -969,6 +1060,19 @@ class AppleHealthRawDataCoreService { |
|
969
|
'latest_realtime': _realtimeStressPointPayload(latest),
|
1060
|
'latest_realtime': _realtimeStressPointPayload(latest),
|
|
970
|
};
|
1061
|
};
|
|
971
|
}
|
1062
|
}
|
|
|
|
1063
|
+ final matchedSleepInterval =
|
|
|
|
1064
|
+ _sleepIntervalContaining(latest.rawEndTime, realtimeSleepIntervals);
|
|
|
|
1065
|
+ if (matchedSleepInterval != null) {
|
|
|
|
1066
|
+ return <String, Object?>{
|
|
|
|
1067
|
+ 'type': HealthRawLocalNotificationRecordType.realtimeStress.name,
|
|
|
|
1068
|
+ 'will_build': false,
|
|
|
|
1069
|
+ 'reason': 'sleep_interval',
|
|
|
|
1070
|
+ 'matched_sleep_interval': _sleepIntervalPayload(matchedSleepInterval),
|
|
|
|
1071
|
+ 'sleep_intervals':
|
|
|
|
1072
|
+ realtimeSleepIntervals.map(_sleepIntervalPayload).toList(),
|
|
|
|
1073
|
+ 'latest_realtime': _realtimeStressPointPayload(latest),
|
|
|
|
1074
|
+ };
|
|
|
|
1075
|
+ }
|
|
972
|
final valid = realtimeWindow
|
1076
|
final valid = realtimeWindow
|
|
973
|
.where((e) => e.result >= 1 && e.result <= 100)
|
1077
|
.where((e) => e.result >= 1 && e.result <= 100)
|
|
974
|
.toList()
|
1078
|
.toList()
|
|
@@ -990,14 +1094,15 @@ class AppleHealthRawDataCoreService { |
|
@@ -990,14 +1094,15 @@ class AppleHealthRawDataCoreService { |
|
990
|
'latest_realtime': _realtimeStressPointPayload(latest),
|
1094
|
'latest_realtime': _realtimeStressPointPayload(latest),
|
|
991
|
};
|
1095
|
};
|
|
992
|
}
|
1096
|
}
|
|
993
|
- final lastPushTime = record.lastRealtimeStressTime;
|
1097
|
+ final lastPushTime = lastPushSendTime;
|
|
994
|
if (lastPushTime != null &&
|
1098
|
if (lastPushTime != null &&
|
|
995
|
- latest.rawEndTime - lastPushTime < Duration.secondsPerHour) {
|
1099
|
+ notificationBuildTime - lastPushTime < Duration.secondsPerHour) {
|
|
996
|
return <String, Object?>{
|
1100
|
return <String, Object?>{
|
|
997
|
'type': HealthRawLocalNotificationRecordType.realtimeStress.name,
|
1101
|
'type': HealthRawLocalNotificationRecordType.realtimeStress.name,
|
|
998
|
'will_build': false,
|
1102
|
'will_build': false,
|
|
999
|
'reason': 'within_realtime_interval',
|
1103
|
'reason': 'within_realtime_interval',
|
|
1000
|
- 'last_realtime_stress_time': lastPushTime,
|
1104
|
+ 'last_realtime_stress_push_send_time': lastPushTime,
|
|
|
|
1105
|
+ 'notification_build_time': notificationBuildTime,
|
|
1001
|
'latest_realtime': _realtimeStressPointPayload(latest),
|
1106
|
'latest_realtime': _realtimeStressPointPayload(latest),
|
|
1002
|
};
|
1107
|
};
|
|
1003
|
}
|
1108
|
}
|
|
@@ -1005,12 +1110,36 @@ class AppleHealthRawDataCoreService { |
|
@@ -1005,12 +1110,36 @@ class AppleHealthRawDataCoreService { |
|
1005
|
'type': HealthRawLocalNotificationRecordType.realtimeStress.name,
|
1110
|
'type': HealthRawLocalNotificationRecordType.realtimeStress.name,
|
|
1006
|
'will_build': true,
|
1111
|
'will_build': true,
|
|
1007
|
'reason': 'candidate',
|
1112
|
'reason': 'candidate',
|
|
1008
|
- 'last_realtime_stress_time': lastPushTime,
|
1113
|
+ 'last_realtime_stress_push_send_time': lastPushTime,
|
|
|
|
1114
|
+ 'notification_build_time': notificationBuildTime,
|
|
1009
|
'valid_window_count': valid.length,
|
1115
|
'valid_window_count': valid.length,
|
|
|
|
1116
|
+ 'sleep_intervals':
|
|
|
|
1117
|
+ realtimeSleepIntervals.map(_sleepIntervalPayload).toList(),
|
|
1010
|
'latest_realtime': _realtimeStressPointPayload(latest),
|
1118
|
'latest_realtime': _realtimeStressPointPayload(latest),
|
|
1011
|
};
|
1119
|
};
|
|
1012
|
}
|
1120
|
}
|
|
1013
|
|
1121
|
|
|
|
|
1122
|
+ HealthRawSleepInterval? _sleepIntervalContaining(
|
|
|
|
1123
|
+ int time,
|
|
|
|
1124
|
+ List<HealthRawSleepInterval> intervals,
|
|
|
|
1125
|
+ ) {
|
|
|
|
1126
|
+ for (final interval in intervals) {
|
|
|
|
1127
|
+ if (time >= interval.startTime && time <= interval.endTime) {
|
|
|
|
1128
|
+ return interval;
|
|
|
|
1129
|
+ }
|
|
|
|
1130
|
+ }
|
|
|
|
1131
|
+ return null;
|
|
|
|
1132
|
+ }
|
|
|
|
1133
|
+
|
|
|
|
1134
|
+ Map<String, Object?> _sleepIntervalPayload(
|
|
|
|
1135
|
+ HealthRawSleepInterval interval,
|
|
|
|
1136
|
+ ) {
|
|
|
|
1137
|
+ return <String, Object?>{
|
|
|
|
1138
|
+ 'start_time': interval.startTime,
|
|
|
|
1139
|
+ 'end_time': interval.endTime,
|
|
|
|
1140
|
+ };
|
|
|
|
1141
|
+ }
|
|
|
|
1142
|
+
|
|
1014
|
Map<String, Object?> _localNotificationPayload(
|
1143
|
Map<String, Object?> _localNotificationPayload(
|
|
1015
|
HealthRawLocalNotification notification,
|
1144
|
HealthRawLocalNotification notification,
|
|
1016
|
) {
|
1145
|
) {
|
|
@@ -1805,6 +1934,10 @@ class AppleHealthRawDataCoreService { |
|
@@ -1805,6 +1934,10 @@ class AppleHealthRawDataCoreService { |
|
1805
|
return DateTime(date.year, date.month, date.day);
|
1934
|
return DateTime(date.year, date.month, date.day);
|
|
1806
|
}
|
1935
|
}
|
|
1807
|
|
1936
|
|
|
|
|
1937
|
+ static (int startTime, int endTime) _dayRangeFromUnixSeconds(int seconds) {
|
|
|
|
1938
|
+ return _dayRangeFromDateKey(_dateKeyFromUnixSeconds(seconds));
|
|
|
|
1939
|
+ }
|
|
|
|
1940
|
+
|
|
1808
|
static (int startTime, int endTime) _dayRangeFromDateKey(int dateKey) {
|
1941
|
static (int startTime, int endTime) _dayRangeFromDateKey(int dateKey) {
|
|
1809
|
final year = dateKey ~/ 10000;
|
1942
|
final year = dateKey ~/ 10000;
|
|
1810
|
final month = (dateKey ~/ 100) % 100;
|
1943
|
final month = (dateKey ~/ 100) % 100;
|
|
@@ -1816,6 +1949,16 @@ class AppleHealthRawDataCoreService { |
|
@@ -1816,6 +1949,16 @@ class AppleHealthRawDataCoreService { |
|
1816
|
);
|
1949
|
);
|
|
1817
|
}
|
1950
|
}
|
|
1818
|
|
1951
|
|
|
|
|
1952
|
+ static HealthRawSleepInterval _defaultSleepIntervalForUnixSeconds(
|
|
|
|
1953
|
+ int seconds,
|
|
|
|
1954
|
+ ) {
|
|
|
|
1955
|
+ final dayRange = _dayRangeFromUnixSeconds(seconds);
|
|
|
|
1956
|
+ return (
|
|
|
|
1957
|
+ startTime: dayRange.$1,
|
|
|
|
1958
|
+ endTime: dayRange.$1 + 6 * 60 * 60,
|
|
|
|
1959
|
+ );
|
|
|
|
1960
|
+ }
|
|
|
|
1961
|
+
|
|
1819
|
static String _formatDateTimeMilliseconds(DateTime dateTime) {
|
1962
|
static String _formatDateTimeMilliseconds(DateTime dateTime) {
|
|
1820
|
String two(int value) => value.toString().padLeft(2, '0');
|
1963
|
String two(int value) => value.toString().padLeft(2, '0');
|
|
1821
|
String three(int value) => value.toString().padLeft(3, '0');
|
1964
|
String three(int value) => value.toString().padLeft(3, '0');
|
|
@@ -1914,6 +2057,8 @@ class _HealthRawLocalNotificationCandidateData { |
|
@@ -1914,6 +2057,8 @@ class _HealthRawLocalNotificationCandidateData { |
|
1914
|
|
2057
|
|
|
1915
|
double _integerDouble(num value) => value.toInt().toDouble();
|
2058
|
double _integerDouble(num value) => value.toInt().toDouble();
|
|
1916
|
|
2059
|
|
|
|
|
2060
|
+int _unixSecondsNow() => DateTime.now().millisecondsSinceEpoch ~/ 1000;
|
|
|
|
2061
|
+
|
|
1917
|
class HealthRawStressLocalStore {
|
2062
|
class HealthRawStressLocalStore {
|
|
1918
|
HealthRawStressLocalStore({
|
2063
|
HealthRawStressLocalStore({
|
|
1919
|
Directory? rootDirectory,
|
2064
|
Directory? rootDirectory,
|
|
@@ -1935,19 +2080,20 @@ class HealthRawStressLocalStore { |
|
@@ -1935,19 +2080,20 @@ class HealthRawStressLocalStore { |
|
1935
|
|
2080
|
|
|
1936
|
Future<void> upsertResult(HealthRawStressCalculationResult result) async {
|
2081
|
Future<void> upsertResult(HealthRawStressCalculationResult result) async {
|
|
1937
|
final db = await _database(result.userId);
|
2082
|
final db = await _database(result.userId);
|
|
|
|
2083
|
+ final updateTime = _currentUnixSeconds();
|
|
1938
|
await db.transaction((txn) async {
|
2084
|
await db.transaction((txn) async {
|
|
1939
|
for (final point in result.hrvStressPoints) {
|
2085
|
for (final point in result.hrvStressPoints) {
|
|
1940
|
await _upsertResettingUploaded(
|
2086
|
await _upsertResettingUploaded(
|
|
1941
|
txn,
|
2087
|
txn,
|
|
1942
|
hrvResultsTable,
|
2088
|
hrvResultsTable,
|
|
1943
|
- _hrvRow(point),
|
2089
|
+ _hrvRow(point, updateTime),
|
|
1944
|
);
|
2090
|
);
|
|
1945
|
}
|
2091
|
}
|
|
1946
|
for (final point in result.realtimeStressPoints) {
|
2092
|
for (final point in result.realtimeStressPoints) {
|
|
1947
|
await _upsertResettingUploaded(
|
2093
|
await _upsertResettingUploaded(
|
|
1948
|
txn,
|
2094
|
txn,
|
|
1949
|
realtimeStressResultsTable,
|
2095
|
realtimeStressResultsTable,
|
|
1950
|
- _realtimeRow(point),
|
2096
|
+ _realtimeRow(point, updateTime),
|
|
1951
|
);
|
2097
|
);
|
|
1952
|
}
|
2098
|
}
|
|
1953
|
});
|
2099
|
});
|
|
@@ -1958,9 +2104,13 @@ class HealthRawStressLocalStore { |
|
@@ -1958,9 +2104,13 @@ class HealthRawStressLocalStore { |
|
1958
|
required Iterable<HealthRawDailyStressPoint> points,
|
2104
|
required Iterable<HealthRawDailyStressPoint> points,
|
|
1959
|
}) async {
|
2105
|
}) async {
|
|
1960
|
final db = await _database(userId);
|
2106
|
final db = await _database(userId);
|
|
|
|
2107
|
+ final updateTime = _currentUnixSeconds();
|
|
1961
|
await db.transaction((txn) async {
|
2108
|
await db.transaction((txn) async {
|
|
1962
|
for (final point in points) {
|
2109
|
for (final point in points) {
|
|
1963
|
- await _upsertDailyStressResettingUploaded(txn, _dailyStressRow(point));
|
2110
|
+ await _upsertDailyStressResettingUploaded(
|
|
|
|
2111
|
+ txn,
|
|
|
|
2112
|
+ _dailyStressRow(point, updateTime),
|
|
|
|
2113
|
+ );
|
|
1964
|
}
|
2114
|
}
|
|
1965
|
});
|
2115
|
});
|
|
1966
|
}
|
2116
|
}
|
|
@@ -1970,9 +2120,13 @@ class HealthRawStressLocalStore { |
|
@@ -1970,9 +2120,13 @@ class HealthRawStressLocalStore { |
|
1970
|
required Iterable<HealthRawSleepResult> results,
|
2120
|
required Iterable<HealthRawSleepResult> results,
|
|
1971
|
}) async {
|
2121
|
}) async {
|
|
1972
|
final db = await _database(userId);
|
2122
|
final db = await _database(userId);
|
|
|
|
2123
|
+ final updateTime = _currentUnixSeconds();
|
|
1973
|
await db.transaction((txn) async {
|
2124
|
await db.transaction((txn) async {
|
|
1974
|
for (final result in results) {
|
2125
|
for (final result in results) {
|
|
1975
|
- await _upsertSleepResultResettingUploaded(txn, _sleepRow(result));
|
2126
|
+ await _upsertSleepResultResettingUploaded(
|
|
|
|
2127
|
+ txn,
|
|
|
|
2128
|
+ _sleepRow(result, updateTime),
|
|
|
|
2129
|
+ );
|
|
1976
|
}
|
2130
|
}
|
|
1977
|
});
|
2131
|
});
|
|
1978
|
}
|
2132
|
}
|
|
@@ -2204,10 +2358,43 @@ class HealthRawStressLocalStore { |
|
@@ -2204,10 +2358,43 @@ class HealthRawStressLocalStore { |
|
2204
|
return _latestRawEndTime(userId, realtimeStressResultsTable);
|
2358
|
return _latestRawEndTime(userId, realtimeStressResultsTable);
|
|
2205
|
}
|
2359
|
}
|
|
2206
|
|
2360
|
|
|
|
|
2361
|
+ Future<int?> latestRealtimeStressPushSendTime(int userId) {
|
|
|
|
2362
|
+ return _latestPushSendTime(userId, realtimeStressResultsTable);
|
|
|
|
2363
|
+ }
|
|
|
|
2364
|
+
|
|
2207
|
Future<int?> earliestRealtimeRawEndTime(int userId) {
|
2365
|
Future<int?> earliestRealtimeRawEndTime(int userId) {
|
|
2208
|
return _earliestRawEndTime(userId, realtimeStressResultsTable);
|
2366
|
return _earliestRawEndTime(userId, realtimeStressResultsTable);
|
|
2209
|
}
|
2367
|
}
|
|
2210
|
|
2368
|
|
|
|
|
2369
|
+ Future<void> markLocalNotificationPushed({
|
|
|
|
2370
|
+ required int userId,
|
|
|
|
2371
|
+ required HealthRawLocalNotificationRecordType recordType,
|
|
|
|
2372
|
+ required int recordTime,
|
|
|
|
2373
|
+ required int pushSendTime,
|
|
|
|
2374
|
+ }) async {
|
|
|
|
2375
|
+ final (table, timeColumn) = switch (recordType) {
|
|
|
|
2376
|
+ HealthRawLocalNotificationRecordType.hrv => (
|
|
|
|
2377
|
+ hrvResultsTable,
|
|
|
|
2378
|
+ 'raw_end_time',
|
|
|
|
2379
|
+ ),
|
|
|
|
2380
|
+ HealthRawLocalNotificationRecordType.realtimeStress => (
|
|
|
|
2381
|
+ realtimeStressResultsTable,
|
|
|
|
2382
|
+ 'raw_end_time',
|
|
|
|
2383
|
+ ),
|
|
|
|
2384
|
+ HealthRawLocalNotificationRecordType.sleep => (
|
|
|
|
2385
|
+ sleepResultsTable,
|
|
|
|
2386
|
+ 'date',
|
|
|
|
2387
|
+ ),
|
|
|
|
2388
|
+ };
|
|
|
|
2389
|
+ final db = await _database(userId);
|
|
|
|
2390
|
+ await db.update(
|
|
|
|
2391
|
+ table,
|
|
|
|
2392
|
+ <String, Object?>{'push_send_time': pushSendTime},
|
|
|
|
2393
|
+ where: '$timeColumn = ?',
|
|
|
|
2394
|
+ whereArgs: [recordTime],
|
|
|
|
2395
|
+ );
|
|
|
|
2396
|
+ }
|
|
|
|
2397
|
+
|
|
2211
|
Future<int?> latestSleepResultTime(int userId) async {
|
2398
|
Future<int?> latestSleepResultTime(int userId) async {
|
|
2212
|
final db = await _database(userId);
|
2399
|
final db = await _database(userId);
|
|
2213
|
final rows = await db.query(
|
2400
|
final rows = await db.query(
|
|
@@ -2347,7 +2534,7 @@ class HealthRawStressLocalStore { |
|
@@ -2347,7 +2534,7 @@ class HealthRawStressLocalStore { |
|
2347
|
final db = await factory.openDatabase(
|
2534
|
final db = await factory.openDatabase(
|
|
2348
|
path,
|
2535
|
path,
|
|
2349
|
options: OpenDatabaseOptions(
|
2536
|
options: OpenDatabaseOptions(
|
|
2350
|
- version: 9,
|
2537
|
+ version: 10,
|
|
2351
|
onCreate: (db, version) async {
|
2538
|
onCreate: (db, version) async {
|
|
2352
|
await _createTables(db);
|
2539
|
await _createTables(db);
|
|
2353
|
},
|
2540
|
},
|
|
@@ -2377,6 +2564,9 @@ class HealthRawStressLocalStore { |
|
@@ -2377,6 +2564,9 @@ class HealthRawStressLocalStore { |
|
2377
|
if (oldVersion < 9) {
|
2564
|
if (oldVersion < 9) {
|
|
2378
|
await _addDateKeyColumns(db);
|
2565
|
await _addDateKeyColumns(db);
|
|
2379
|
}
|
2566
|
}
|
|
|
|
2567
|
+ if (oldVersion < 10) {
|
|
|
|
2568
|
+ await _addUpdateTimeColumns(db);
|
|
|
|
2569
|
+ }
|
|
2380
|
},
|
2570
|
},
|
|
2381
|
),
|
2571
|
),
|
|
2382
|
);
|
2572
|
);
|
|
@@ -2404,7 +2594,9 @@ CREATE TABLE IF NOT EXISTS $hrvResultsTable ( |
|
@@ -2404,7 +2594,9 @@ CREATE TABLE IF NOT EXISTS $hrvResultsTable ( |
|
2404
|
is_sleep_likely INTEGER NOT NULL DEFAULT 0,
|
2594
|
is_sleep_likely INTEGER NOT NULL DEFAULT 0,
|
|
2405
|
is_suspected_activity INTEGER NOT NULL DEFAULT 0,
|
2595
|
is_suspected_activity INTEGER NOT NULL DEFAULT 0,
|
|
2406
|
uploaded INTEGER NOT NULL DEFAULT 0,
|
2596
|
uploaded INTEGER NOT NULL DEFAULT 0,
|
|
2407
|
- update_time INTEGER NOT NULL DEFAULT 0
|
2597
|
+ update_time INTEGER NOT NULL DEFAULT 0,
|
|
|
|
2598
|
+ push_send_time INTEGER,
|
|
|
|
2599
|
+ upload_time INTEGER
|
|
2408
|
)
|
2600
|
)
|
|
2409
|
''');
|
2601
|
''');
|
|
2410
|
await db.execute('''
|
2602
|
await db.execute('''
|
|
@@ -2421,7 +2613,9 @@ CREATE TABLE IF NOT EXISTS $realtimeStressResultsTable ( |
|
@@ -2421,7 +2613,9 @@ CREATE TABLE IF NOT EXISTS $realtimeStressResultsTable ( |
|
2421
|
is_sleep_likely INTEGER NOT NULL DEFAULT 0,
|
2613
|
is_sleep_likely INTEGER NOT NULL DEFAULT 0,
|
|
2422
|
is_suspected_activity INTEGER NOT NULL DEFAULT 0,
|
2614
|
is_suspected_activity INTEGER NOT NULL DEFAULT 0,
|
|
2423
|
uploaded INTEGER NOT NULL DEFAULT 0,
|
2615
|
uploaded INTEGER NOT NULL DEFAULT 0,
|
|
2424
|
- update_time INTEGER NOT NULL DEFAULT 0
|
2616
|
+ update_time INTEGER NOT NULL DEFAULT 0,
|
|
|
|
2617
|
+ push_send_time INTEGER,
|
|
|
|
2618
|
+ upload_time INTEGER
|
|
2425
|
)
|
2619
|
)
|
|
2426
|
''');
|
2620
|
''');
|
|
2427
|
await _createDailyStressTable(db);
|
2621
|
await _createDailyStressTable(db);
|
|
@@ -2439,7 +2633,8 @@ CREATE TABLE IF NOT EXISTS $dailyStressResultsTable ( |
|
@@ -2439,7 +2633,8 @@ CREATE TABLE IF NOT EXISTS $dailyStressResultsTable ( |
|
2439
|
state INTEGER NOT NULL,
|
2633
|
state INTEGER NOT NULL,
|
|
2440
|
data_time INTEGER NOT NULL,
|
2634
|
data_time INTEGER NOT NULL,
|
|
2441
|
uploaded INTEGER NOT NULL DEFAULT 0,
|
2635
|
uploaded INTEGER NOT NULL DEFAULT 0,
|
|
2442
|
- update_time INTEGER NOT NULL DEFAULT 0
|
2636
|
+ update_time INTEGER NOT NULL DEFAULT 0,
|
|
|
|
2637
|
+ upload_time INTEGER
|
|
2443
|
)
|
2638
|
)
|
|
2444
|
''');
|
2639
|
''');
|
|
2445
|
}
|
2640
|
}
|
|
@@ -2457,7 +2652,9 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( |
|
@@ -2457,7 +2652,9 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( |
|
2457
|
awak_minutes INTEGER NOT NULL,
|
2652
|
awak_minutes INTEGER NOT NULL,
|
|
2458
|
sleep_minutes INTEGER NOT NULL,
|
2653
|
sleep_minutes INTEGER NOT NULL,
|
|
2459
|
uploaded INTEGER NOT NULL DEFAULT 0,
|
2654
|
uploaded INTEGER NOT NULL DEFAULT 0,
|
|
2460
|
- update_time INTEGER NOT NULL DEFAULT 0
|
2655
|
+ update_time INTEGER NOT NULL DEFAULT 0,
|
|
|
|
2656
|
+ push_send_time INTEGER,
|
|
|
|
2657
|
+ upload_time INTEGER
|
|
2461
|
)
|
2658
|
)
|
|
2462
|
''');
|
2659
|
''');
|
|
2463
|
}
|
2660
|
}
|
|
@@ -2517,6 +2714,21 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( |
|
@@ -2517,6 +2714,21 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( |
|
2517
|
}
|
2714
|
}
|
|
2518
|
}
|
2715
|
}
|
|
2519
|
|
2716
|
|
|
|
|
2717
|
+ Future<void> _addPushSendTimeColumns(DatabaseExecutor db) async {
|
|
|
|
2718
|
+ for (final table in const [
|
|
|
|
2719
|
+ hrvResultsTable,
|
|
|
|
2720
|
+ realtimeStressResultsTable,
|
|
|
|
2721
|
+ dailyStressResultsTable,
|
|
|
|
2722
|
+ sleepResultsTable,
|
|
|
|
2723
|
+ ]) {
|
|
|
|
2724
|
+ try {
|
|
|
|
2725
|
+ await db.execute('ALTER TABLE $table ADD COLUMN upload_time INTEGER');
|
|
|
|
2726
|
+ } on DatabaseException catch (error) {
|
|
|
|
2727
|
+ if (!error.isDuplicateColumnError()) rethrow;
|
|
|
|
2728
|
+ }
|
|
|
|
2729
|
+ }
|
|
|
|
2730
|
+ }
|
|
|
|
2731
|
+
|
|
2520
|
Future<void> _addFlagColumns(DatabaseExecutor db, String table) async {
|
2732
|
Future<void> _addFlagColumns(DatabaseExecutor db, String table) async {
|
|
2521
|
for (final column in const [
|
2733
|
for (final column in const [
|
|
2522
|
'is_workout',
|
2734
|
'is_workout',
|
|
@@ -2600,6 +2812,19 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( |
|
@@ -2600,6 +2812,19 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( |
|
2600
|
return rows.first['raw_end_time'] as int;
|
2812
|
return rows.first['raw_end_time'] as int;
|
|
2601
|
}
|
2813
|
}
|
|
2602
|
|
2814
|
|
|
|
|
2815
|
+ Future<int?> _latestPushSendTime(int userId, String table) async {
|
|
|
|
2816
|
+ final db = await _database(userId);
|
|
|
|
2817
|
+ final rows = await db.query(
|
|
|
|
2818
|
+ table,
|
|
|
|
2819
|
+ columns: ['push_send_time'],
|
|
|
|
2820
|
+ where: 'push_send_time IS NOT NULL',
|
|
|
|
2821
|
+ orderBy: 'push_send_time DESC',
|
|
|
|
2822
|
+ limit: 1,
|
|
|
|
2823
|
+ );
|
|
|
|
2824
|
+ if (rows.isEmpty) return null;
|
|
|
|
2825
|
+ return rows.first['push_send_time'] as int?;
|
|
|
|
2826
|
+ }
|
|
|
|
2827
|
+
|
|
2603
|
Future<int?> _earliestRawEndTime(int userId, String table) async {
|
2828
|
Future<int?> _earliestRawEndTime(int userId, String table) async {
|
|
2604
|
final db = await _database(userId);
|
2829
|
final db = await _database(userId);
|
|
2605
|
final rows = await db.query(
|
2830
|
final rows = await db.query(
|
|
@@ -2620,9 +2845,10 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( |
|
@@ -2620,9 +2845,10 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( |
|
2620
|
final times = rawEndTimes.toList();
|
2845
|
final times = rawEndTimes.toList();
|
|
2621
|
if (times.isEmpty) return;
|
2846
|
if (times.isEmpty) return;
|
|
2622
|
final db = await _database(userId);
|
2847
|
final db = await _database(userId);
|
|
|
|
2848
|
+ final uploadTime = _currentUnixSeconds();
|
|
2623
|
await db.update(
|
2849
|
await db.update(
|
|
2624
|
table,
|
2850
|
table,
|
|
2625
|
- {'uploaded': 1},
|
2851
|
+ {'uploaded': 1, 'upload_time': uploadTime},
|
|
2626
|
where: 'raw_end_time IN (${List.filled(times.length, '?').join(',')})',
|
2852
|
where: 'raw_end_time IN (${List.filled(times.length, '?').join(',')})',
|
|
2627
|
whereArgs: times,
|
2853
|
whereArgs: times,
|
|
2628
|
);
|
2854
|
);
|
|
@@ -2636,9 +2862,10 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( |
|
@@ -2636,9 +2862,10 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( |
|
2636
|
}) async {
|
2862
|
}) async {
|
|
2637
|
if (time <= 0) return;
|
2863
|
if (time <= 0) return;
|
|
2638
|
final db = await _database(userId);
|
2864
|
final db = await _database(userId);
|
|
|
|
2865
|
+ final uploadTime = _currentUnixSeconds();
|
|
2639
|
await db.update(
|
2866
|
await db.update(
|
|
2640
|
table,
|
2867
|
table,
|
|
2641
|
- {'uploaded': 1},
|
2868
|
+ {'uploaded': 1, 'upload_time': uploadTime},
|
|
2642
|
where: '$timeColumn <= ? AND uploaded != 1',
|
2869
|
where: '$timeColumn <= ? AND uploaded != 1',
|
|
2643
|
whereArgs: [time],
|
2870
|
whereArgs: [time],
|
|
2644
|
);
|
2871
|
);
|
|
@@ -2668,7 +2895,10 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( |
|
@@ -2668,7 +2895,10 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( |
|
2668
|
return date.year * 10000 + date.month * 100 + date.day;
|
2895
|
return date.year * 10000 + date.month * 100 + date.day;
|
|
2669
|
}
|
2896
|
}
|
|
2670
|
|
2897
|
|
|
2671
|
- Map<String, Object?> _hrvRow(HealthRawHrvStressPoint point) {
|
2898
|
+ Map<String, Object?> _hrvRow(
|
|
|
|
2899
|
+ HealthRawHrvStressPoint point,
|
|
|
|
2900
|
+ int updateTime,
|
|
|
|
2901
|
+ ) {
|
|
2672
|
return <String, Object?>{
|
2902
|
return <String, Object?>{
|
|
2673
|
'raw_end_time': point.rawEndTime,
|
2903
|
'raw_end_time': point.rawEndTime,
|
|
2674
|
'date_key': _dateKeyFromUnixSeconds(point.rawEndTime),
|
2904
|
'date_key': _dateKeyFromUnixSeconds(point.rawEndTime),
|
|
@@ -2684,11 +2914,15 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( |
|
@@ -2684,11 +2914,15 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( |
|
2684
|
'baseline_resting_hr': point.baselineRestingHr,
|
2914
|
'baseline_resting_hr': point.baselineRestingHr,
|
|
2685
|
..._flagsRow(point.flags),
|
2915
|
..._flagsRow(point.flags),
|
|
2686
|
'uploaded': point.uploaded ? 1 : 0,
|
2916
|
'uploaded': point.uploaded ? 1 : 0,
|
|
2687
|
- 'update_time': _currentUnixSeconds(),
|
2917
|
+ 'update_time': updateTime,
|
|
|
|
2918
|
+ 'upload_time': point.uploadTime,
|
|
2688
|
};
|
2919
|
};
|
|
2689
|
}
|
2920
|
}
|
|
2690
|
|
2921
|
|
|
2691
|
- Map<String, Object?> _realtimeRow(HealthRawRealtimeStressPoint point) {
|
2922
|
+ Map<String, Object?> _realtimeRow(
|
|
|
|
2923
|
+ HealthRawRealtimeStressPoint point,
|
|
|
|
2924
|
+ int updateTime,
|
|
|
|
2925
|
+ ) {
|
|
2692
|
return <String, Object?>{
|
2926
|
return <String, Object?>{
|
|
2693
|
'raw_end_time': point.rawEndTime,
|
2927
|
'raw_end_time': point.rawEndTime,
|
|
2694
|
'date_key': _dateKeyFromUnixSeconds(point.rawEndTime),
|
2928
|
'date_key': _dateKeyFromUnixSeconds(point.rawEndTime),
|
|
@@ -2699,11 +2933,15 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( |
|
@@ -2699,11 +2933,15 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( |
|
2699
|
'source_end_time': point.sourceEndTime,
|
2933
|
'source_end_time': point.sourceEndTime,
|
|
2700
|
..._flagsRow(point.flags),
|
2934
|
..._flagsRow(point.flags),
|
|
2701
|
'uploaded': point.uploaded ? 1 : 0,
|
2935
|
'uploaded': point.uploaded ? 1 : 0,
|
|
2702
|
- 'update_time': _currentUnixSeconds(),
|
2936
|
+ 'update_time': updateTime,
|
|
|
|
2937
|
+ 'upload_time': point.uploadTime,
|
|
2703
|
};
|
2938
|
};
|
|
2704
|
}
|
2939
|
}
|
|
2705
|
|
2940
|
|
|
2706
|
- Map<String, Object?> _dailyStressRow(HealthRawDailyStressPoint point) {
|
2941
|
+ Map<String, Object?> _dailyStressRow(
|
|
|
|
2942
|
+ HealthRawDailyStressPoint point,
|
|
|
|
2943
|
+ int updateTime,
|
|
|
|
2944
|
+ ) {
|
|
2707
|
return <String, Object?>{
|
2945
|
return <String, Object?>{
|
|
2708
|
'date': point.date,
|
2946
|
'date': point.date,
|
|
2709
|
'date_key': point.date,
|
2947
|
'date_key': point.date,
|
|
@@ -2713,11 +2951,12 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( |
|
@@ -2713,11 +2951,12 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( |
|
2713
|
'state': point.state.value,
|
2951
|
'state': point.state.value,
|
|
2714
|
'data_time': point.dataTime,
|
2952
|
'data_time': point.dataTime,
|
|
2715
|
'uploaded': point.uploaded ? 1 : 0,
|
2953
|
'uploaded': point.uploaded ? 1 : 0,
|
|
2716
|
- 'update_time': _currentUnixSeconds(),
|
2954
|
+ 'update_time': updateTime,
|
|
|
|
2955
|
+ 'upload_time': point.uploadTime,
|
|
2717
|
};
|
2956
|
};
|
|
2718
|
}
|
2957
|
}
|
|
2719
|
|
2958
|
|
|
2720
|
- Map<String, Object?> _sleepRow(HealthRawSleepResult result) {
|
2959
|
+ Map<String, Object?> _sleepRow(HealthRawSleepResult result, int updateTime) {
|
|
2721
|
return <String, Object?>{
|
2960
|
return <String, Object?>{
|
|
2722
|
'date': result.date,
|
2961
|
'date': result.date,
|
|
2723
|
'date_key': _dateKeyFromUnixSeconds(result.date),
|
2962
|
'date_key': _dateKeyFromUnixSeconds(result.date),
|
|
@@ -2729,7 +2968,8 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( |
|
@@ -2729,7 +2968,8 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( |
|
2729
|
'awak_minutes': result.awakMinutes,
|
2968
|
'awak_minutes': result.awakMinutes,
|
|
2730
|
'sleep_minutes': result.sleepMinutes,
|
2969
|
'sleep_minutes': result.sleepMinutes,
|
|
2731
|
'uploaded': result.uploaded ? 1 : 0,
|
2970
|
'uploaded': result.uploaded ? 1 : 0,
|
|
2732
|
- 'update_time': _currentUnixSeconds(),
|
2971
|
+ 'update_time': updateTime,
|
|
|
|
2972
|
+ 'upload_time': result.uploadTime,
|
|
2733
|
};
|
2973
|
};
|
|
2734
|
}
|
2974
|
}
|
|
2735
|
|
2975
|
|
|
@@ -2798,6 +3038,7 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( |
|
@@ -2798,6 +3038,7 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( |
|
2798
|
'is_sleep_likely': row['is_sleep_likely'],
|
3038
|
'is_sleep_likely': row['is_sleep_likely'],
|
|
2799
|
'is_suspected_activity': row['is_suspected_activity'],
|
3039
|
'is_suspected_activity': row['is_suspected_activity'],
|
|
2800
|
'uploaded': uploadPayloadChanged ? 0 : existingRow['uploaded'],
|
3040
|
'uploaded': uploadPayloadChanged ? 0 : existingRow['uploaded'],
|
|
|
|
3041
|
+ 'upload_time': uploadPayloadChanged ? null : existingRow['upload_time'],
|
|
2801
|
'update_time': row['update_time'],
|
3042
|
'update_time': row['update_time'],
|
|
2802
|
},
|
3043
|
},
|
|
2803
|
where: 'raw_end_time = ?',
|
3044
|
where: 'raw_end_time = ?',
|
|
@@ -2832,6 +3073,7 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( |
|
@@ -2832,6 +3073,7 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( |
|
2832
|
final isToday = row['date'] == _todayDateKey();
|
3073
|
final isToday = row['date'] == _todayDateKey();
|
|
2833
|
if (isToday) {
|
3074
|
if (isToday) {
|
|
2834
|
row['uploaded'] = 0;
|
3075
|
row['uploaded'] = 0;
|
|
|
|
3076
|
+ row['upload_time'] = null;
|
|
2835
|
}
|
3077
|
}
|
|
2836
|
final existing = await db.query(
|
3078
|
final existing = await db.query(
|
|
2837
|
dailyStressResultsTable,
|
3079
|
dailyStressResultsTable,
|
|
@@ -2884,6 +3126,8 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( |
|
@@ -2884,6 +3126,8 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( |
|
2884
|
'state': row['state'],
|
3126
|
'state': row['state'],
|
|
2885
|
'data_time': row['data_time'],
|
3127
|
'data_time': row['data_time'],
|
|
2886
|
'uploaded': isToday ? 0 : (valueChanged ? 0 : existingRow['uploaded']),
|
3128
|
'uploaded': isToday ? 0 : (valueChanged ? 0 : existingRow['uploaded']),
|
|
|
|
3129
|
+ 'upload_time':
|
|
|
|
3130
|
+ isToday || valueChanged ? null : existingRow['upload_time'],
|
|
2887
|
'update_time': row['update_time'],
|
3131
|
'update_time': row['update_time'],
|
|
2888
|
},
|
3132
|
},
|
|
2889
|
where: 'date = ?',
|
3133
|
where: 'date = ?',
|
|
@@ -2945,6 +3189,7 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( |
|
@@ -2945,6 +3189,7 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( |
|
2945
|
'awak_minutes': row['awak_minutes'],
|
3189
|
'awak_minutes': row['awak_minutes'],
|
|
2946
|
'sleep_minutes': row['sleep_minutes'],
|
3190
|
'sleep_minutes': row['sleep_minutes'],
|
|
2947
|
'uploaded': valueChanged ? 0 : existingRow['uploaded'],
|
3191
|
'uploaded': valueChanged ? 0 : existingRow['uploaded'],
|
|
|
|
3192
|
+ 'upload_time': valueChanged ? null : existingRow['upload_time'],
|
|
2948
|
'update_time': row['update_time'],
|
3193
|
'update_time': row['update_time'],
|
|
2949
|
},
|
3194
|
},
|
|
2950
|
where: 'date = ?',
|
3195
|
where: 'date = ?',
|