@@ -31,6 +31,8 @@ class LocationProvider with ChangeNotifier {
3131 VoidCallback ? onAlarmTriggered;
3232 StreamSubscription <Activity >? _activitySubscription;
3333 bool _isGpsActive = false ;
34+ Timer ? _adaptiveTimer;
35+ static const double kAdaptiveThreshold = 10000 ;
3436
3537 final FlutterLocalNotificationsPlugin _notificationsPlugin =
3638 FlutterLocalNotificationsPlugin ();
@@ -60,6 +62,44 @@ class LocationProvider with ChangeNotifier {
6062 );
6163 }
6264
65+ Future <void > _decideTrackingMode () async {
66+ if (_selectedLocationIndex == null || ! _isTracking) return ;
67+
68+ final position = await _locationService.getCurrentPosition (
69+ desiredAccuracy: LocationAccuracy .medium,
70+ );
71+
72+ final target = _locations[_selectedLocationIndex! ];
73+ final distance = _locationService.calculateDistance (
74+ position.latitude,
75+ position.longitude,
76+ target.latitude,
77+ target.longitude,
78+ );
79+
80+ logHere ("📍 Check: ${distance .toStringAsFixed (0 )}m away." );
81+
82+ if (distance > kAdaptiveThreshold) {
83+ if (_isGpsActive) _stopGpsStream ();
84+
85+ int intervalMinutes = (distance / 2000 ).floor ().clamp (2 , 30 );
86+
87+ logHere ("🌍 Far away (>10km). Sleeping for $intervalMinutes mins." );
88+
89+ _adaptiveTimer? .cancel ();
90+ _adaptiveTimer = Timer (Duration (minutes: intervalMinutes), () {
91+ _decideTrackingMode ();
92+ });
93+ } else {
94+ logHere ("🎯 Close (<10km)! Switching to High Precision Stream." );
95+ _adaptiveTimer? .cancel ();
96+
97+ if (! _isGpsActive) {
98+ _startGpsStream ();
99+ }
100+ }
101+ }
102+
63103 Future <void > _initActivityRecognition () async {
64104 // 1. Check or Request Permission
65105 bool isGranted = await Permission .activityRecognition.isGranted;
@@ -81,28 +121,30 @@ class LocationProvider with ChangeNotifier {
81121 logHere ("Detected Activity: ${activity .type }" );
82122
83123 if (activity.type == ActivityType .STILL ) {
84- // Device is STILL -> Pause GPS to save battery
85- if (_isGpsActive) {
86- logHere ("💤 Device is STILL. Pausing GPS." );
87- _stopGpsStream ();
88- }
124+ // STOP EVERYTHING when still
125+ logHere ("💤 Still. Pausing all location checks." );
126+ _stopGpsStream ();
127+ _adaptiveTimer? .cancel ();
89128 } else {
90- // Device is MOVING (Walking, Vehicle, etc.) -> Turn ON GPS
91- if (! _isGpsActive) {
92- logHere ("🚗 Device is MOVING. Activating GPS." );
93- _startGpsStream ();
129+ // MOVING? Let the brain decide strategy (Stream vs Timer)
130+ // Only trigger if we aren't already streaming or waiting on a timer
131+ if (! _isGpsActive &&
132+ (_adaptiveTimer == null || ! _adaptiveTimer! .isActive)) {
133+ logHere ("🚗 Moving. Calculating best tracking mode..." );
134+ _decideTrackingMode ();
94135 }
95136 }
96137 }
97138
98139 void _startGpsStream () {
99140 if (_selectedLocationIndex == null || _isGpsActive) return ;
100-
141+
101142 final target = _locations[_selectedLocationIndex! ];
102143 _isGpsActive = true ;
103144
104145 _locationService.startListening (
105- accuracy: locationAccuracyOptions[_settingsProvider.locationTrackingAccuracy],
146+ accuracy:
147+ locationAccuracyOptions[_settingsProvider.locationTrackingAccuracy],
106148 distanceFilter: 20 ,
107149 onUpdate: (position) {
108150 _sendRealtimeUpdate (position, target);
@@ -195,20 +237,19 @@ class LocationProvider with ChangeNotifier {
195237 }
196238
197239 void stopTracking () {
198- // 1. Stop Activity Listener
199240 _activitySubscription? .cancel ();
200241 _activitySubscription = null ;
201242
202- // 2. Stop GPS
203243 _stopGpsStream ();
244+ _adaptiveTimer? .cancel (); // NEW: Kill the timer
245+ _adaptiveTimer = null ;
204246
205- // 3. Cleanup Notifications & Background Service
206247 _notificationsPlugin.cancel (1 );
207248 _notificationsPlugin.cancel (0 );
208249 if (FlutterBackground .isBackgroundExecutionEnabled) {
209250 FlutterBackground .disableBackgroundExecution ();
210251 }
211-
252+
212253 _alarmTriggered = false ;
213254 _isTracking = false ;
214255 _isInitializingTracking = false ;
@@ -217,42 +258,18 @@ class LocationProvider with ChangeNotifier {
217258 }
218259
219260 Future <bool > _startTracking () async {
220- if (_selectedLocationIndex == null || ! _isTracking) return false ;
221- final target = _locations[_selectedLocationIndex! ];
261+ if (_selectedLocationIndex == null ) return false ;
222262
223263 try {
224- // 1. Initial Position Check (Force one-time fetch to ensure we aren't already there)
225- final position = await _locationService.getCurrentPosition (
226- desiredAccuracy: locationAccuracyOptions[_settingsProvider.locationTrackingAccuracy],
227- );
228-
229- final distance = _locationService.calculateDistance (
230- position.latitude, position.longitude,
231- target.latitude, target.longitude,
232- );
233-
234- if (distance <= target.radius) {
235- Fluttertoast .showToast (msg: msgAlreadyWithinRadius, toastLength: Toast .LENGTH_SHORT );
236- _isInitializingTracking = false ;
237- _isTracking = false ;
238- notifyListeners ();
239- return false ;
240- }
241-
242- // 2. Start Smart Tracking (Activity Recognition)
243- // We start GPS immediately as a safety measure, the Activity Listener
244- // will turn it off in a few seconds if it detects we are 'STILL'.
245- _startGpsStream ();
264+ // 1. Start Activity Recognition
246265 await _initActivityRecognition ();
247-
248- return true ;
249266
267+ // 2. Trigger immediate check to decide strategy
268+ await _decideTrackingMode ();
269+
270+ return true ;
250271 } catch (e) {
251272 logHere ('$kLogInitialPositionFailed $e ' );
252- Fluttertoast .showToast (msg: msgUnableToFetch);
253- _isInitializingTracking = false ;
254- _isTracking = false ;
255- notifyListeners ();
256273 return false ;
257274 }
258275 }
0 commit comments