web_flutter_tank_appwrite_app/lib/controllers/geolocation_advanced_controller.dart

354 lines
11 KiB
Dart

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<Position>();
final _previousPosition = Rxn<Position>();
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 = <Position>[].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<Position> get positionHistory => _positionHistory;
@override
void onClose() {
stopTracking();
super.onClose();
}
/// Holt die aktuelle Position einmalig
Future<void> 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<void> 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<void> 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<void> 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<void> toggleTracking() async {
if (_isTracking.value) {
await stopTracking();
} else {
await startTracking();
}
}
/// Öffnet die Location Einstellungen
Future<void> openLocationSettings() async {
await GeolocationService.openLocationSettings();
}
/// Öffnet die App Einstellungen
Future<void> openAppSettings() async {
await GeolocationService.openAppSettings();
}
/// Exportiert Tracking-Daten als Map
Map<String, dynamic> 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<String, double>? getTrackCenter() {
if (_positionHistory.isEmpty) return null;
return GeoUtils.calculateCentroid(_positionHistory);
}
/// Berechnet Bounding Box aller getrackten Positionen
Map<String, double>? 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!);
}
}