mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge.git
synced 2026-07-31 07:44:24 +02:00
Health Connect: Fix duplicate and truncated sleep records across slice boundaries
SleepSyncer was inserting the same sleep session in multiple slices when the session spanned a slice boundary. The 1-day look-back caused the session to be re-discovered in adjacent slices, and the overlap check allowed it to be synced in each one. Assign each session to the slice where its start time falls ([sliceStart, sliceEnd)). This ownership rule ensures each session is synced exactly once regardless of how many slices it overlaps with. Add a 12-hour look-forward to the DB query for SLEEP so that sessions starting near the slice end are fetched with their full data, preventing truncation at the slice boundary. Fixes: https://codeberg.org/Freeyourgadget/Gadgetbridge/issues/5873
This commit is contained in:
+8
-2
@@ -242,6 +242,7 @@ class HealthConnectUtils {
|
||||
val manager = GBApplication.app().deviceManager
|
||||
val syncIntervalInSeconds: Long = 24 * 60 * 60 // 1 day slices
|
||||
val lookBackInSeconds: Long = 24 * 60 * 60
|
||||
val sleepLookForwardSeconds: Long = 12 * 60 * 60
|
||||
|
||||
for (targetAddress in selectedDevices) {
|
||||
// Check if worker has been cancelled
|
||||
@@ -363,14 +364,19 @@ class HealthConnectUtils {
|
||||
updateSyncStatus(summary, true, summaryCallback, mainHandler)
|
||||
|
||||
val queryStartTs = currentSliceStartTs.minusSeconds(lookBackInSeconds)
|
||||
LOG.info("$HC_SYNC_TAG Querying Gadgetbridge DB for {}({}) from {} to {}", gbDevice.aliasOrName, dataType.name, queryStartTs, currentSliceEndTs)
|
||||
val queryEndTs = if (dataType == HealthConnectPermissionManager.HealthConnectDataType.SLEEP) {
|
||||
currentSliceEndTs.plusSeconds(sleepLookForwardSeconds)
|
||||
} else {
|
||||
currentSliceEndTs
|
||||
}
|
||||
LOG.info("$HC_SYNC_TAG Querying Gadgetbridge DB for {}({}) from {} to {}", gbDevice.aliasOrName, dataType.name, queryStartTs, queryEndTs)
|
||||
|
||||
// Fetch activityBasedSamples under their own lock if needed for the current dataType
|
||||
val activityBasedSamples: List<ActivitySample>? =
|
||||
if (dataType == HealthConnectPermissionManager.HealthConnectDataType.ACTIVITY ||
|
||||
dataType == HealthConnectPermissionManager.HealthConnectDataType.SLEEP) {
|
||||
GBApplication.acquireDbReadOnly().use { db ->
|
||||
getActivitySamples(db, gbDevice, queryStartTs.epochSecond.toInt(), currentSliceEndTs.epochSecond.toInt())
|
||||
getActivitySamples(db, gbDevice, queryStartTs.epochSecond.toInt(), queryEndTs.epochSecond.toInt())
|
||||
}
|
||||
} else {
|
||||
null
|
||||
|
||||
+7
-4
@@ -123,11 +123,14 @@ internal object SleepSyncer : ContextualActivitySampleSyncer {
|
||||
val sessionBoundaryStart = analysisSession.sleepStart.toInstant()
|
||||
val sessionBoundaryEndInclusive = analysisSession.sleepEnd.toInstant()
|
||||
|
||||
// Check if session overlaps with the current slice (inclusive boundaries [sliceStart, sliceEnd])
|
||||
// Skip if: sessionStart > sliceEnd OR sessionEnd < sliceStart
|
||||
if (sessionBoundaryStart.isAfter(sliceEndBoundary) || sessionBoundaryEndInclusive.isBefore(sliceStartBoundary)) {
|
||||
// Only process this session if its START falls within the current slice [sliceStart, sliceEnd).
|
||||
// The look-back query ensures full session data is available even for sessions starting near the
|
||||
// previous slice boundary. The look-forward query ensures full session data for sessions starting
|
||||
// near the current slice end. This ownership rule prevents duplicate records when the same session
|
||||
// is discovered across multiple slices due to the look-back overlap.
|
||||
if (sessionBoundaryStart.isBefore(sliceStartBoundary) || !sessionBoundaryStart.isBefore(sliceEndBoundary)) {
|
||||
LOG.debug(
|
||||
"Skipping sleep session (identified by SleepAnalysis) for device '{}' (Timeframe: {} to {}) as it does not overlap with current slice ({} to {}).",
|
||||
"Skipping sleep session (identified by SleepAnalysis) for device '{}' (Timeframe: {} to {}) as its start does not fall within current slice [{} to {}).",
|
||||
deviceName,
|
||||
sessionBoundaryStart,
|
||||
sessionBoundaryEndInclusive,
|
||||
|
||||
Reference in New Issue
Block a user