Skip to content

Commit 5efab7d

Browse files
sprobst76claude
andcommitted
fix(geofence): fix root causes of fragmented tracking and stuck Start button
Three bugs fixed: 1. Asymmetric bounce protection (geofence_event_queue.dart): - Before: bounced BOTH ENTER→EXIT and EXIT→ENTER within 15 min - Problem: user returning after a real/false exit was blocked for 15 min → ENTER event bounced → no new session → Start button stuck - Fix: only bounce ENTER→EXIT (GPS drift stopping work too early) EXIT→ENTER is never bounced so re-entry always creates a new session - Window extended from 15 to 20 min for ENTER→EXIT protection 2. Minimum session duration (geofence_sync_service.dart): - Before: any EXIT immediately stopped the running session - Fix: ignore EXIT if session has been running < 25 min GPS drift cannot cause a false stop in the first 25 min of work - Real exits after 25+ min of work are still processed normally 3. Don't restart geofence service if already running (geofence_service.dart): - Before: startGeofencingService() called on every app open - Problem: service restart may trigger spurious enter/exit events - Fix: check isForegroundServiceRunning() first, only start if stopped Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent d5686d8 commit 5efab7d

File tree

4 files changed

+35
-18
lines changed

4 files changed

+35
-18
lines changed

lib/services/geofence_event_queue.dart

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,15 @@ class GeofenceEventQueue {
7474
return resultDuplicate; // Duplikat ignorieren
7575
}
7676

77-
// Bounce-Protection: EXIT→ENTER oder ENTER→EXIT innerhalb von 15 Minuten ignorieren
78-
// Verhindert zerstückelte Einträge bei GPS-Fluktuation an der Zonengrenze
79-
// 15 Minuten, da GPS-Drift oft länger als 5 Min außerhalb der Zone zeigt
80-
if (lastEvent.zoneId == event.zoneId &&
81-
lastEvent.event != event.event &&
82-
timeDiff < 900) { // 15 Minuten
83-
return resultBounce; // Bounce ignorieren
77+
// Bounce-Protection: Nur ENTER→EXIT innerhalb von 20 Minuten ignorieren.
78+
// GPS-Drift kann den User bis zu 20 Min außerhalb der Zone zeigen, ohne dass
79+
// er wirklich gegangen ist. EXIT→ENTER wird NICHT gebounced, damit der User
80+
// nach einem echten oder falschen Exit sofort wieder eingeloggt werden kann.
81+
if (lastEvent.event == GeofenceEvent.enter &&
82+
event.event == GeofenceEvent.exit &&
83+
lastEvent.zoneId == event.zoneId &&
84+
timeDiff < 1200) { // 20 Minuten
85+
return resultBounce; // Bounce: zu früher Exit ignoriert
8486
}
8587
}
8688

lib/services/geofence_service.dart

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,30 @@ import 'geofence_callback.dart';
1010

1111
class MyGeofenceService {
1212
/// Startet den Foreground-Service und fügt die konfigurierten Geofence-Zonen hinzu.
13+
/// Wenn der Service bereits läuft, werden nur die Zonen neu registriert.
1314
Future<void> init({required double lat, required double lng}) async {
1415
final status = await Permission.locationAlways.request();
1516
if (!status.isGranted) {
1617
throw Exception('Standort-Berechtigung verweigert');
1718
}
1819

19-
final started = await GeofenceForegroundService().startGeofencingService(
20-
notificationChannelId: 'time_tracker_channel',
21-
contentTitle: 'TimeTracker im Hintergrund',
22-
contentText: 'Zonenüberwachung aktiv',
23-
serviceId: 1,
24-
callbackDispatcher: callbackDispatcher,
25-
);
26-
if (!started) {
27-
throw Exception('Geofencing-Service konnte nicht gestartet werden');
20+
final alreadyRunning =
21+
await GeofenceForegroundService().isForegroundServiceRunning();
22+
23+
if (!alreadyRunning) {
24+
final started = await GeofenceForegroundService().startGeofencingService(
25+
notificationChannelId: 'time_tracker_channel',
26+
contentTitle: 'TimeTracker im Hintergrund',
27+
contentText: 'Zonenüberwachung aktiv',
28+
serviceId: 1,
29+
callbackDispatcher: callbackDispatcher,
30+
);
31+
if (!started) {
32+
throw Exception('Geofencing-Service konnte nicht gestartet werden');
33+
}
2834
}
2935

30-
// Lade konfigurierte Zonen aus Hive
36+
// Zonen immer neu registrieren (Service behält bestehende Zonen bei)
3137
await _registerConfiguredZones(lat, lng);
3238
}
3339

lib/services/geofence_sync_service.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,15 @@ class GeofenceSyncService {
109109
return;
110110
}
111111

112+
// Mindest-Sitzungsdauer: Erst nach 25 Minuten automatisch stoppen.
113+
// GPS-Drift kann echte Sessions früh unterbrechen – kurze Sessions
114+
// werden daher als Fehler gewertet und ignoriert.
115+
final sessionDuration = event.timestamp.difference(runningEntry.start);
116+
if (sessionDuration.inMinutes < 25) {
117+
log('GeofenceSyncService: EXIT ignored – session too short (${sessionDuration.inMinutes} min < 25 min)');
118+
return;
119+
}
120+
112121
// Arbeitszeit beenden
113122
final startTime = runningEntry.start;
114123
runningEntry.stop = event.timestamp;

pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: time_tracker
22
description: Zeiterfassung mit Geofence, Urlaub, Feiertagen & ICS-Sync
3-
version: 0.1.0-beta.39+39
3+
version: 0.1.0-beta.40+40
44
publish_to: 'none'
55

66
environment:

0 commit comments

Comments
 (0)