|
...
|
...
|
@@ -44,8 +44,9 @@ actor NativeHealthDataUploader { |
|
|
|
|
|
|
|
private let session: URLSession
|
|
|
|
private let defaultUploadTimeWeek = 1
|
|
|
|
private let firstUploadYear = 2
|
|
|
|
private let firstUploadYear = 1
|
|
|
|
private let uploadBatchSize = 500
|
|
|
|
private let sleepUploadLookback: TimeInterval = 24 * 60 * 60
|
|
|
|
private var lastUploadTriggerDates: [NativeHealthDataType: Date] = [:]
|
|
|
|
|
|
|
|
init(session: URLSession = .shared) {
|
|
...
|
...
|
@@ -371,6 +372,10 @@ private extension NativeHealthDataUploader { |
|
|
|
}
|
|
|
|
DebugLogger.debugLog("[activityTarget] uploading, body=\(target.uploadBodyForDebug)")
|
|
|
|
try await uploadActivityTarget(target)
|
|
|
|
let uploadedTypes = try await uploadActivityTargetHealthValues(target)
|
|
|
|
uploadedTypes.forEach {
|
|
|
|
notifyFlutterUpload(type: $0.type, timestamp: $0.timestamp)
|
|
|
|
}
|
|
|
|
DebugLogger.debugLog("[activityTarget] upload success")
|
|
|
|
return UploadTaskResult(success: true, uploadedCount: 1, errorMessage: nil)
|
|
|
|
} catch {
|
|
...
|
...
|
@@ -394,10 +399,23 @@ private extension NativeHealthDataUploader { |
|
|
|
|
|
|
|
for type in NativeHealthDataType.allCases where type != .unknown {
|
|
|
|
let serverTime = serverTimeList.latestTimeInterval(for: type)
|
|
|
|
let finalTime = serverTime ?? twoYearsAgoMidnight
|
|
|
|
let finalTime: TimeInterval
|
|
|
|
let sourceDescription: String
|
|
|
|
if type == .sleep {
|
|
|
|
if let serverTime {
|
|
|
|
finalTime = max(0, serverTime - sleepUploadLookback)
|
|
|
|
sourceDescription = "server minus 24h"
|
|
|
|
} else {
|
|
|
|
finalTime = twoYearsAgoMidnight
|
|
|
|
sourceDescription = "missing; first sleep upload uses two years"
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
finalTime = serverTime ?? twoYearsAgoMidnight
|
|
|
|
sourceDescription = serverTime == nil ? "missing; first upload uses two years" : "server"
|
|
|
|
}
|
|
|
|
|
|
|
|
DebugLogger.debugLog(
|
|
|
|
"[\(type.debugName)] server start time=\(serverTime.map { Self.debugDateFormatter.string(from: Date(timeIntervalSince1970: $0)) } ?? "<missing; first upload uses two years>"), resolved=\(Self.debugDateFormatter.string(from: Date(timeIntervalSince1970: finalTime)))"
|
|
|
|
"[\(type.debugName)] server start time=\(serverTime.map { Self.debugDateFormatter.string(from: Date(timeIntervalSince1970: $0)) } ?? "<missing>"), source=\(sourceDescription), resolved=\(Self.debugDateFormatter.string(from: Date(timeIntervalSince1970: finalTime)))"
|
|
|
|
)
|
|
|
|
resultTimeList.append(
|
|
|
|
NativeHealthUploadTimeList.HealthUploadTime(
|
|
...
|
...
|
@@ -475,6 +493,27 @@ private extension NativeHealthDataUploader { |
|
|
|
try await request(path: "/client/doublefeel/health/v2/activity_target/", method: "POST", body: target.uploadBody)
|
|
|
|
}
|
|
|
|
|
|
|
|
func uploadActivityTargetHealthValues(
|
|
|
|
_ target: NativeActivityTarget,
|
|
|
|
timestamp: TimeInterval = Date().timeIntervalSince1970
|
|
|
|
) async throws -> [(type: NativeHealthDataType, timestamp: TimeInterval)] {
|
|
|
|
let values: [(NativeHealthDataType, Double?)] = [
|
|
|
|
(.activeEnergy, target.activeEnergyBurned),
|
|
|
|
(.exercise, target.appleExerciseTime),
|
|
|
|
(.stand, target.appleStandHours),
|
|
|
|
]
|
|
|
|
let data = values.compactMap { item -> NativeHealthDataPoint? in
|
|
|
|
let (type, value) = item
|
|
|
|
guard let value else { return nil }
|
|
|
|
return NativeHealthDataPoint(dataType: type, time: timestamp, value: value)
|
|
|
|
}
|
|
|
|
guard !data.isEmpty else { return [] }
|
|
|
|
|
|
|
|
DebugLogger.debugLog("[activityTarget] uploading common values, count=\(data.count)")
|
|
|
|
try await uploadCommon(data)
|
|
|
|
return data.map { ($0.dataType, $0.time) }
|
|
|
|
}
|
|
|
|
|
|
|
|
func fetchLastUploadTime() async throws -> NativeHealthUploadTimeList {
|
|
|
|
let data = try await request(path: "/client/doublefeel/health/v2/data_upload/common/", method: "GET")
|
|
|
|
return try NativeHealthUploadTimeList.decode(from: data)
|
...
|
...
|
|