diff --git a/lib/screens/settings_screen.dart b/lib/screens/settings_screen.dart index e48e420..f430e99 100644 --- a/lib/screens/settings_screen.dart +++ b/lib/screens/settings_screen.dart @@ -341,7 +341,7 @@ class _SettingsScreenState extends State { keyboardType: const TextInputType.numberWithOptions(decimal: true), decoration: const InputDecoration( labelText: 'Meters', - helperText: 'Valid range: 1 to 1000 meters', + helperText: 'Valid range: 10 to 1000 meters', ), ), actions: [ @@ -353,7 +353,7 @@ class _SettingsScreenState extends State { onPressed: () { final parsed = double.tryParse(controller.text.trim()); if (parsed == null) return; - Navigator.pop(context, parsed.clamp(1.0, 1000.0)); + Navigator.pop(context, parsed.clamp(10.0, 1000.0)); }, child: const Text('Save'), ), @@ -382,7 +382,7 @@ class _SettingsScreenState extends State { keyboardType: TextInputType.number, decoration: const InputDecoration( labelText: 'Seconds', - helperText: 'Valid range: 5 to 60 seconds', + helperText: 'Valid range: 10 to 31 seconds', ), ), actions: [ @@ -394,7 +394,7 @@ class _SettingsScreenState extends State { onPressed: () { final parsed = int.tryParse(controller.text.trim()); if (parsed == null) return; - Navigator.pop(context, parsed.clamp(5, 60)); + Navigator.pop(context, parsed.clamp(10, 31)); }, child: const Text('Save'), ), diff --git a/lib/services/app_config_snapshot_service.dart b/lib/services/app_config_snapshot_service.dart index fa02a91..eda29b0 100644 --- a/lib/services/app_config_snapshot_service.dart +++ b/lib/services/app_config_snapshot_service.dart @@ -144,11 +144,11 @@ class AppConfigSnapshotService { } if (section.fastLocationMovementThresholdMeters != null) { locationTracking.fastLocationMovementThresholdMeters = - section.fastLocationMovementThresholdMeters!; + section.fastLocationMovementThresholdMeters!.clamp(10.0, 1000.0); } if (section.fastLocationActiveCadenceSeconds != null) { locationTracking.fastLocationActiveCadenceSeconds = - section.fastLocationActiveCadenceSeconds!; + section.fastLocationActiveCadenceSeconds!.clamp(10, 31); } await locationTracking.saveSettings(); await appProvider.reloadProfileScopedSettings(); diff --git a/lib/services/location_tracking_service.dart b/lib/services/location_tracking_service.dart index e1b8889..efb4699 100644 --- a/lib/services/location_tracking_service.dart +++ b/lib/services/location_tracking_service.dart @@ -20,6 +20,14 @@ import 'profiles_feature_service.dart'; /// - MeshCore mesh network integration /// - Real-time position updates via callbacks class LocationTrackingService { + static const double _defaultFastLocationMovementThresholdMeters = 10.0; + static const double _minFastLocationMovementThresholdMeters = 10.0; + static const int _defaultFastLocationActiveCadenceSeconds = 10; + static const int _minFastLocationActiveCadenceSeconds = 10; + static const int _maxFastLocationActiveCadenceSeconds = 31; + static const Duration _fastLocationMinimumUpdateInterval = Duration( + seconds: 31, + ); // ============================================================================ // Singleton Pattern // ============================================================================ @@ -75,10 +83,12 @@ class LocationTrackingService { bool fastLocationUpdatesEnabled = false; /// Distance threshold for fast GPS updates - double fastLocationMovementThresholdMeters = 10.0; + double fastLocationMovementThresholdMeters = + _defaultFastLocationMovementThresholdMeters; /// Cadence for active-use fast GPS updates - int fastLocationActiveCadenceSeconds = 10; + int fastLocationActiveCadenceSeconds = + _defaultFastLocationActiveCadenceSeconds; /// Target channel index for fast GPS updates; null means disabled/unset. int? fastLocationChannelIdx; @@ -522,12 +532,18 @@ class LocationTrackingService { } Future updateFastLocationMovementThreshold(double meters) async { - fastLocationMovementThresholdMeters = meters.clamp(1.0, 1000.0); + fastLocationMovementThresholdMeters = meters.clamp( + _minFastLocationMovementThresholdMeters, + 1000.0, + ); await saveSettings(); } Future updateFastLocationActiveCadenceSeconds(int seconds) async { - fastLocationActiveCadenceSeconds = seconds.clamp(5, 60); + fastLocationActiveCadenceSeconds = seconds.clamp( + _minFastLocationActiveCadenceSeconds, + _maxFastLocationActiveCadenceSeconds, + ); await saveSettings(); _refreshFastLocationTimer(); } @@ -583,6 +599,11 @@ class LocationTrackingService { final now = DateTime.now(); final previous = _lastFastLocationSentPosition; final previousTime = _lastFastLocationSentAt; + if (previousTime != null && + now.difference(previousTime) < _fastLocationMinimumUpdateInterval) { + return; + } + if (previous != null && previousTime != null) { final distance = Geolocator.distanceBetween( previous.latitude, @@ -673,12 +694,14 @@ class LocationTrackingService { prefs.getBool(_scopedKey(_prefKeyFastLocationEnabled)) ?? false; fastLocationMovementThresholdMeters = (prefs.getDouble(_scopedKey(_prefKeyFastMovementThreshold)) ?? - gpsUpdateDistance) - .clamp(1.0, 1000.0); + _defaultFastLocationMovementThresholdMeters) + .clamp(_minFastLocationMovementThresholdMeters, 1000.0); fastLocationActiveCadenceSeconds = - (prefs.getInt(_scopedKey(_prefKeyFastActiveCadence)) ?? 10).clamp( - 5, - 60, + (prefs.getInt(_scopedKey(_prefKeyFastActiveCadence)) ?? + _defaultFastLocationActiveCadenceSeconds) + .clamp( + _minFastLocationActiveCadenceSeconds, + _maxFastLocationActiveCadenceSeconds, ); fastLocationChannelIdx = prefs.getInt(_scopedKey(_prefKeyFastChannelIdx)); diff --git a/pubspec.yaml b/pubspec.yaml index 85d02b5..212cd66 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 2026.0323.1+40 +version: 2026.0323.2+41 environment: sdk: ^3.9.2 diff --git a/test/services/location_tracking_service_test.dart b/test/services/location_tracking_service_test.dart index 29dc77b..34b4c99 100644 --- a/test/services/location_tracking_service_test.dart +++ b/test/services/location_tracking_service_test.dart @@ -19,6 +19,13 @@ void main() { service.fastLocationChannelIdx = null; }); + test('loads conservative fast location defaults', () async { + await service.loadSettings(); + + expect(service.fastLocationMovementThresholdMeters, 10.0); + expect(service.fastLocationActiveCadenceSeconds, 10); + }); + test('persists and restores fast location channel idx', () async { await service.updateFastLocationChannelIdx(3); @@ -37,4 +44,12 @@ void main() { expect(service.fastLocationChannelIdx, isNull); }); + + test('clamps fast location settings to conservative limits', () async { + await service.updateFastLocationMovementThreshold(3); + await service.updateFastLocationActiveCadenceSeconds(45); + + expect(service.fastLocationMovementThresholdMeters, 10.0); + expect(service.fastLocationActiveCadenceSeconds, 31); + }); }