import 'package:get/get.dart'; import 'package:geolocator/geolocator.dart'; import '../services/geolocation.dart'; import '../utils/geo_utils.dart'; /// Erweiterte Geolocation Controller Klasse mit zusätzlichen Features class GeolocationAdvancedController extends GetxController { // Reactive Variablen final _currentPosition = Rxn(); final _previousPosition = Rxn(); final _statusText = 'Noch keine Position ermittelt'.obs; final _isTracking = false.obs; final _isLoading = false.obs; final _totalDistance = 0.0.obs; final _averageSpeed = 0.0.obs; final _maxSpeed = 0.0.obs; final _trackingDuration = 0.obs; final _positionHistory = [].obs; // Timer für Tracking-Dauer DateTime? _trackingStartTime; // Getter für reactive Variablen Position? get currentPosition => _currentPosition.value; Position? get previousPosition => _previousPosition.value; String get statusText => _statusText.value; bool get isTracking => _isTracking.value; bool get isLoading => _isLoading.value; double get totalDistance => _totalDistance.value; double get averageSpeed => _averageSpeed.value; double get maxSpeed => _maxSpeed.value; int get trackingDuration => _trackingDuration.value; List get positionHistory => _positionHistory; @override void onClose() { stopTracking(); super.onClose(); } /// Holt die aktuelle Position einmalig Future getCurrentPosition() async { try { _isLoading.value = true; _statusText.value = 'Position wird ermittelt...'; Position? position = await GeolocationService.getCurrentPosition(); if (position != null) { _updatePosition(position); _statusText.value = GeolocationService.positionToString(position); Get.snackbar( 'Position ermittelt', 'Genauigkeit: ${position.accuracy.toStringAsFixed(1)}m', snackPosition: SnackPosition.BOTTOM, ); } else { _statusText.value = 'Position konnte nicht ermittelt werden'; Get.snackbar( 'Fehler', 'Position konnte nicht ermittelt werden', snackPosition: SnackPosition.BOTTOM, ); } } catch (e) { _statusText.value = 'Fehler beim Abrufen der Position: $e'; Get.snackbar( 'Fehler', 'Unerwarteter Fehler: $e', snackPosition: SnackPosition.BOTTOM, ); } finally { _isLoading.value = false; } } /// Startet kontinuierliches Position Tracking mit Statistiken Future startTracking() async { if (_isTracking.value) return; try { _isLoading.value = true; _statusText.value = 'Tracking wird gestartet...'; // Reset Statistiken _resetTrackingStats(); _trackingStartTime = DateTime.now(); bool success = await GeolocationService.startPositionStream( onPositionChanged: (Position position) { _updatePosition(position); _updateTrackingStats(position); _statusText.value = _buildTrackingStatusText(position); }, onError: (String error) { _statusText.value = 'Tracking Fehler: $error'; _isTracking.value = false; Get.snackbar( 'Tracking Fehler', error, snackPosition: SnackPosition.BOTTOM, ); }, accuracy: LocationAccuracy.high, distanceFilter: 3, // Updates alle 3 Meter für genauere Statistiken ); if (success) { _isTracking.value = true; _statusText.value = 'Tracking aktiv - Warten auf erste Position...'; // Starte Timer für Tracking-Dauer _startDurationTimer(); Get.snackbar( 'Tracking gestartet', 'Sammle GPS-Daten und Statistiken...', snackPosition: SnackPosition.BOTTOM, ); } else { _statusText.value = 'Tracking konnte nicht gestartet werden'; Get.snackbar( 'Fehler', 'Tracking konnte nicht gestartet werden', snackPosition: SnackPosition.BOTTOM, ); } } catch (e) { _statusText.value = 'Fehler beim Starten des Trackings: $e'; Get.snackbar( 'Fehler', 'Tracking-Fehler: $e', snackPosition: SnackPosition.BOTTOM, ); } finally { _isLoading.value = false; } } /// Stoppt das Position Tracking Future stopTracking() async { if (!_isTracking.value) return; try { await GeolocationService.stopPositionStream(); _isTracking.value = false; _trackingStartTime = null; String summary = _buildTrackingSummary(); _statusText.value = 'Tracking gestoppt\n$summary'; Get.snackbar( 'Tracking gestoppt', summary, snackPosition: SnackPosition.BOTTOM, duration: const Duration(seconds: 5), ); } catch (e) { Get.snackbar( 'Fehler', 'Fehler beim Stoppen des Trackings: $e', snackPosition: SnackPosition.BOTTOM, ); } } /// Prüft Permission Status Future checkPermissions() async { try { _isLoading.value = true; _statusText.value = 'Berechtigungen werden geprüft...'; LocationPermission permission = await GeolocationService.checkPermission(); bool serviceEnabled = await GeolocationService.isLocationServiceEnabled(); _statusText.value = 'Service aktiv: $serviceEnabled\n' 'Berechtigung: ${LocationPermissionHelper.getPermissionStatusText(permission)}'; String permissionStatus = LocationPermissionHelper.getPermissionStatusText(permission); Get.snackbar( 'Berechtigungs-Status', 'Location Service: ${serviceEnabled ? "Aktiv" : "Inaktiv"}\n$permissionStatus', snackPosition: SnackPosition.BOTTOM, duration: const Duration(seconds: 4), ); } catch (e) { _statusText.value = 'Fehler beim Prüfen der Berechtigungen: $e'; Get.snackbar( 'Fehler', 'Berechtigungen konnten nicht geprüft werden: $e', snackPosition: SnackPosition.BOTTOM, ); } finally { _isLoading.value = false; } } /// Private Methoden void _updatePosition(Position position) { _previousPosition.value = _currentPosition.value; _currentPosition.value = position; _positionHistory.add(position); } void _updateTrackingStats(Position position) { if (_previousPosition.value != null) { // Berechne Distanz zur vorherigen Position double distance = GeoUtils.calculateDistanceKm( startLat: _previousPosition.value!.latitude, startLng: _previousPosition.value!.longitude, endLat: position.latitude, endLng: position.longitude, ); _totalDistance.value += distance; // Aktualisiere max Geschwindigkeit double currentSpeedKmh = GeoUtils.mpsToKmh(position.speed); if (currentSpeedKmh > _maxSpeed.value) { _maxSpeed.value = currentSpeedKmh; } // Berechne Durchschnittsgeschwindigkeit if (_trackingStartTime != null) { double hours = DateTime.now().difference(_trackingStartTime!).inMinutes / 60.0; if (hours > 0) { _averageSpeed.value = _totalDistance.value / hours; } } } } void _resetTrackingStats() { _totalDistance.value = 0.0; _averageSpeed.value = 0.0; _maxSpeed.value = 0.0; _trackingDuration.value = 0; _positionHistory.clear(); } String _buildTrackingStatusText(Position position) { return 'TRACKING AKTIV\n' 'Position: ${position.latitude.toStringAsFixed(6)}, ${position.longitude.toStringAsFixed(6)}\n' 'Genauigkeit: ${position.accuracy.toStringAsFixed(1)}m\n' 'Geschwindigkeit: ${GeoUtils.mpsToKmh(position.speed).toStringAsFixed(1)} km/h\n' 'Distanz: ${(_totalDistance.value * 1000).toStringAsFixed(0)}m\n' 'Dauer: ${_formatDuration(_trackingDuration.value)}'; } String _buildTrackingSummary() { return 'Gesamtdistanz: ${(_totalDistance.value * 1000).toStringAsFixed(0)}m\n' 'Max. Geschwindigkeit: ${_maxSpeed.value.toStringAsFixed(1)} km/h\n' 'Ø Geschwindigkeit: ${_averageSpeed.value.toStringAsFixed(1)} km/h\n' 'Dauer: ${_formatDuration(_trackingDuration.value)}\n' 'Punkte: ${_positionHistory.length}'; } void _startDurationTimer() { // Update duration every second while tracking Future.doWhile(() async { await Future.delayed(const Duration(seconds: 1)); if (_isTracking.value && _trackingStartTime != null) { _trackingDuration.value = DateTime.now().difference(_trackingStartTime!).inSeconds; return true; } return false; }); } String _formatDuration(int seconds) { int hours = seconds ~/ 3600; int minutes = (seconds % 3600) ~/ 60; int secs = seconds % 60; if (hours > 0) { return '${hours}h ${minutes}m ${secs}s'; } else if (minutes > 0) { return '${minutes}m ${secs}s'; } else { return '${secs}s'; } } // Zusätzliche Helper-Methoden /// Togglet das Tracking (Start/Stop) Future toggleTracking() async { if (_isTracking.value) { await stopTracking(); } else { await startTracking(); } } /// Öffnet die Location Einstellungen Future openLocationSettings() async { await GeolocationService.openLocationSettings(); } /// Öffnet die App Einstellungen Future openAppSettings() async { await GeolocationService.openAppSettings(); } /// Exportiert Tracking-Daten als Map Map exportTrackingData() { return { 'totalDistance': _totalDistance.value, 'averageSpeed': _averageSpeed.value, 'maxSpeed': _maxSpeed.value, 'duration': _trackingDuration.value, 'pointCount': _positionHistory.length, 'positions': _positionHistory.map((p) => { 'latitude': p.latitude, 'longitude': p.longitude, 'accuracy': p.accuracy, 'speed': p.speed, 'timestamp': p.timestamp.toIso8601String(), }).toList(), }; } /// Berechnet Mittelpunkt aller getrackten Positionen Map? getTrackCenter() { if (_positionHistory.isEmpty) return null; return GeoUtils.calculateCentroid(_positionHistory); } /// Berechnet Bounding Box aller getrackten Positionen Map? getTrackBounds() { if (_positionHistory.isEmpty) return null; return GeoUtils.calculateBoundingBox(_positionHistory); } /// Resettet alle Werte void reset() { stopTracking(); _currentPosition.value = null; _previousPosition.value = null; _statusText.value = 'Noch keine Position ermittelt'; _resetTrackingStats(); } // Getter für UI bool get hasPosition => _currentPosition.value != null; double get currentAccuracy => _currentPosition.value?.accuracy ?? 0.0; double get currentSpeedKmh => GeoUtils.mpsToKmh(_currentPosition.value?.speed ?? 0.0); String get formattedPosition { if (_currentPosition.value == null) return 'Keine Position verfügbar'; return GeolocationService.positionToString(_currentPosition.value!); } }