SleepAnalysis: Bridge non-sleep gaps with no activity as awake sleep

Fix dead code bug where MAX_WAKE_PHASE_LENGTH could never trigger
because Block A unconditionally nulled sleepStart before Block B
could check it. Non-sleep samples now only break a session if the
user has step activity or the gap exceeds 1 hour. Bridged gaps are
counted as awake sleep duration.

See: #5757
This commit is contained in:
Gideon Zenz
2026-03-24 18:58:24 +01:00
committed by José Rebelo
parent 36e23fe08c
commit 4c966bf232
@@ -26,7 +26,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
public class SleepAnalysis {
public static final long MIN_SESSION_LENGTH = 5 * 60;
public static final long MAX_WAKE_PHASE_LENGTH = 2 * 60 * 60;
public static final long MAX_WAKE_PHASE_LENGTH = 60 * 60;
public List<SleepSession> calculateSleepSessions(Iterable<? extends ActivitySample> samples) {
List<SleepSession> result = new ArrayList<>();
@@ -47,18 +47,21 @@ public class SleepAnalysis {
sleepEnd = getDateFromSample(sample);
durationSinceLastSleep = 0;
} else {
//exclude "not worn" times from sleep sessions as this makes a discrepancy with the charts
final boolean validTimes = sleepStart != null && sleepEnd != null && sleepEnd.getTime() - sleepStart.getTime() > MIN_SESSION_LENGTH;
final long durationLengths = lightSleepDuration + deepSleepDuration + remSleepDuration + awakeSleepDuration;
if (validTimes && durationLengths > MIN_SESSION_LENGTH)
result.add(new SleepSession(sleepStart, sleepEnd, lightSleepDuration, deepSleepDuration, remSleepDuration, awakeSleepDuration));
sleepStart = null;
sleepEnd = null;
lightSleepDuration = 0;
deepSleepDuration = 0;
remSleepDuration = 0;
awakeSleepDuration = 0;
} else if (sleepStart != null) {
final long gap = previousSample != null ? sample.getTimestamp() - previousSample.getTimestamp() : 0;
final int steps = sample.getSteps();
final boolean hasActivity = steps > 0 && steps != ActivitySample.NOT_MEASURED;
if (hasActivity || durationSinceLastSleep + gap > MAX_WAKE_PHASE_LENGTH) {
final long durationLengths = lightSleepDuration + deepSleepDuration + remSleepDuration + awakeSleepDuration;
if (sleepEnd.getTime() - sleepStart.getTime() > MIN_SESSION_LENGTH && durationLengths > MIN_SESSION_LENGTH)
result.add(new SleepSession(sleepStart, sleepEnd, lightSleepDuration, deepSleepDuration, remSleepDuration, awakeSleepDuration));
sleepStart = null;
sleepEnd = null;
lightSleepDuration = 0;
deepSleepDuration = 0;
remSleepDuration = 0;
awakeSleepDuration = 0;
}
}
if (previousSample != null) {
@@ -73,15 +76,18 @@ public class SleepAnalysis {
awakeSleepDuration += durationSinceLastSample;
} else {
durationSinceLastSleep += durationSinceLastSample;
if (sleepStart != null && durationSinceLastSleep > MAX_WAKE_PHASE_LENGTH) {
if (lightSleepDuration + deepSleepDuration + remSleepDuration + awakeSleepDuration > MIN_SESSION_LENGTH)
result.add(new SleepSession(sleepStart, sleepEnd, lightSleepDuration, deepSleepDuration, remSleepDuration, awakeSleepDuration));
sleepStart = null;
sleepEnd = null;
lightSleepDuration = 0;
deepSleepDuration = 0;
remSleepDuration = 0;
awakeSleepDuration = 0;
if (sleepStart != null) {
awakeSleepDuration += durationSinceLastSample;
if (durationSinceLastSleep > MAX_WAKE_PHASE_LENGTH) {
if (lightSleepDuration + deepSleepDuration + remSleepDuration + awakeSleepDuration > MIN_SESSION_LENGTH)
result.add(new SleepSession(sleepStart, sleepEnd, lightSleepDuration, deepSleepDuration, remSleepDuration, awakeSleepDuration));
sleepStart = null;
sleepEnd = null;
lightSleepDuration = 0;
deepSleepDuration = 0;
remSleepDuration = 0;
awakeSleepDuration = 0;
}
}
}
}