refactor: Projektstruktur in app/ und backend/ aufgeteilt
This commit is contained in:
184
app/lib/services/location_service.dart
Normal file
184
app/lib/services/location_service.dart
Normal file
@@ -0,0 +1,184 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'package:geolocator/geolocator.dart';
|
||||
import 'package:votianlt_app/services/developer.dart' as developer;
|
||||
import 'websocket_service.dart';
|
||||
|
||||
/// Service for tracking and sending GPS location.
|
||||
/// Sends position every 30 seconds when online.
|
||||
/// Does not buffer location data when offline.
|
||||
class LocationService {
|
||||
static final LocationService _instance = LocationService._internal();
|
||||
|
||||
factory LocationService() => _instance;
|
||||
|
||||
LocationService._internal();
|
||||
|
||||
Timer? _locationTimer;
|
||||
bool _isTracking = false;
|
||||
Position? _lastPosition;
|
||||
|
||||
static const String _topic = '/server/location';
|
||||
static const int _sendIntervalSeconds = 30;
|
||||
|
||||
/// Check if location services are enabled and permission is granted
|
||||
Future<bool> _checkPermissions() async {
|
||||
// Check if location services are enabled
|
||||
bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
|
||||
if (!serviceEnabled) {
|
||||
developer.log(
|
||||
'Location services are disabled',
|
||||
name: 'LocationService',
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check location permission
|
||||
LocationPermission permission = await Geolocator.checkPermission();
|
||||
if (permission == LocationPermission.denied) {
|
||||
permission = await Geolocator.requestPermission();
|
||||
if (permission == LocationPermission.denied) {
|
||||
developer.log(
|
||||
'Location permission denied',
|
||||
name: 'LocationService',
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (permission == LocationPermission.deniedForever) {
|
||||
developer.log(
|
||||
'Location permission permanently denied',
|
||||
name: 'LocationService',
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Start location tracking and periodic sending
|
||||
Future<void> startTracking() async {
|
||||
if (_isTracking) {
|
||||
developer.log(
|
||||
'Location tracking already active',
|
||||
name: 'LocationService',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
final hasPermission = await _checkPermissions();
|
||||
if (!hasPermission) {
|
||||
developer.log(
|
||||
'Cannot start location tracking - permission not granted',
|
||||
name: 'LocationService',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
_isTracking = true;
|
||||
developer.log(
|
||||
'Starting location tracking (sending every $_sendIntervalSeconds seconds)',
|
||||
name: 'LocationService',
|
||||
);
|
||||
|
||||
// Get initial position
|
||||
await _updateAndSendPosition();
|
||||
|
||||
// Start periodic timer
|
||||
_locationTimer = Timer.periodic(
|
||||
const Duration(seconds: _sendIntervalSeconds),
|
||||
(_) => _updateAndSendPosition(),
|
||||
);
|
||||
}
|
||||
|
||||
/// Stop location tracking
|
||||
void stopTracking() {
|
||||
if (!_isTracking) return;
|
||||
|
||||
developer.log(
|
||||
'Stopping location tracking',
|
||||
name: 'LocationService',
|
||||
);
|
||||
|
||||
_locationTimer?.cancel();
|
||||
_locationTimer = null;
|
||||
_isTracking = false;
|
||||
}
|
||||
|
||||
/// Get current position and send to server if online
|
||||
Future<void> _updateAndSendPosition() async {
|
||||
try {
|
||||
final position = await Geolocator.getCurrentPosition(
|
||||
locationSettings: const LocationSettings(
|
||||
accuracy: LocationAccuracy.best,
|
||||
),
|
||||
);
|
||||
|
||||
_lastPosition = position;
|
||||
|
||||
developer.log(
|
||||
'Position updated: ${position.latitude}, ${position.longitude}',
|
||||
name: 'LocationService',
|
||||
);
|
||||
|
||||
await _sendPosition(position);
|
||||
} catch (e, st) {
|
||||
developer.log(
|
||||
'Error getting position: $e',
|
||||
name: 'LocationService',
|
||||
);
|
||||
developer.log('Stack: $st', name: 'LocationService');
|
||||
}
|
||||
}
|
||||
|
||||
/// Send position to server if online
|
||||
/// Does NOT buffer when offline - location data is time-sensitive
|
||||
Future<void> _sendPosition(Position position) async {
|
||||
final wsService = WebSocketService();
|
||||
|
||||
// Only send if connected and authenticated
|
||||
if (!wsService.isConnected || !wsService.isAuthenticated) {
|
||||
developer.log(
|
||||
'Not sending position - not connected/authenticated',
|
||||
name: 'LocationService',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
final payload = {
|
||||
'latitude': position.latitude,
|
||||
'longitude': position.longitude,
|
||||
'accuracy': position.accuracy,
|
||||
'altitude': position.altitude,
|
||||
'speed': position.speed,
|
||||
'heading': position.heading,
|
||||
'timestamp': position.timestamp.toIso8601String(),
|
||||
};
|
||||
|
||||
try {
|
||||
const topic = _topic;
|
||||
final jsonPayload = jsonEncode(payload);
|
||||
|
||||
// Use direct WebSocket send to avoid buffering
|
||||
wsService.sendMessage(topic, jsonPayload);
|
||||
|
||||
developer.log(
|
||||
'Position sent to server: ${position.latitude}, ${position.longitude}',
|
||||
name: 'LocationService',
|
||||
);
|
||||
} catch (e, st) {
|
||||
developer.log(
|
||||
'Error sending position: $e',
|
||||
name: 'LocationService',
|
||||
);
|
||||
developer.log('Stack: $st', name: 'LocationService');
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the last known position
|
||||
Position? get lastPosition => _lastPosition;
|
||||
|
||||
/// Check if tracking is active
|
||||
bool get isTracking => _isTracking;
|
||||
}
|
||||
Reference in New Issue
Block a user