68 lines
2.2 KiB
Dart
68 lines
2.2 KiB
Dart
import 'dart:async';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:geolocator/geolocator.dart';
|
|
import 'package:permission_handler/permission_handler.dart';
|
|
|
|
final locationServiceProvider = Provider<LocationService>((ref) {
|
|
return LocationService();
|
|
});
|
|
|
|
final locationStreamProvider = StreamProvider<Position>((ref) {
|
|
final service = ref.watch(locationServiceProvider);
|
|
return service.getFilteredPositionStream();
|
|
});
|
|
|
|
class LocationService {
|
|
Position? _lastValidPosition;
|
|
|
|
// Filtering thresholds
|
|
static const double _maxAcceptableAccuracy = 20.0; // meters
|
|
static const double _maxRealisticSpeed = 100.0; // roughly 223 MPH in m/s
|
|
static const double _maxAcceleration = 20.0; // m/s^2 (unrealistic jump)
|
|
|
|
Future<bool> requestPermissions() async {
|
|
final status = await Permission.locationWhenInUse.request();
|
|
if (status.isGranted) {
|
|
// Optional: request background if needed later
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
Stream<Position> getFilteredPositionStream() {
|
|
final LocationSettings locationSettings = AndroidSettings(
|
|
accuracy: LocationAccuracy.bestForNavigation,
|
|
distanceFilter: 0,
|
|
intervalDuration: const Duration(milliseconds: 200), // 5Hz updates
|
|
// (Optional) foregroundNotificationConfig if we implement background tracking
|
|
);
|
|
|
|
return Geolocator.getPositionStream(locationSettings: locationSettings)
|
|
.where(_isValidPosition);
|
|
}
|
|
|
|
bool _isValidPosition(Position position) {
|
|
if (position.accuracy > _maxAcceptableAccuracy) {
|
|
return false; // Reject extremely poor accuracy
|
|
}
|
|
|
|
if (position.speed > _maxRealisticSpeed) {
|
|
return false; // Reject impossible speeds
|
|
}
|
|
|
|
if (_lastValidPosition != null) {
|
|
// Avoid division by zero and ensure positive time diff
|
|
final timeDiff = (position.timestamp.difference(_lastValidPosition!.timestamp)).inMilliseconds / 1000.0;
|
|
if (timeDiff > 0) {
|
|
final accel = (position.speed - _lastValidPosition!.speed).abs() / timeDiff;
|
|
if (accel > _maxAcceleration) {
|
|
return false; // Reject impossible acceleration spikes
|
|
}
|
|
}
|
|
}
|
|
|
|
_lastValidPosition = position;
|
|
return true;
|
|
}
|
|
}
|