354 lines
11 KiB
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!);
|
|
}
|
|
} |