Commit 08bbfa570ffc016e902c1f9e9ef7670a2f016b04

Authored by 权海
1 parent 0c4b2326

feat(ui):计算修改

@@ -368,6 +368,8 @@ protocol HealthKitRawDataHostApi { @@ -368,6 +368,8 @@ protocol HealthKitRawDataHostApi {
368 func hasHealthData(completion: @escaping (Result<Bool, Error>) -> Void) 368 func hasHealthData(completion: @escaping (Result<Bool, Error>) -> Void)
369 /// 从AppleHealth 中读取原始数据 369 /// 从AppleHealth 中读取原始数据
370 func getHealthKitRawData(dataType: Int64, startTime: Int64, endTime: Int64, completion: @escaping (Result<[HealthKitRawDataPoint], Error>) -> Void) 370 func getHealthKitRawData(dataType: Int64, startTime: Int64, endTime: Int64, completion: @escaping (Result<[HealthKitRawDataPoint], Error>) -> Void)
  371 + /// 原生数据上传
  372 + func performHealthDataUpload(completion: @escaping (Result<Bool, Error>) -> Void)
371 /// 从从AppleHealth中读取睡眠数据 373 /// 从从AppleHealth中读取睡眠数据
372 func getHealthKitRawSleepData(startTime: Int64, endTime: Int64, completion: @escaping (Result<[HealthKitRawSleepDataPoint], Error>) -> Void) 374 func getHealthKitRawSleepData(startTime: Int64, endTime: Int64, completion: @escaping (Result<[HealthKitRawSleepDataPoint], Error>) -> Void)
373 /// 从从AppleHealth中读取活动统计数据 375 /// 从从AppleHealth中读取活动统计数据
@@ -420,6 +422,22 @@ class HealthKitRawDataHostApiSetup { @@ -420,6 +422,22 @@ class HealthKitRawDataHostApiSetup {
420 } else { 422 } else {
421 getHealthKitRawDataChannel.setMessageHandler(nil) 423 getHealthKitRawDataChannel.setMessageHandler(nil)
422 } 424 }
  425 + /// 原生数据上传
  426 + let performHealthDataUploadChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.doublefeel_flutter.HealthKitRawDataHostApi.performHealthDataUpload\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
  427 + if let api = api {
  428 + performHealthDataUploadChannel.setMessageHandler { _, reply in
  429 + api.performHealthDataUpload { result in
  430 + switch result {
  431 + case .success(let res):
  432 + reply(wrapResult(res))
  433 + case .failure(let error):
  434 + reply(wrapError(error))
  435 + }
  436 + }
  437 + }
  438 + } else {
  439 + performHealthDataUploadChannel.setMessageHandler(nil)
  440 + }
423 /// 从从AppleHealth中读取睡眠数据 441 /// 从从AppleHealth中读取睡眠数据
424 let getHealthKitRawSleepDataChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.doublefeel_flutter.HealthKitRawDataHostApi.getHealthKitRawSleepData\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) 442 let getHealthKitRawSleepDataChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.doublefeel_flutter.HealthKitRawDataHostApi.getHealthKitRawSleepData\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
425 if let api = api { 443 if let api = api {
@@ -1224,13 +1224,10 @@ class HealthRawPointFlags { @@ -1224,13 +1224,10 @@ class HealthRawPointFlags {
1224 isSleepLikely && !isWorkout && !isWorkoutRecovery; 1224 isSleepLikely && !isWorkout && !isWorkoutRecovery;
1225 1225
1226 bool get isAwakeHrBaselineEligible => 1226 bool get isAwakeHrBaselineEligible =>
1227 - !isSleepLikely &&  
1228 - !isWorkout &&  
1229 - !isWorkoutRecovery &&  
1230 - !isSuspectedActivity; 1227 + !isSleepLikely && !isWorkout && !isWorkoutRecovery;
1231 1228
1232 bool get isSleepHrBaselineEligible => 1229 bool get isSleepHrBaselineEligible =>
1233 - isSleepLikely && !isWorkout && !isWorkoutRecovery && !isSuspectedActivity; 1230 + isSleepLikely && !isWorkout && !isWorkoutRecovery;
1234 1231
1235 bool get isCurrentAwakeHrEligible => isAwakeHrBaselineEligible; 1232 bool get isCurrentAwakeHrEligible => isAwakeHrBaselineEligible;
1236 1233
@@ -43,7 +43,76 @@ class LocalHealthDataConvert { @@ -43,7 +43,76 @@ class LocalHealthDataConvert {
43 return rangeDays(dateRangeType, dateKey(previousStart)); 43 return rangeDays(dateRangeType, dateKey(previousStart));
44 } 44 }
45 45
  46 + static List<HealthKitRawDataPoint> completeSleepIntervalsForDay(
  47 + DateTime day,
  48 + List<HealthKitRawDataPoint> sleepIntervals,
  49 + ) {
  50 + final dayStart = unixSeconds(day);
  51 + final dayEnd = unixSeconds(day.add(const Duration(days: 1)));
  52 + final queryStart = unixSeconds(day.subtract(const Duration(days: 1)));
  53 + final candidates = sleepIntervals
  54 + .where((e) => e.endTime > queryStart && e.startTime < dayEnd)
  55 + .toList()
  56 + ..sort((a, b) {
  57 + final startCompare = a.startTime.compareTo(b.startTime);
  58 + if (startCompare != 0) return startCompare;
  59 + return a.endTime.compareTo(b.endTime);
  60 + });
  61 + if (candidates.isEmpty) return const <HealthKitRawDataPoint>[];
  62 +
  63 + final anchorIndex = candidates.indexWhere(
  64 + (e) => e.endTime > dayStart && e.startTime < dayEnd,
  65 + );
  66 + if (anchorIndex < 0) return const <HealthKitRawDataPoint>[];
  67 +
  68 + var rangeStart = candidates[anchorIndex].startTime;
  69 + var rangeEnd = candidates[anchorIndex].endTime;
  70 + var expanded = true;
  71 + while (expanded) {
  72 + expanded = false;
  73 + for (final interval in candidates) {
  74 + final isConnected =
  75 + interval.startTime <= rangeEnd && interval.endTime >= rangeStart;
  76 + if (!isConnected) continue;
  77 + final nextStart = math.min(rangeStart, interval.startTime);
  78 + final nextEnd = math.max(rangeEnd, interval.endTime);
  79 + if (nextStart != rangeStart || nextEnd != rangeEnd) {
  80 + rangeStart = nextStart;
  81 + rangeEnd = nextEnd;
  82 + expanded = true;
  83 + }
  84 + }
  85 + }
  86 +
  87 + return candidates
  88 + .where((e) => e.endTime > rangeStart && e.startTime < rangeEnd)
  89 + .toList();
  90 + }
  91 +
  92 + static HealthKitRawDataPoint? mergeContinuousSleepIntervals(
  93 + List<HealthKitRawDataPoint> sleepIntervals,
  94 + ) {
  95 + if (sleepIntervals.isEmpty) return null;
  96 + final sorted = [...sleepIntervals]..sort((a, b) {
  97 + final startCompare = a.startTime.compareTo(b.startTime);
  98 + if (startCompare != 0) return startCompare;
  99 + return a.endTime.compareTo(b.endTime);
  100 + });
  101 + var start = sorted.first.startTime;
  102 + var end = sorted.first.endTime;
  103 + for (final interval in sorted.skip(1)) {
  104 + if (interval.startTime > end) break;
  105 + end = math.max(end, interval.endTime);
  106 + }
  107 + return HealthKitRawDataPoint(
  108 + dataType: sorted.first.dataType,
  109 + startTime: start,
  110 + endTime: end,
  111 + );
  112 + }
  113 +
46 static SleepStatisticsData sleepStatistics({ 114 static SleepStatisticsData sleepStatistics({
  115 + required int dateRangeType,
47 required List<DateTime> days, 116 required List<DateTime> days,
48 required List<DateTime> previousDays, 117 required List<DateTime> previousDays,
49 required List<HealthKitRawDataPoint> sleepIntervals, 118 required List<HealthKitRawDataPoint> sleepIntervals,
@@ -69,6 +138,7 @@ class LocalHealthDataConvert { @@ -69,6 +138,7 @@ class LocalHealthDataConvert {
69 final previousValidSleep = 138 final previousValidSleep =
70 previousDaily.where((e) => e.durationSeconds > 0).toList(); 139 previousDaily.where((e) => e.durationSeconds > 0).toList();
71 final allSleepHr = validSleep.expand((e) => e.sleepHr).toList(); 140 final allSleepHr = validSleep.expand((e) => e.sleepHr).toList();
  141 + final shouldIncludeHeartRate = dateRangeType == 3;
72 142
73 return SleepStatisticsData( 143 return SleepStatisticsData(
74 avgSleepDuration: _averageOrNull( 144 avgSleepDuration: _averageOrNull(
@@ -109,19 +179,28 @@ class LocalHealthDataConvert { @@ -109,19 +179,28 @@ class LocalHealthDataConvert {
109 worstSleepInfo: _worstSleepInfo(validSleep), 179 worstSleepInfo: _worstSleepInfo(validSleep),
110 earliestSleepInfo: _earliestSleepInfo(validSleep), 180 earliestSleepInfo: _earliestSleepInfo(validSleep),
111 latestSleepInfo: _latestSleepInfo(validSleep), 181 latestSleepInfo: _latestSleepInfo(validSleep),
112 - hrList: [  
113 - for (final point in allSleepHr)  
114 - SleepHrItem(time: point.endTime, value: point.value),  
115 - ],  
116 - avgHr: _averageOrNull(  
117 - allSleepHr.map((e) => e.value).whereType<num>().toList(),  
118 - ),  
119 - maxHr: _maxOrNull(  
120 - allSleepHr.map((e) => e.value).whereType<num>().toList(),  
121 - ),  
122 - minHr: _minOrNull(  
123 - allSleepHr.map((e) => e.value).whereType<num>().toList(),  
124 - ), 182 + hrList: shouldIncludeHeartRate
  183 + ? [
  184 + for (final point in allSleepHr)
  185 + SleepHrItem(time: point.endTime, value: point.value),
  186 + ]
  187 + : null,
  188 + avgHr: shouldIncludeHeartRate
  189 + ? _averageOrNull(
  190 + allSleepHr.map((e) => e.value).whereType<num>().toList(),
  191 + )
  192 + : null,
  193 + maxHr: shouldIncludeHeartRate
  194 + ? _maxOrNull(
  195 + allSleepHr.map((e) => e.value).whereType<num>().toList(),
  196 + )
  197 + : null,
  198 + minHr: shouldIncludeHeartRate
  199 + ? _minOrNull(
  200 + allSleepHr.map((e) => e.value).whereType<num>().toList(),
  201 + )
  202 + : null,
  203 + sleepTargetDuration: (_sleepGoalMinutes * 60).round(),
125 ); 204 );
126 } 205 }
127 206
@@ -274,7 +353,7 @@ class LocalHealthDataConvert { @@ -274,7 +353,7 @@ class LocalHealthDataConvert {
274 ? null 353 ? null
275 : (latestWithGoal!.exerciseTimeGoal! * 60).round(), 354 : (latestWithGoal!.exerciseTimeGoal! * 60).round(),
276 updateTime: latestWithGoal?.endTime, 355 updateTime: latestWithGoal?.endTime,
277 - sleepTargetDuration: _sleepGoalMinutes.round(), 356 + sleepTargetDuration: (60 * _sleepGoalMinutes).round(),
278 ); 357 );
279 } 358 }
280 359
@@ -282,30 +361,26 @@ class LocalHealthDataConvert { @@ -282,30 +361,26 @@ class LocalHealthDataConvert {
282 required DateTime day, 361 required DateTime day,
283 required List<HealthRawHrvStressPoint> hrvPoints, 362 required List<HealthRawHrvStressPoint> hrvPoints,
284 required List<HealthKitRawActivityDataPoint> activity, 363 required List<HealthKitRawActivityDataPoint> activity,
285 - required List<HealthKitRawDataPoint> heartRate, 364 + required List<HealthKitRawDataPoint> sleepingHeartRate,
286 required List<HealthKitRawDataPoint> restingHeartRate, 365 required List<HealthKitRawDataPoint> restingHeartRate,
287 required List<HealthKitRawDataPoint> sleepIntervals, 366 required List<HealthKitRawDataPoint> sleepIntervals,
288 }) { 367 }) {
289 final activitySummary = _activityDaySummary(day, activity); 368 final activitySummary = _activityDaySummary(day, activity);
290 - final sleepSummary = _sleepDaySummary(day, sleepIntervals, heartRate); 369 + final sleepSummary = _sleepDaySummary(
  370 + day,
  371 + sleepIntervals,
  372 + sleepingHeartRate,
  373 + );
291 final dayStart = unixSeconds(day); 374 final dayStart = unixSeconds(day);
292 final dayEnd = unixSeconds(day.add(const Duration(days: 1))); 375 final dayEnd = unixSeconds(day.add(const Duration(days: 1)));
293 - final sleepWindowStart =  
294 - unixSeconds(day.subtract(const Duration(hours: 12)));  
295 - final sleepWindowEnd = unixSeconds(day.add(const Duration(hours: 12)));  
296 - final sleepTimeList = sleepIntervals  
297 - .where(  
298 - (e) => e.endTime > sleepWindowStart && e.startTime < sleepWindowEnd)  
299 - .map(  
300 - (e) => V2SleepTimeRange(  
301 - fromTime: math.max(e.startTime, sleepWindowStart),  
302 - toTime: math.min(e.endTime, sleepWindowEnd),  
303 - ),  
304 - )  
305 - .toList();  
306 - final dayHr = heartRate  
307 - .where((e) => e.endTime >= dayStart && e.endTime < dayEnd)  
308 - .toList(); 376 + final sleepTimeList = sleepSummary.mergeSleepTimeRange == null
  377 + ? <V2SleepTimeRange>[]
  378 + : [
  379 + V2SleepTimeRange(
  380 + fromTime: sleepSummary.mergeSleepTimeRange!.startTime,
  381 + toTime: sleepSummary.mergeSleepTimeRange!.endTime,
  382 + ),
  383 + ];
309 final dayRestingHr = restingHeartRate 384 final dayRestingHr = restingHeartRate
310 .where((e) => e.endTime >= dayStart && e.endTime < dayEnd) 385 .where((e) => e.endTime >= dayStart && e.endTime < dayEnd)
311 .toList() 386 .toList()
@@ -315,8 +390,9 @@ class LocalHealthDataConvert { @@ -315,8 +390,9 @@ class LocalHealthDataConvert {
315 hrvAvg: _averageOrNull(hrvPoints.map((e) => e.result).toList())?.round(), 390 hrvAvg: _averageOrNull(hrvPoints.map((e) => e.result).toList())?.round(),
316 lastRestingHrValue: 391 lastRestingHrValue:
317 dayRestingHr.isEmpty ? null : dayRestingHr.last.value?.round(), 392 dayRestingHr.isEmpty ? null : dayRestingHr.last.value?.round(),
318 - hrAvg: _averageOrNull(dayHr.map((e) => e.value).whereType<num>().toList())  
319 - ?.round(), 393 + hrAvg: _averageOrNull(
  394 + sleepSummary.sleepHr.map((e) => e.value).whereType<num>().toList(),
  395 + )?.round(),
320 move: activitySummary.move.round(), 396 move: activitySummary.move.round(),
321 exercise: activitySummary.exerciseSeconds.round(), 397 exercise: activitySummary.exerciseSeconds.round(),
322 stand: activitySummary.standHours.round(), 398 stand: activitySummary.standHours.round(),
@@ -388,25 +464,22 @@ class LocalHealthDataConvert { @@ -388,25 +464,22 @@ class LocalHealthDataConvert {
388 List<HealthKitRawDataPoint> sleepIntervals, 464 List<HealthKitRawDataPoint> sleepIntervals,
389 List<HealthKitRawDataPoint> heartRate, 465 List<HealthKitRawDataPoint> heartRate,
390 ) { 466 ) {
391 - final windowStart = unixSeconds(day.subtract(const Duration(hours: 12)));  
392 - final windowEnd = unixSeconds(day.add(const Duration(hours: 12)));  
393 - final intervals = sleepIntervals  
394 - .where((e) => e.endTime > windowStart && e.startTime < windowEnd)  
395 - .toList(); 467 + final intervals = completeSleepIntervalsForDay(day, sleepIntervals);
  468 + final mergedInterval = mergeContinuousSleepIntervals(intervals);
  469 + final windowStart = mergedInterval?.startTime ?? unixSeconds(day);
  470 + final windowEnd = mergedInterval?.endTime ??
  471 + unixSeconds(day.add(const Duration(days: 1)));
396 final summary = _sleepSummary(intervals, windowStart, windowEnd); 472 final summary = _sleepSummary(intervals, windowStart, windowEnd);
397 final score = _sleepScore(summary); 473 final score = _sleepScore(summary);
398 - final sleepStageIntervals = intervals  
399 - .where((e) => _isAsleepSleepType(e.dataType))  
400 - .toList(growable: false);  
401 - final heartRateIntervals =  
402 - sleepStageIntervals.isEmpty ? intervals : sleepStageIntervals;  
403 final sleepHr = heartRate 474 final sleepHr = heartRate
404 - .where((point) => heartRateIntervals.any((interval) => 475 + .where((point) => intervals.any((interval) =>
405 point.endTime > interval.startTime && 476 point.endTime > interval.startTime &&
406 point.endTime <= interval.endTime)) 477 point.endTime <= interval.endTime))
407 .toList(); 478 .toList();
408 return _SleepDaySummary( 479 return _SleepDaySummary(
409 day: day, 480 day: day,
  481 + sleepIntervals: intervals,
  482 + mergeSleepTimeRange: mergedInterval,
410 durationSeconds: (summary.totalAsleepMinutes * 60).round(), 483 durationSeconds: (summary.totalAsleepMinutes * 60).round(),
411 asleepTime: intervals.isEmpty 484 asleepTime: intervals.isEmpty
412 ? null 485 ? null
@@ -711,13 +784,6 @@ class LocalHealthDataConvert { @@ -711,13 +784,6 @@ class LocalHealthDataConvert {
711 return 3; 784 return 3;
712 } 785 }
713 786
714 - static bool _isAsleepSleepType(int type) {  
715 - return type == _sleepTypeAsleepUnspecified ||  
716 - type == _sleepTypeAsleepCore ||  
717 - type == _sleepTypeAsleepDeep ||  
718 - type == _sleepTypeAsleepRem;  
719 - }  
720 -  
721 static int _hrvStateFromValue(num value) { 787 static int _hrvStateFromValue(num value) {
722 if (value >= 30) return 4; 788 if (value >= 30) return 4;
723 if (value >= 21) return 3; 789 if (value >= 21) return 3;
@@ -778,6 +844,8 @@ class LocalHealthDataConvert { @@ -778,6 +844,8 @@ class LocalHealthDataConvert {
778 class _SleepDaySummary { 844 class _SleepDaySummary {
779 const _SleepDaySummary({ 845 const _SleepDaySummary({
780 required this.day, 846 required this.day,
  847 + required this.sleepIntervals,
  848 + required this.mergeSleepTimeRange,
781 required this.durationSeconds, 849 required this.durationSeconds,
782 required this.asleepTime, 850 required this.asleepTime,
783 required this.score, 851 required this.score,
@@ -786,6 +854,8 @@ class _SleepDaySummary { @@ -786,6 +854,8 @@ class _SleepDaySummary {
786 }); 854 });
787 855
788 final DateTime day; 856 final DateTime day;
  857 + final List<HealthKitRawDataPoint> sleepIntervals;
  858 + final HealthKitRawDataPoint? mergeSleepTimeRange;
789 final num durationSeconds; 859 final num durationSeconds;
790 final num? asleepTime; 860 final num? asleepTime;
791 final num? score; 861 final num? score;
@@ -31,10 +31,10 @@ class LocalHealthDataSource implements HealthDataSource { @@ -31,10 +31,10 @@ class LocalHealthDataSource implements HealthDataSource {
31 ); 31 );
32 final allDays = [...previousDays, ...days]; 32 final allDays = [...previousDays, ...days];
33 final queryStart = LocalHealthDataConvert.unixSeconds( 33 final queryStart = LocalHealthDataConvert.unixSeconds(
34 - allDays.first.subtract(const Duration(hours: 12)), 34 + allDays.first.subtract(const Duration(days: 1)),
35 ); 35 );
36 final queryEnd = LocalHealthDataConvert.unixSeconds( 36 final queryEnd = LocalHealthDataConvert.unixSeconds(
37 - allDays.last.add(const Duration(days: 1, hours: 12)), 37 + allDays.last.add(const Duration(days: 1)),
38 ); 38 );
39 final sleepIntervals = await coreService.queryRawSleepIntervals( 39 final sleepIntervals = await coreService.queryRawSleepIntervals(
40 startTime: queryStart, 40 startTime: queryStart,
@@ -48,6 +48,7 @@ class LocalHealthDataSource implements HealthDataSource { @@ -48,6 +48,7 @@ class LocalHealthDataSource implements HealthDataSource {
48 48
49 return AppSuccess( 49 return AppSuccess(
50 LocalHealthDataConvert.sleepStatistics( 50 LocalHealthDataConvert.sleepStatistics(
  51 + dateRangeType: dateRangeType,
51 days: days, 52 days: days,
52 previousDays: previousDays, 53 previousDays: previousDays,
53 sleepIntervals: sleepIntervals, 54 sleepIntervals: sleepIntervals,
@@ -87,10 +88,6 @@ class LocalHealthDataSource implements HealthDataSource { @@ -87,10 +88,6 @@ class LocalHealthDataSource implements HealthDataSource {
87 startTime: queryStart, 88 startTime: queryStart,
88 endTime: queryEnd, 89 endTime: queryEnd,
89 ); 90 );
90 - final sleepIntervals = await coreService.queryRawSleepIntervals(  
91 - startTime: queryStart,  
92 - endTime: queryEnd,  
93 - );  
94 91
95 return AppSuccess( 92 return AppSuccess(
96 LocalHealthDataConvert.activityBurnStatistics( 93 LocalHealthDataConvert.activityBurnStatistics(
@@ -98,7 +95,7 @@ class LocalHealthDataSource implements HealthDataSource { @@ -98,7 +95,7 @@ class LocalHealthDataSource implements HealthDataSource {
98 previousDays: previousDays, 95 previousDays: previousDays,
99 activity: activity, 96 activity: activity,
100 heartRate: heartRate, 97 heartRate: heartRate,
101 - sleepIntervals: sleepIntervals, 98 + sleepIntervals: [],
102 ), 99 ),
103 ); 100 );
104 } catch (error) { 101 } catch (error) {
@@ -188,10 +185,10 @@ class LocalHealthDataSource implements HealthDataSource { @@ -188,10 +185,10 @@ class LocalHealthDataSource implements HealthDataSource {
188 ) - 185 ) -
189 1; 186 1;
190 final sleepStart = LocalHealthDataConvert.unixSeconds( 187 final sleepStart = LocalHealthDataConvert.unixSeconds(
191 - day.subtract(const Duration(hours: 12)), 188 + day.subtract(const Duration(days: 1)),
192 ); 189 );
193 final sleepEnd = LocalHealthDataConvert.unixSeconds( 190 final sleepEnd = LocalHealthDataConvert.unixSeconds(
194 - day.add(const Duration(hours: 12)), 191 + day.add(const Duration(days: 1)),
195 ); 192 );
196 final hrvPoints = await coreService.queryHrvStressPoints( 193 final hrvPoints = await coreService.queryHrvStressPoints(
197 startTime: dayStart, 194 startTime: dayStart,
@@ -201,10 +198,10 @@ class LocalHealthDataSource implements HealthDataSource { @@ -201,10 +198,10 @@ class LocalHealthDataSource implements HealthDataSource {
201 startTime: dayStart, 198 startTime: dayStart,
202 endTime: dayEnd, 199 endTime: dayEnd,
203 ); 200 );
204 - final heartRate = await coreService.queryRawDataPoints( 201 + final sleepingHeartRate = await coreService.queryRawDataPoints(
205 dataType: HealthDataUploadType.heartRate.type, 202 dataType: HealthDataUploadType.heartRate.type,
206 startTime: sleepStart, 203 startTime: sleepStart,
207 - endTime: dayEnd, 204 + endTime: sleepEnd,
208 ); 205 );
209 final restingHeartRate = await coreService.queryRawDataPoints( 206 final restingHeartRate = await coreService.queryRawDataPoints(
210 dataType: HealthDataUploadType.restingHeartRate.type, 207 dataType: HealthDataUploadType.restingHeartRate.type,
@@ -220,7 +217,7 @@ class LocalHealthDataSource implements HealthDataSource { @@ -220,7 +217,7 @@ class LocalHealthDataSource implements HealthDataSource {
220 day: day, 217 day: day,
221 hrvPoints: hrvPoints, 218 hrvPoints: hrvPoints,
222 activity: activity, 219 activity: activity,
223 - heartRate: heartRate, 220 + sleepingHeartRate: sleepingHeartRate,
224 restingHeartRate: restingHeartRate, 221 restingHeartRate: restingHeartRate,
225 sleepIntervals: sleepIntervals, 222 sleepIntervals: sleepIntervals,
226 ), 223 ),
@@ -438,6 +438,35 @@ class HealthKitRawDataHostApi { @@ -438,6 +438,35 @@ class HealthKitRawDataHostApi {
438 } 438 }
439 } 439 }
440 440
  441 + /// 原生数据上传
  442 + Future<bool> performHealthDataUpload() async {
  443 + final String pigeonVar_channelName = 'dev.flutter.pigeon.doublefeel_flutter.HealthKitRawDataHostApi.performHealthDataUpload$pigeonVar_messageChannelSuffix';
  444 + final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
  445 + pigeonVar_channelName,
  446 + pigeonChannelCodec,
  447 + binaryMessenger: pigeonVar_binaryMessenger,
  448 + );
  449 + final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(null);
  450 + final List<Object?>? pigeonVar_replyList =
  451 + await pigeonVar_sendFuture as List<Object?>?;
  452 + if (pigeonVar_replyList == null) {
  453 + throw _createConnectionError(pigeonVar_channelName);
  454 + } else if (pigeonVar_replyList.length > 1) {
  455 + throw PlatformException(
  456 + code: pigeonVar_replyList[0]! as String,
  457 + message: pigeonVar_replyList[1] as String?,
  458 + details: pigeonVar_replyList[2],
  459 + );
  460 + } else if (pigeonVar_replyList[0] == null) {
  461 + throw PlatformException(
  462 + code: 'null-error',
  463 + message: 'Host platform returned null value for non-null return value.',
  464 + );
  465 + } else {
  466 + return (pigeonVar_replyList[0] as bool?)!;
  467 + }
  468 + }
  469 +
441 /// 从从AppleHealth中读取睡眠数据 470 /// 从从AppleHealth中读取睡眠数据
442 Future<List<HealthKitRawSleepDataPoint>> getHealthKitRawSleepData(int startTime, int endTime) async { 471 Future<List<HealthKitRawSleepDataPoint>> getHealthKitRawSleepData(int startTime, int endTime) async {
443 final String pigeonVar_channelName = 'dev.flutter.pigeon.doublefeel_flutter.HealthKitRawDataHostApi.getHealthKitRawSleepData$pigeonVar_messageChannelSuffix'; 472 final String pigeonVar_channelName = 'dev.flutter.pigeon.doublefeel_flutter.HealthKitRawDataHostApi.getHealthKitRawSleepData$pigeonVar_messageChannelSuffix';
@@ -98,6 +98,10 @@ abstract class HealthKitRawDataHostApi { @@ -98,6 +98,10 @@ abstract class HealthKitRawDataHostApi {
98 List<HealthKitRawDataPoint> getHealthKitRawData( 98 List<HealthKitRawDataPoint> getHealthKitRawData(
99 int dataType, int startTime, int endTime); 99 int dataType, int startTime, int endTime);
100 100
  101 + /// 原生数据上传
  102 + @async
  103 + bool performHealthDataUpload();
  104 +
101 /// 从从AppleHealth中读取睡眠数据 105 /// 从从AppleHealth中读取睡眠数据
102 @async 106 @async
103 List<HealthKitRawSleepDataPoint> getHealthKitRawSleepData( 107 List<HealthKitRawSleepDataPoint> getHealthKitRawSleepData(