Commit a7f33c698359d2620e7f48d4339ca112c58965d0

Authored by 权海
1 parent 16975990

feat(ui):修改一些问题

@@ -209,14 +209,14 @@ final class HealthKitQueryReader { @@ -209,14 +209,14 @@ final class HealthKitQueryReader {
209 healthValueTimestamp: timestamp, 209 healthValueTimestamp: timestamp,
210 move: Int(summary.activeEnergyBurnedGoal.doubleValue(for: .kilocalorie())), 210 move: Int(summary.activeEnergyBurnedGoal.doubleValue(for: .kilocalorie())),
211 stand: Int(summary.appleStandHoursGoal.doubleValue(for: .count())), 211 stand: Int(summary.appleStandHoursGoal.doubleValue(for: .count())),
212 - activityMoveMode: Self.activityMoveModeValue(from: summary), 212 +// activityMoveMode: Self.activityMoveModeValue(from: summary),
213 activeEnergyBurned: summary.activeEnergyBurned.doubleValue(for: .kilocalorie()), 213 activeEnergyBurned: summary.activeEnergyBurned.doubleValue(for: .kilocalorie()),
214 activeEnergyBurnedGoal: summary.activeEnergyBurnedGoal.doubleValue(for: .kilocalorie()), 214 activeEnergyBurnedGoal: summary.activeEnergyBurnedGoal.doubleValue(for: .kilocalorie()),
215 appleMoveTime: Self.appleMoveTimeValue(from: summary), 215 appleMoveTime: Self.appleMoveTimeValue(from: summary),
216 appleMoveTimeGoal: Self.appleMoveTimeGoalValue(from: summary), 216 appleMoveTimeGoal: Self.appleMoveTimeGoalValue(from: summary),
217 - appleExerciseTime: summary.appleExerciseTime.doubleValue(for: .minute()), 217 + appleExerciseTime: summary.appleExerciseTime.doubleValue(for: .second()),
218 exerciseTimeGoal: Self.exerciseTimeGoalValue(from: summary), 218 exerciseTimeGoal: Self.exerciseTimeGoalValue(from: summary),
219 - appleStandHours: summary.appleStandHours.doubleValue(for: .count()), 219 + appleStandHours: Self.standSeconds(from: summary.appleStandHours),
220 standHoursGoal: Self.standHoursGoalValue(from: summary) 220 standHoursGoal: Self.standHoursGoalValue(from: summary)
221 ) 221 )
222 } 222 }
@@ -244,18 +244,22 @@ final class HealthKitQueryReader { @@ -244,18 +244,22 @@ final class HealthKitQueryReader {
244 244
245 private static func exerciseTimeGoalValue(from summary: HKActivitySummary) -> Double? { 245 private static func exerciseTimeGoalValue(from summary: HKActivitySummary) -> Double? {
246 if #available(iOS 16.0, *) { 246 if #available(iOS 16.0, *) {
247 - return summary.exerciseTimeGoal?.doubleValue(for: .minute()) 247 + return summary.exerciseTimeGoal?.doubleValue(for: .second())
248 } 248 }
249 return nil 249 return nil
250 } 250 }
251 251
252 private static func standHoursGoalValue(from summary: HKActivitySummary) -> Double? { 252 private static func standHoursGoalValue(from summary: HKActivitySummary) -> Double? {
253 if #available(iOS 16.0, *) { 253 if #available(iOS 16.0, *) {
254 - return summary.standHoursGoal?.doubleValue(for: .count()) 254 + return summary.standHoursGoal.map { Self.standSeconds(from: $0) }
255 } 255 }
256 return nil 256 return nil
257 } 257 }
258 258
  259 + private static func standSeconds(from quantity: HKQuantity) -> Double {
  260 + quantity.doubleValue(for: .count()) * 60 * 60
  261 + }
  262 +
259 private func fetchHeartRateFamily(startDate: Date, endDate: Date) async throws -> [NativeHealthDataPoint] { 263 private func fetchHeartRateFamily(startDate: Date, endDate: Date) async throws -> [NativeHealthDataPoint] {
260 async let heartRate = fetchQuantitySamples( 264 async let heartRate = fetchQuantitySamples(
261 identifier: .heartRate, 265 identifier: .heartRate,
@@ -332,7 +336,7 @@ final class HealthKitQueryReader { @@ -332,7 +336,7 @@ final class HealthKitQueryReader {
332 return samples.map { sample in 336 return samples.map { sample in
333 var ismotion: Bool? 337 var ismotion: Bool?
334 if identifier == .heartRate, let rawValue = sample.metadata?[ HKMetadataKeyHeartRateMotionContext] as? NSNumber, let context = HKHeartRateMotionContext(rawValue: rawValue.intValue) { 338 if identifier == .heartRate, let rawValue = sample.metadata?[ HKMetadataKeyHeartRateMotionContext] as? NSNumber, let context = HKHeartRateMotionContext(rawValue: rawValue.intValue) {
335 - ismotion = context == .active || context == .sedentary 339 + ismotion = context == .active
336 } 340 }
337 341
338 return NativeHealthRawDataPoint( 342 return NativeHealthRawDataPoint(
@@ -132,7 +132,7 @@ struct NativeActivityTarget { @@ -132,7 +132,7 @@ struct NativeActivityTarget {
132 let healthValueTimestamp: TimeInterval? 132 let healthValueTimestamp: TimeInterval?
133 let move: Int? 133 let move: Int?
134 let stand: Int? 134 let stand: Int?
135 - let activityMoveMode: Int? 135 +// let activityMoveMode: Int?
136 // 千卡 136 // 千卡
137 let activeEnergyBurned: Double? 137 let activeEnergyBurned: Double?
138 let activeEnergyBurnedGoal: Double? 138 let activeEnergyBurnedGoal: Double?
@@ -370,14 +370,18 @@ protocol HealthKitRawDataHostApi { @@ -370,14 +370,18 @@ protocol HealthKitRawDataHostApi {
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 /// 原生数据上传 371 /// 原生数据上传
372 func performHealthDataUpload(completion: @escaping (Result<Bool, Error>) -> Void) 372 func performHealthDataUpload(completion: @escaping (Result<Bool, Error>) -> Void)
  373 + /// 上传心率 - 实时压力数据, 返回上传截止的时间戳, 方便flutter修改数据库
  374 + func performHRDataUpload(sqliteFilePath: String, completion: @escaping (Result<Int64, Error>) -> Void)
  375 + /// 上传HRV - HRV压力数据, 返回上传截止的时间戳,方便flutter 修改数据库
  376 + func performHRVDataUpload(sqliteFilePath: String, completion: @escaping (Result<Int64, Error>) -> Void)
  377 + /// 上传当前实时压力(统计维度是当天)
  378 + func performAvgRealtimeStressDataUpload(sqliteFilePath: String, completion: @escaping (Result<Bool, Error>) -> Void)
373 /// 从从AppleHealth中读取睡眠数据 379 /// 从从AppleHealth中读取睡眠数据
374 func getHealthKitRawSleepData(startTime: Int64, endTime: Int64, completion: @escaping (Result<[HealthKitRawSleepDataPoint], Error>) -> Void) 380 func getHealthKitRawSleepData(startTime: Int64, endTime: Int64, completion: @escaping (Result<[HealthKitRawSleepDataPoint], Error>) -> Void)
375 /// 从从AppleHealth中读取活动统计数据 381 /// 从从AppleHealth中读取活动统计数据
376 func getHealthKitRawActivityData(startTime: Int64, endTime: Int64, completion: @escaping (Result<[HealthKitRawActivityDataPoint], Error>) -> Void) 382 func getHealthKitRawActivityData(startTime: Int64, endTime: Int64, completion: @escaping (Result<[HealthKitRawActivityDataPoint], Error>) -> Void)
377 /// 从从AppleHealth中读取锻炼数据 383 /// 从从AppleHealth中读取锻炼数据
378 func getHealthKitRawWorkoutData(startTime: Int64, endTime: Int64, completion: @escaping (Result<[HealthKitRawWorkoutDataPoint], Error>) -> Void) 384 func getHealthKitRawWorkoutData(startTime: Int64, endTime: Int64, completion: @escaping (Result<[HealthKitRawWorkoutDataPoint], Error>) -> Void)
379 - /// 从从AppleHealth中读取锻炼数据  
380 - func getHealthKitRawMotionData(startTime: Int64, endTime: Int64, completion: @escaping (Result<[HealthKitRawMotionDataPoint], Error>) -> Void)  
381 } 385 }
382 386
383 /// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. 387 /// Generated setup class from Pigeon to handle messages through the `binaryMessenger`.
@@ -438,6 +442,60 @@ class HealthKitRawDataHostApiSetup { @@ -438,6 +442,60 @@ class HealthKitRawDataHostApiSetup {
438 } else { 442 } else {
439 performHealthDataUploadChannel.setMessageHandler(nil) 443 performHealthDataUploadChannel.setMessageHandler(nil)
440 } 444 }
  445 + /// 上传心率 - 实时压力数据, 返回上传截止的时间戳, 方便flutter修改数据库
  446 + let performHRDataUploadChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.doublefeel_flutter.HealthKitRawDataHostApi.performHRDataUpload\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
  447 + if let api = api {
  448 + performHRDataUploadChannel.setMessageHandler { message, reply in
  449 + let args = message as! [Any?]
  450 + let sqliteFilePathArg = args[0] as! String
  451 + api.performHRDataUpload(sqliteFilePath: sqliteFilePathArg) { result in
  452 + switch result {
  453 + case .success(let res):
  454 + reply(wrapResult(res))
  455 + case .failure(let error):
  456 + reply(wrapError(error))
  457 + }
  458 + }
  459 + }
  460 + } else {
  461 + performHRDataUploadChannel.setMessageHandler(nil)
  462 + }
  463 + /// 上传HRV - HRV压力数据, 返回上传截止的时间戳,方便flutter 修改数据库
  464 + let performHRVDataUploadChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.doublefeel_flutter.HealthKitRawDataHostApi.performHRVDataUpload\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
  465 + if let api = api {
  466 + performHRVDataUploadChannel.setMessageHandler { message, reply in
  467 + let args = message as! [Any?]
  468 + let sqliteFilePathArg = args[0] as! String
  469 + api.performHRVDataUpload(sqliteFilePath: sqliteFilePathArg) { result in
  470 + switch result {
  471 + case .success(let res):
  472 + reply(wrapResult(res))
  473 + case .failure(let error):
  474 + reply(wrapError(error))
  475 + }
  476 + }
  477 + }
  478 + } else {
  479 + performHRVDataUploadChannel.setMessageHandler(nil)
  480 + }
  481 + /// 上传当前实时压力(统计维度是当天)
  482 + let performAvgRealtimeStressDataUploadChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.doublefeel_flutter.HealthKitRawDataHostApi.performAvgRealtimeStressDataUpload\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
  483 + if let api = api {
  484 + performAvgRealtimeStressDataUploadChannel.setMessageHandler { message, reply in
  485 + let args = message as! [Any?]
  486 + let sqliteFilePathArg = args[0] as! String
  487 + api.performAvgRealtimeStressDataUpload(sqliteFilePath: sqliteFilePathArg) { result in
  488 + switch result {
  489 + case .success(let res):
  490 + reply(wrapResult(res))
  491 + case .failure(let error):
  492 + reply(wrapError(error))
  493 + }
  494 + }
  495 + }
  496 + } else {
  497 + performAvgRealtimeStressDataUploadChannel.setMessageHandler(nil)
  498 + }
441 /// 从从AppleHealth中读取睡眠数据 499 /// 从从AppleHealth中读取睡眠数据
442 let getHealthKitRawSleepDataChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.doublefeel_flutter.HealthKitRawDataHostApi.getHealthKitRawSleepData\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) 500 let getHealthKitRawSleepDataChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.doublefeel_flutter.HealthKitRawDataHostApi.getHealthKitRawSleepData\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
443 if let api = api { 501 if let api = api {
@@ -495,24 +553,5 @@ class HealthKitRawDataHostApiSetup { @@ -495,24 +553,5 @@ class HealthKitRawDataHostApiSetup {
495 } else { 553 } else {
496 getHealthKitRawWorkoutDataChannel.setMessageHandler(nil) 554 getHealthKitRawWorkoutDataChannel.setMessageHandler(nil)
497 } 555 }
498 - /// 从从AppleHealth中读取锻炼数据  
499 - let getHealthKitRawMotionDataChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.doublefeel_flutter.HealthKitRawDataHostApi.getHealthKitRawMotionData\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)  
500 - if let api = api {  
501 - getHealthKitRawMotionDataChannel.setMessageHandler { message, reply in  
502 - let args = message as! [Any?]  
503 - let startTimeArg = args[0] as! Int64  
504 - let endTimeArg = args[1] as! Int64  
505 - api.getHealthKitRawMotionData(startTime: startTimeArg, endTime: endTimeArg) { result in  
506 - switch result {  
507 - case .success(let res):  
508 - reply(wrapResult(res))  
509 - case .failure(let error):  
510 - reply(wrapError(error))  
511 - }  
512 - }  
513 - }  
514 - } else {  
515 - getHealthKitRawMotionDataChannel.setMessageHandler(nil)  
516 - }  
517 } 556 }
518 } 557 }
@@ -20,6 +20,19 @@ final class HealthKitRawDataHostApiImpl: HealthKitRawDataHostApi { @@ -20,6 +20,19 @@ final class HealthKitRawDataHostApiImpl: HealthKitRawDataHostApi {
20 completion(.success(true)) 20 completion(.success(true))
21 } 21 }
22 } 22 }
  23 +
  24 + func performHRDataUpload(sqliteFilePath: String, completion: @escaping (Result<Int64, any Error>) -> Void) {
  25 +
  26 + }
  27 +
  28 + func performHRVDataUpload(sqliteFilePath: String, completion: @escaping (Result<Int64, any Error>) -> Void) {
  29 +
  30 + }
  31 +
  32 + func performAvgRealtimeStressDataUpload(sqliteFilePath: String, completion: @escaping (Result<Bool, any Error>) -> Void) {
  33 +
  34 + }
  35 +
23 36
24 func getHealthKitRawData( 37 func getHealthKitRawData(
25 dataType: Int64, 38 dataType: Int64,
@@ -133,10 +146,6 @@ final class HealthKitRawDataHostApiImpl: HealthKitRawDataHostApi { @@ -133,10 +146,6 @@ final class HealthKitRawDataHostApiImpl: HealthKitRawDataHostApi {
133 } 146 }
134 } 147 }
135 } 148 }
136 -  
137 - func getHealthKitRawMotionData(startTime: Int64, endTime: Int64, completion: @escaping (Result<[HealthKitRawMotionDataPoint], any Error>) -> Void) {  
138 -  
139 - }  
140 149
141 func performSleepDataUpload(completion: @escaping (Result<Bool, Error>) -> Void) { 150 func performSleepDataUpload(completion: @escaping (Result<Bool, Error>) -> Void) {
142 Task { 151 Task {
@@ -107,7 +107,6 @@ class ApiActivityBurnReportDataSource implements ActivityBurnReportDataSource { @@ -107,7 +107,6 @@ class ApiActivityBurnReportDataSource implements ActivityBurnReportDataSource {
107 ActivityBurnStatisticsDataV2 data, 107 ActivityBurnStatisticsDataV2 data,
108 ) { 108 ) {
109 final target = data.activityTargetInfo; 109 final target = data.activityTargetInfo;
110 - final overallValues = _dailyOverallValues(date, data.overallList);  
111 final points = <ActivityBurnHeartRatePoint>[ 110 final points = <ActivityBurnHeartRatePoint>[
112 for (final item in data.hrList ?? const <HrList>[]) 111 for (final item in data.hrList ?? const <HrList>[])
113 if (_parseDateTime(item.dataTime) case final time?) 112 if (_parseDateTime(item.dataTime) case final time?)
@@ -120,13 +119,13 @@ class ApiActivityBurnReportDataSource implements ActivityBurnReportDataSource { @@ -120,13 +119,13 @@ class ApiActivityBurnReportDataSource implements ActivityBurnReportDataSource {
120 return ActivityBurnReport( 119 return ActivityBurnReport(
121 date: date, 120 date: date,
122 activeEnergy: 121 activeEnergy:
123 - _metric(overallValues?.move ?? data.totalMove, target?.move), 122 + _metric(data.totalMove, target?.move),
124 exerciseMinutes: _exerciseDurationMetric( 123 exerciseMinutes: _exerciseDurationMetric(
125 - overallValues?.exercise ?? data.totalExercise, 124 + data.totalExercise,
126 target?.exercise, 125 target?.exercise,
127 ), 126 ),
128 standHours: _standDurationMetric( 127 standHours: _standDurationMetric(
129 - overallValues?.stand ?? data.totalStand, 128 + data.totalStand,
130 target?.stand, 129 target?.stand,
131 ), 130 ),
132 heartRate: ActivityBurnHeartRateSummary( 131 heartRate: ActivityBurnHeartRateSummary(
@@ -137,19 +136,6 @@ class ApiActivityBurnReportDataSource implements ActivityBurnReportDataSource { @@ -137,19 +136,6 @@ class ApiActivityBurnReportDataSource implements ActivityBurnReportDataSource {
137 ); 136 );
138 } 137 }
139 138
140 - Value? _dailyOverallValues(  
141 - DateTime date,  
142 - List<OverallList>? overallList,  
143 - ) {  
144 - final items = overallList ?? const <OverallList>[];  
145 - for (final item in items) {  
146 - final time = _parseDateTime(item.timeKey);  
147 - if (time != null && _normalizeDate(time) == date) return item.value;  
148 - }  
149 - if (items.length == 1) return items.first.value;  
150 - return null;  
151 - }  
152 -  
153 List<ActivityBurnReport> _periodReports( 139 List<ActivityBurnReport> _periodReports(
154 DateTime start, 140 DateTime start,
155 int dayCount, 141 int dayCount,
@@ -230,7 +230,7 @@ class LocalHealthDataConvert { @@ -230,7 +230,7 @@ class LocalHealthDataConvert {
230 final data = ActivityBurnStatisticsDataV2( 230 final data = ActivityBurnStatisticsDataV2(
231 totalMove: _sum(daily.map((e) => e.move).toList()), 231 totalMove: _sum(daily.map((e) => e.move).toList()),
232 totalSteps: _sum(daily.map((e) => e.steps).toList()), 232 totalSteps: _sum(daily.map((e) => e.steps).toList()),
233 - totalStand: _sum(daily.map((e) => e.standHours).toList()), 233 + totalStand: _sum(daily.map((e) => e.standSeconds).toList()),
234 totalExercise: _sum(daily.map((e) => e.exerciseSeconds).toList()), 234 totalExercise: _sum(daily.map((e) => e.exerciseSeconds).toList()),
235 avgMove: _averageOrNull(daily.map((e) => e.move).toList()), 235 avgMove: _averageOrNull(daily.map((e) => e.move).toList()),
236 qoqAvgMove: _averageOrNull(previousDaily.map((e) => e.move).toList()), 236 qoqAvgMove: _averageOrNull(previousDaily.map((e) => e.move).toList()),
@@ -248,7 +248,7 @@ class LocalHealthDataConvert { @@ -248,7 +248,7 @@ class LocalHealthDataConvert {
248 timeKey: dateKey(item.day), 248 timeKey: dateKey(item.day),
249 value: Value( 249 value: Value(
250 move: item.move, 250 move: item.move,
251 - stand: item.standHours, 251 + stand: item.standSeconds,
252 exercise: item.exerciseSeconds, 252 exercise: item.exerciseSeconds,
253 ), 253 ),
254 ), 254 ),
@@ -370,7 +370,7 @@ class LocalHealthDataConvert { @@ -370,7 +370,7 @@ class LocalHealthDataConvert {
370 stand: latestWithGoal?.standHoursGoal?.round(), 370 stand: latestWithGoal?.standHoursGoal?.round(),
371 exercise: latestWithGoal?.exerciseTimeGoal == null 371 exercise: latestWithGoal?.exerciseTimeGoal == null
372 ? null 372 ? null
373 - : (latestWithGoal!.exerciseTimeGoal! * 60).round(), 373 + : latestWithGoal!.exerciseTimeGoal!.round(),
374 updateTime: latestWithGoal?.endTime, 374 updateTime: latestWithGoal?.endTime,
375 sleepTargetDuration: (60 * _sleepGoalMinutes).round(), 375 sleepTargetDuration: (60 * _sleepGoalMinutes).round(),
376 ); 376 );
@@ -414,7 +414,7 @@ class LocalHealthDataConvert { @@ -414,7 +414,7 @@ class LocalHealthDataConvert {
414 )?.round(), 414 )?.round(),
415 move: activitySummary.move.round(), 415 move: activitySummary.move.round(),
416 exercise: activitySummary.exerciseSeconds.round(), 416 exercise: activitySummary.exerciseSeconds.round(),
417 - stand: activitySummary.standHours.round(), 417 + stand: activitySummary.standSeconds.round(),
418 steps: activitySummary.steps.round(), 418 steps: activitySummary.steps.round(),
419 sleepDuration: sleepSummary.durationSeconds.round(), 419 sleepDuration: sleepSummary.durationSeconds.round(),
420 sleepScore: sleepSummary.score?.toDouble(), 420 sleepScore: sleepSummary.score?.toDouble(),
@@ -598,14 +598,12 @@ class LocalHealthDataConvert { @@ -598,14 +598,12 @@ class LocalHealthDataConvert {
598 day: day, 598 day: day,
599 move: latest?.activeEnergyBurned ?? 0, 599 move: latest?.activeEnergyBurned ?? 0,
600 steps: latest?.appleMoveTime ?? 0, 600 steps: latest?.appleMoveTime ?? 0,
601 - standHours: latest?.appleStandHours ?? 0,  
602 - exerciseSeconds: (latest?.appleExerciseTime ?? 0) * 60, 601 + standSeconds: latest?.appleStandHours ?? 0,
  602 + exerciseSeconds: latest?.appleExerciseTime ?? 0,
603 moveGoal: latest?.activeEnergyBurnedGoal, 603 moveGoal: latest?.activeEnergyBurnedGoal,
604 stepGoal: latest?.appleMoveTimeGoal, 604 stepGoal: latest?.appleMoveTimeGoal,
605 standGoal: latest?.standHoursGoal, 605 standGoal: latest?.standHoursGoal,
606 - exerciseGoalSeconds: latest?.exerciseTimeGoal == null  
607 - ? null  
608 - : latest!.exerciseTimeGoal! * 60, 606 + exerciseGoalSeconds: latest?.exerciseTimeGoal,
609 ); 607 );
610 } 608 }
611 609
@@ -1024,7 +1022,7 @@ class _ActivityDaySummary { @@ -1024,7 +1022,7 @@ class _ActivityDaySummary {
1024 required this.day, 1022 required this.day,
1025 required this.move, 1023 required this.move,
1026 required this.steps, 1024 required this.steps,
1027 - required this.standHours, 1025 + required this.standSeconds,
1028 required this.exerciseSeconds, 1026 required this.exerciseSeconds,
1029 required this.moveGoal, 1027 required this.moveGoal,
1030 required this.stepGoal, 1028 required this.stepGoal,
@@ -1035,7 +1033,7 @@ class _ActivityDaySummary { @@ -1035,7 +1033,7 @@ class _ActivityDaySummary {
1035 final DateTime day; 1033 final DateTime day;
1036 final num move; 1034 final num move;
1037 final num steps; 1035 final num steps;
1038 - final num standHours; 1036 + final num standSeconds;
1039 final num exerciseSeconds; 1037 final num exerciseSeconds;
1040 final num? moveGoal; 1038 final num? moveGoal;
1041 final num? stepGoal; 1039 final num? stepGoal;
@@ -467,15 +467,15 @@ class HealthKitRawDataHostApi { @@ -467,15 +467,15 @@ class HealthKitRawDataHostApi {
467 } 467 }
468 } 468 }
469 469
470 - /// 从从AppleHealth中读取睡眠数据  
471 - Future<List<HealthKitRawSleepDataPoint>> getHealthKitRawSleepData(int startTime, int endTime) async {  
472 - final String pigeonVar_channelName = 'dev.flutter.pigeon.doublefeel_flutter.HealthKitRawDataHostApi.getHealthKitRawSleepData$pigeonVar_messageChannelSuffix'; 470 + /// 上传心率 - 实时压力数据, 返回上传截止的时间戳, 方便flutter修改数据库
  471 + Future<int> performHRDataUpload({required String sqliteFilePath}) async {
  472 + final String pigeonVar_channelName = 'dev.flutter.pigeon.doublefeel_flutter.HealthKitRawDataHostApi.performHRDataUpload$pigeonVar_messageChannelSuffix';
473 final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>( 473 final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
474 pigeonVar_channelName, 474 pigeonVar_channelName,
475 pigeonChannelCodec, 475 pigeonChannelCodec,
476 binaryMessenger: pigeonVar_binaryMessenger, 476 binaryMessenger: pigeonVar_binaryMessenger,
477 ); 477 );
478 - final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[startTime, endTime]); 478 + final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[sqliteFilePath]);
479 final List<Object?>? pigeonVar_replyList = 479 final List<Object?>? pigeonVar_replyList =
480 await pigeonVar_sendFuture as List<Object?>?; 480 await pigeonVar_sendFuture as List<Object?>?;
481 if (pigeonVar_replyList == null) { 481 if (pigeonVar_replyList == null) {
@@ -492,13 +492,71 @@ class HealthKitRawDataHostApi { @@ -492,13 +492,71 @@ class HealthKitRawDataHostApi {
492 message: 'Host platform returned null value for non-null return value.', 492 message: 'Host platform returned null value for non-null return value.',
493 ); 493 );
494 } else { 494 } else {
495 - return (pigeonVar_replyList[0] as List<Object?>?)!.cast<HealthKitRawSleepDataPoint>(); 495 + return (pigeonVar_replyList[0] as int?)!;
496 } 496 }
497 } 497 }
498 498
499 - /// 从从AppleHealth中读取活动统计数据  
500 - Future<List<HealthKitRawActivityDataPoint>> getHealthKitRawActivityData(int startTime, int endTime) async {  
501 - final String pigeonVar_channelName = 'dev.flutter.pigeon.doublefeel_flutter.HealthKitRawDataHostApi.getHealthKitRawActivityData$pigeonVar_messageChannelSuffix'; 499 + /// 上传HRV - HRV压力数据, 返回上传截止的时间戳,方便flutter 修改数据库
  500 + Future<int> performHRVDataUpload({required String sqliteFilePath}) async {
  501 + final String pigeonVar_channelName = 'dev.flutter.pigeon.doublefeel_flutter.HealthKitRawDataHostApi.performHRVDataUpload$pigeonVar_messageChannelSuffix';
  502 + final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
  503 + pigeonVar_channelName,
  504 + pigeonChannelCodec,
  505 + binaryMessenger: pigeonVar_binaryMessenger,
  506 + );
  507 + final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[sqliteFilePath]);
  508 + final List<Object?>? pigeonVar_replyList =
  509 + await pigeonVar_sendFuture as List<Object?>?;
  510 + if (pigeonVar_replyList == null) {
  511 + throw _createConnectionError(pigeonVar_channelName);
  512 + } else if (pigeonVar_replyList.length > 1) {
  513 + throw PlatformException(
  514 + code: pigeonVar_replyList[0]! as String,
  515 + message: pigeonVar_replyList[1] as String?,
  516 + details: pigeonVar_replyList[2],
  517 + );
  518 + } else if (pigeonVar_replyList[0] == null) {
  519 + throw PlatformException(
  520 + code: 'null-error',
  521 + message: 'Host platform returned null value for non-null return value.',
  522 + );
  523 + } else {
  524 + return (pigeonVar_replyList[0] as int?)!;
  525 + }
  526 + }
  527 +
  528 + /// 上传当前实时压力(统计维度是当天)
  529 + Future<bool> performAvgRealtimeStressDataUpload({required String sqliteFilePath}) async {
  530 + final String pigeonVar_channelName = 'dev.flutter.pigeon.doublefeel_flutter.HealthKitRawDataHostApi.performAvgRealtimeStressDataUpload$pigeonVar_messageChannelSuffix';
  531 + final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
  532 + pigeonVar_channelName,
  533 + pigeonChannelCodec,
  534 + binaryMessenger: pigeonVar_binaryMessenger,
  535 + );
  536 + final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[sqliteFilePath]);
  537 + final List<Object?>? pigeonVar_replyList =
  538 + await pigeonVar_sendFuture as List<Object?>?;
  539 + if (pigeonVar_replyList == null) {
  540 + throw _createConnectionError(pigeonVar_channelName);
  541 + } else if (pigeonVar_replyList.length > 1) {
  542 + throw PlatformException(
  543 + code: pigeonVar_replyList[0]! as String,
  544 + message: pigeonVar_replyList[1] as String?,
  545 + details: pigeonVar_replyList[2],
  546 + );
  547 + } else if (pigeonVar_replyList[0] == null) {
  548 + throw PlatformException(
  549 + code: 'null-error',
  550 + message: 'Host platform returned null value for non-null return value.',
  551 + );
  552 + } else {
  553 + return (pigeonVar_replyList[0] as bool?)!;
  554 + }
  555 + }
  556 +
  557 + /// 从从AppleHealth中读取睡眠数据
  558 + Future<List<HealthKitRawSleepDataPoint>> getHealthKitRawSleepData(int startTime, int endTime) async {
  559 + final String pigeonVar_channelName = 'dev.flutter.pigeon.doublefeel_flutter.HealthKitRawDataHostApi.getHealthKitRawSleepData$pigeonVar_messageChannelSuffix';
502 final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>( 560 final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
503 pigeonVar_channelName, 561 pigeonVar_channelName,
504 pigeonChannelCodec, 562 pigeonChannelCodec,
@@ -521,13 +579,13 @@ class HealthKitRawDataHostApi { @@ -521,13 +579,13 @@ class HealthKitRawDataHostApi {
521 message: 'Host platform returned null value for non-null return value.', 579 message: 'Host platform returned null value for non-null return value.',
522 ); 580 );
523 } else { 581 } else {
524 - return (pigeonVar_replyList[0] as List<Object?>?)!.cast<HealthKitRawActivityDataPoint>(); 582 + return (pigeonVar_replyList[0] as List<Object?>?)!.cast<HealthKitRawSleepDataPoint>();
525 } 583 }
526 } 584 }
527 585
528 - /// 从从AppleHealth中读取锻炼数据  
529 - Future<List<HealthKitRawWorkoutDataPoint>> getHealthKitRawWorkoutData(int startTime, int endTime) async {  
530 - final String pigeonVar_channelName = 'dev.flutter.pigeon.doublefeel_flutter.HealthKitRawDataHostApi.getHealthKitRawWorkoutData$pigeonVar_messageChannelSuffix'; 586 + /// 从从AppleHealth中读取活动统计数据
  587 + Future<List<HealthKitRawActivityDataPoint>> getHealthKitRawActivityData(int startTime, int endTime) async {
  588 + final String pigeonVar_channelName = 'dev.flutter.pigeon.doublefeel_flutter.HealthKitRawDataHostApi.getHealthKitRawActivityData$pigeonVar_messageChannelSuffix';
531 final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>( 589 final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
532 pigeonVar_channelName, 590 pigeonVar_channelName,
533 pigeonChannelCodec, 591 pigeonChannelCodec,
@@ -550,13 +608,13 @@ class HealthKitRawDataHostApi { @@ -550,13 +608,13 @@ class HealthKitRawDataHostApi {
550 message: 'Host platform returned null value for non-null return value.', 608 message: 'Host platform returned null value for non-null return value.',
551 ); 609 );
552 } else { 610 } else {
553 - return (pigeonVar_replyList[0] as List<Object?>?)!.cast<HealthKitRawWorkoutDataPoint>(); 611 + return (pigeonVar_replyList[0] as List<Object?>?)!.cast<HealthKitRawActivityDataPoint>();
554 } 612 }
555 } 613 }
556 614
557 /// 从从AppleHealth中读取锻炼数据 615 /// 从从AppleHealth中读取锻炼数据
558 - Future<List<HealthKitRawMotionDataPoint>> getHealthKitRawMotionData(int startTime, int endTime) async {  
559 - final String pigeonVar_channelName = 'dev.flutter.pigeon.doublefeel_flutter.HealthKitRawDataHostApi.getHealthKitRawMotionData$pigeonVar_messageChannelSuffix'; 616 + Future<List<HealthKitRawWorkoutDataPoint>> getHealthKitRawWorkoutData(int startTime, int endTime) async {
  617 + final String pigeonVar_channelName = 'dev.flutter.pigeon.doublefeel_flutter.HealthKitRawDataHostApi.getHealthKitRawWorkoutData$pigeonVar_messageChannelSuffix';
560 final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>( 618 final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
561 pigeonVar_channelName, 619 pigeonVar_channelName,
562 pigeonChannelCodec, 620 pigeonChannelCodec,
@@ -579,7 +637,7 @@ class HealthKitRawDataHostApi { @@ -579,7 +637,7 @@ class HealthKitRawDataHostApi {
579 message: 'Host platform returned null value for non-null return value.', 637 message: 'Host platform returned null value for non-null return value.',
580 ); 638 );
581 } else { 639 } else {
582 - return (pigeonVar_replyList[0] as List<Object?>?)!.cast<HealthKitRawMotionDataPoint>(); 640 + return (pigeonVar_replyList[0] as List<Object?>?)!.cast<HealthKitRawWorkoutDataPoint>();
583 } 641 }
584 } 642 }
585 } 643 }
@@ -102,6 +102,18 @@ abstract class HealthKitRawDataHostApi { @@ -102,6 +102,18 @@ abstract class HealthKitRawDataHostApi {
102 @async 102 @async
103 bool performHealthDataUpload(); 103 bool performHealthDataUpload();
104 104
  105 + /// 上传心率 - 实时压力数据, 返回上传截止的时间戳, 方便flutter修改数据库
  106 + @async
  107 + int performHRDataUpload({required String sqliteFilePath});
  108 +
  109 + /// 上传HRV - HRV压力数据, 返回上传截止的时间戳,方便flutter 修改数据库
  110 + @async
  111 + int performHRVDataUpload({required String sqliteFilePath});
  112 +
  113 + /// 上传当前实时压力(统计维度是当天)
  114 + @async
  115 + bool performAvgRealtimeStressDataUpload({required String sqliteFilePath});
  116 +
105 /// 从从AppleHealth中读取睡眠数据 117 /// 从从AppleHealth中读取睡眠数据
106 @async 118 @async
107 List<HealthKitRawSleepDataPoint> getHealthKitRawSleepData( 119 List<HealthKitRawSleepDataPoint> getHealthKitRawSleepData(
@@ -116,9 +128,4 @@ abstract class HealthKitRawDataHostApi { @@ -116,9 +128,4 @@ abstract class HealthKitRawDataHostApi {
116 @async 128 @async
117 List<HealthKitRawWorkoutDataPoint> getHealthKitRawWorkoutData( 129 List<HealthKitRawWorkoutDataPoint> getHealthKitRawWorkoutData(
118 int startTime, int endTime); 130 int startTime, int endTime);
119 -  
120 - /// 从从AppleHealth中读取锻炼数据  
121 - @async  
122 - List<HealthKitRawMotionDataPoint> getHealthKitRawMotionData(  
123 - int startTime, int endTime);  
124 } 131 }
@@ -287,4 +287,56 @@ void main() { @@ -287,4 +287,56 @@ void main() {
287 HealthRawStressState.attention.value); 287 HealthRawStressState.attention.value);
288 }); 288 });
289 }); 289 });
  290 +
  291 + group('LocalHealthDataConvert activity unit conversion', () {
  292 + test('passes through exercise and stand seconds from raw activity data',
  293 + () {
  294 + final day = DateTime(2026, 7, 16);
  295 + final endTime = LocalHealthDataConvert.unixSeconds(
  296 + day.add(const Duration(hours: 12)),
  297 + );
  298 + final activity = [
  299 + HealthKitRawActivityDataPoint(
  300 + endTime: endTime,
  301 + activeEnergyBurned: 300,
  302 + activeEnergyBurnedGoal: 500,
  303 + appleMoveTime: 8000,
  304 + appleMoveTimeGoal: 10000,
  305 + appleExerciseTime: 1800,
  306 + exerciseTimeGoal: 3600,
  307 + appleStandHours: 21600,
  308 + standHoursGoal: 43200,
  309 + ),
  310 + ];
  311 +
  312 + final healthData = LocalHealthDataConvert.v2HealthData(
  313 + day: day,
  314 + hrvPoints: const [],
  315 + activity: activity,
  316 + sleepingHeartRate: const [],
  317 + restingHeartRate: const [],
  318 + sleepIntervals: const [],
  319 + );
  320 + final target = LocalHealthDataConvert.v2ActivityTarget(
  321 + userId: 1,
  322 + activity: activity,
  323 + );
  324 + final statistics = LocalHealthDataConvert.activityBurnStatistics(
  325 + days: [day],
  326 + previousDays: const [],
  327 + activity: activity,
  328 + heartRate: const [],
  329 + sleepIntervals: const [],
  330 + );
  331 +
  332 + expect(healthData.exercise, 1800);
  333 + expect(healthData.stand, 21600);
  334 + expect(target.exercise, 3600);
  335 + expect(target.stand, 43200);
  336 + expect(statistics.totalExercise, 1800);
  337 + expect(statistics.totalStand, 21600);
  338 + expect(statistics.activityTargetInfo?.exercise, 3600);
  339 + expect(statistics.activityTargetInfo?.stand, 43200);
  340 + });
  341 + });
290 } 342 }