diff --git a/lib/home.dart b/lib/home.dart index 2008499..6fcedbf 100644 --- a/lib/home.dart +++ b/lib/home.dart @@ -1,3 +1,4 @@ +import 'package:fforte/screens/sharedMethods/send_file.dart'; import 'package:flutter/material.dart'; import 'l10n/app_localizations.dart'; import 'screens/addCam/add_cam_main.dart'; @@ -101,7 +102,7 @@ class HomePage extends StatelessWidget { minimumSize: const Size(250, 40), ), onPressed: () { - // _sendFile(); + SendFile.sendFile(); }, child: Text(AppLocalizations.of(context)!.sendfile), ), diff --git a/lib/main.dart b/lib/main.dart index f1e366b..4b93c4b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -16,16 +16,19 @@ import 'l10n/l10n.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); + // Set default values (Propably there is a better way to do this) SharedPreferences prefs = await SharedPreferences.getInstance(); + // isFirstLaunch decides whether the intro screen is shown or not bool isFirstLaunch = prefs.getBool('isFirstLaunch') ?? true; if (prefs.getString("kTage1")?.isEmpty ?? true) await prefs.setString('kTage1', "28"); if (prefs.getString("kTage2")?.isEmpty ?? true) await prefs.setString('kTage2', "48"); - if (prefs.getString("fotofallenApiAddress")?.isEmpty ?? true) await prefs.setString('fotofallenApiAddress', 'http://192.168.1.170/www.dbb-wolf.de/data/app24.php'); - if (prefs.getString("exkursionenApiAddress")?.isEmpty ?? true) await prefs.setString('exkursionenApiAddress', 'http://192.168.1.170/www.dbb-wolf.de/data/api_exkursion.php'); + // if (prefs.getString("fotofallenApiAddress")?.isEmpty ?? true) await prefs.setString('fotofallenApiAddress', 'http://192.168.1.170/www.dbb-wolf.de/data/app24.php'); + // if (prefs.getString("exkursionenApiAddress")?.isEmpty ?? true) await prefs.setString('exkursionenApiAddress', 'http://192.168.1.170/www.dbb-wolf.de/data/api_exkursion.php'); runApp(MyApp(isFirstLaunch: isFirstLaunch)); } class MyApp extends StatelessWidget { + final bool isFirstLaunch; const MyApp({super.key, required this.isFirstLaunch}); @@ -37,7 +40,9 @@ class MyApp extends StatelessWidget { darkTheme: FlexThemeData.dark(scheme: FlexScheme.greenM3, useMaterial3: true), themeMode: ThemeMode.system, + // here the isFirstLaunch comes into play initialRoute: isFirstLaunch ? '/introScreen' : '/home', + // Localization settings supportedLocales: L10n.all, localizationsDelegates: const [ AppLocalizations.delegate, diff --git a/lib/screens/addCam/add_cam_main.dart b/lib/screens/addCam/add_cam_main.dart index 7df4020..2cea16c 100644 --- a/lib/screens/addCam/add_cam_main.dart +++ b/lib/screens/addCam/add_cam_main.dart @@ -13,6 +13,7 @@ import 'package:fforte/l10n/app_localizations.dart'; import 'package:geolocator/geolocator.dart'; import 'package:latlong2/latlong.dart'; import 'package:animations/animations.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import 'widgets/abbau_dat.dart'; import 'widgets/karte.dart'; @@ -164,11 +165,15 @@ class _AddCamMainState extends State { } } else { // If it is not a template set default values - rmap["Datum"]!["controller"]!.text = DateTime.now().toString(); + rmap["Datum"]!["controller"]!.text = DateTime.now().toString().split(" ").first; rmap["Status"]!["controller"]!.text = "aktiv"; rmap["FotoFilm"]!["controller"]!.text = "Foto"; rmap["MEZ"]!["controller"]!.text = "Sommerzeit"; rmap["Platzung"]!["controller"]!.text = ""; + SharedPreferences.getInstance().then((SharedPreferences prefs) { + rmap["KTage1"]!["controller"]!.text = prefs.getString("kTage1"); + rmap["KTage2"]!["controller"]!.text = prefs.getString("kTage2"); + }); } // Set initial default position @@ -201,7 +206,6 @@ class _AddCamMainState extends State { ); } } - return currentPosition; }); } @@ -353,7 +357,7 @@ class _AddCamMainState extends State { } } - if (!mounted) return; + if (!context.mounted) return; final result = await Navigator.of(context).push( MaterialPageRoute( @@ -480,7 +484,7 @@ class _AddCamMainState extends State { initDatum: DateTime.parse(rmap["Datum"]!["controller"]!.text), onDateChanged: (value) { setState(() { - rmap["Datum"]!["controller"]!.text = value.toString(); + rmap["Datum"]!["controller"]!.text = value.toString().split(" ").first; }); }, name: AppLocalizations.of(context)!.pickDate, @@ -705,8 +709,8 @@ class _AddCamMainState extends State { } bool empty = CheckRequired.checkRequired(rmap); - // TODO for debugging always false - empty = false; + // for debugging always false + // empty = false; if (empty == true) { AddEntriesDialogHelper.showTemplateDialog( diff --git a/lib/screens/excursion/excursion_main.dart b/lib/screens/excursion/excursion_main.dart index 517f6dd..07c4969 100644 --- a/lib/screens/excursion/excursion_main.dart +++ b/lib/screens/excursion/excursion_main.dart @@ -167,7 +167,7 @@ class _ExcursionMainState extends State { rmap["BLand"]!["controller"]!.text = prefs.getString('bLand') ?? ""; }); - rmap["Datum"]!["controller"]!.text = DateTime.now().toString(); + rmap["Datum"]!["controller"]!.text = DateTime.now().toString().split(" ").first; rmap["Sent"]!["controller"]!.text = "0"; } @@ -205,7 +205,7 @@ class _ExcursionMainState extends State { Datum( initDatum: DateTime.now(), onDateChanged: (date) { - rmap["Datum"]!["controller"]!.text = date.toString(); + rmap["Datum"]!["controller"]!.text = date.toString().split(" ").first; }, name: AppLocalizations.of(context)!.date, ), @@ -357,7 +357,7 @@ class _ExcursionMainState extends State { // Check for always permission before starting tracking LocationPermission permission = await Geolocator.checkPermission(); if (permission != LocationPermission.always) { - if (mounted) { + if (context.mounted) { bool? shouldContinue = await showDialog( context: context, builder: (context) => AlertDialog( @@ -371,7 +371,7 @@ class _ExcursionMainState extends State { TextButton( onPressed: () async { await Geolocator.openAppSettings(); - Navigator.of(context).pop(true); + if (context.mounted) Navigator.of(context).pop(true); }, child: Text(AppLocalizations.of(context)!.openSettings), ), @@ -387,7 +387,7 @@ class _ExcursionMainState extends State { // Try checking the permission multiple times for (int i = 0; i < 5; i++) { await Future.delayed(const Duration(seconds: 1)); - if (!mounted) return; + if (!context.mounted) return; permission = await Geolocator.checkPermission(); if (permission == LocationPermission.always) { @@ -396,7 +396,7 @@ class _ExcursionMainState extends State { // If this is the last attempt and we still don't have permission if (i == 4 && permission != LocationPermission.always) { - if (mounted) { + if (context.mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(AppLocalizations.of(context)!.permissionNotGranted), @@ -410,7 +410,8 @@ class _ExcursionMainState extends State { } } - await Navigator.push(context, MaterialPageRoute( + if (context.mounted) { + await Navigator.push(context, MaterialPageRoute( builder: (context) { return Tracking( weg: rmap["Weg"]!["controller"]!, @@ -418,6 +419,7 @@ class _ExcursionMainState extends State { ); }, )); + } setState(() {}); }, child: Text(AppLocalizations.of(context)!.trackingAnAusschalten), diff --git a/lib/screens/excursion/widgets/hinweise.dart b/lib/screens/excursion/widgets/hinweise.dart index 4a4e872..0cef332 100644 --- a/lib/screens/excursion/widgets/hinweise.dart +++ b/lib/screens/excursion/widgets/hinweise.dart @@ -26,15 +26,15 @@ class _HinweiseState extends State { void initState() { sonstigesController.addListener(updateController); - liegestelleChecked = widget.hinweise.text.contains("liegestelle") ? true : false; - kadaverChecked = widget.hinweise.text.contains("kadaver") ? true : false; - sichtungChecked = widget.hinweise.text.contains("sichtung") ? true : false; - heulenChecked = widget.hinweise.text.contains("heulen") ? true : false; + liegestelleChecked = widget.hinweise.text.contains("Liegestelle") ? true : false; + kadaverChecked = widget.hinweise.text.contains("Wildtierkadaver") ? true : false; + sichtungChecked = widget.hinweise.text.contains("Sichtung") ? true : false; + heulenChecked = widget.hinweise.text.contains("Heulen") ? true : false; bool firstRun = true; for (String val in widget.hinweise.text.split(",")) { - if (val != "liegestelle" && val != "kadaver" && val != "sichtung" && val != "heulen" && val != "") { + if (val != "Liegestelle" && val != "Wildtierkadaver" && val != "Sichtung" && val != "Heulen" && val != "") { sonstigesChecked = true; if (!firstRun) sonstigesController.text += ","; sonstigesController.text += val; @@ -53,10 +53,10 @@ class _HinweiseState extends State { void updateController() { Map props = { - "liegestelle": liegestelleChecked, - "kadaver": kadaverChecked, - "sichtung": sichtungChecked, - "heulen": heulenChecked, + "Liegestelle": liegestelleChecked, + "Wildtierkadaver": kadaverChecked, + "Sichtung": sichtungChecked, + "Heulen": heulenChecked, "sonstiges": sonstigesChecked }; bool firstRun = true; diff --git a/lib/screens/excursion/widgets/tracking.dart b/lib/screens/excursion/widgets/tracking.dart index b2ccdb3..eaf68dd 100644 --- a/lib/screens/excursion/widgets/tracking.dart +++ b/lib/screens/excursion/widgets/tracking.dart @@ -215,7 +215,7 @@ class _TrackingState extends State { currentPosition!.longitude, ), radius: currentPosition!.accuracy, - color: Colors.blue.withOpacity(0.2), + color: Colors.blue.withAlpha(2), borderColor: Colors.blue, borderStrokeWidth: 2, ), diff --git a/lib/screens/helper/add_entries_dialog_helper.dart b/lib/screens/helper/add_entries_dialog_helper.dart index 7be992e..2280a95 100644 --- a/lib/screens/helper/add_entries_dialog_helper.dart +++ b/lib/screens/helper/add_entries_dialog_helper.dart @@ -72,12 +72,12 @@ class AddEntriesDialogHelper { TextButton( onPressed: () async { setState(() => isLoading = true); - int errorCode = await HttpRequest.httpRequest( + int errorCode = await HttpRequestService.httpRequest( saveDataMap: saveData, ); setState(() => isLoading = false); - if (errorCode == 201 && context.mounted) { + if (errorCode == 200 && context.mounted) { Navigator.pop(context); // saveData(true); SaveMainEntryMethod.saveEntry( @@ -160,12 +160,12 @@ class AddEntriesDialogHelper { TextButton( onPressed: () async { setState(() => isLoading = true); - int errorCode = await HttpRequest.httpRequest( + int errorCode = await HttpRequestService.httpRequest( saveDataMap: saveData, ); setState(() => isLoading = false); - if (errorCode != 201 || !context.mounted) { + if (errorCode != 200 || !context.mounted) { SaveMainEntryMethod.saveEntry( entryData: saveData, isTemplate: isTemplate, diff --git a/lib/screens/intro_screen.dart b/lib/screens/intro_screen.dart index 9202a09..3c94bdd 100644 --- a/lib/screens/intro_screen.dart +++ b/lib/screens/intro_screen.dart @@ -15,8 +15,8 @@ class _IntroScreenState extends State { TextEditingController ffApiAddress = TextEditingController(); TextEditingController exApiAddress = TextEditingController(); - String selectedFFApiAddress = "Test"; - String selectedEXApiAddress = "Test"; + String selectedFFApiAddress = "https://data.dbb-wolf.de/app24.php"; + String selectedEXApiAddress = "https://data.dbb-wolf.de/api_exkursion.php"; String? selectedBLand = "Sachsen"; Future _saveData() async { @@ -38,8 +38,8 @@ class _IntroScreenState extends State { Future.delayed(Duration.zero, () async { SharedPreferences prefs = await SharedPreferences.getInstance(); setState(() { - ffApiAddress.text = prefs.getString('fotofallenApiAddress') ?? ""; - exApiAddress.text = prefs.getString('exkursionenApiAddress') ?? ""; + ffApiAddress.text = prefs.getString('fotofallenApiAddress') ?? "https://data.dbb-wolf.de/app24.php"; + exApiAddress.text = prefs.getString('exkursionenApiAddress') ?? "https://data.dbb-wolf.de/api_exkursion.php"; addresse1C.text = prefs.getString('addresse1') ?? ""; bLandC.text = prefs.getString('bLand') ?? "Sachsen"; }); @@ -75,7 +75,7 @@ class _IntroScreenState extends State { Expanded( flex: 4, child: TextField( - readOnly: true, + readOnly: true, controller: bLandC, ), ), @@ -161,86 +161,97 @@ class _IntroScreenState extends State { ], ), const SizedBox( - height: 25, + height: 35, ), Align( - alignment: Alignment.bottomLeft, - child: Text(AppLocalizations.of(context)!.ffApiAddress)), + alignment: Alignment.bottomLeft, + child: Text(AppLocalizations.of(context)!.ffApiAddress)), Row( children: [ + // Expanded( + // flex: 4, + // child: TextField( + // decoration: InputDecoration( + // hintText: + // AppLocalizations.of(context)!.ffApiAddress), + // controller: ffApiAddress, + // ), + // ), Expanded( - flex: 4, + flex: 1, child: TextField( - decoration: InputDecoration( - hintText: - AppLocalizations.of(context)!.ffApiAddress), controller: ffApiAddress, ), - ), - Expanded( - flex: 1, - child: PopupMenuButton( - icon: const Icon(Icons.arrow_drop_down), - initialValue: selectedFFApiAddress, - onSelected: (value) { - setState(() { - selectedFFApiAddress = value; - ffApiAddress.text = value; - }); - }, - itemBuilder: (context) => [ - PopupMenuItem( - value: - "http://192.168.1.106/www.dbb-wolf.de/data/app24.php", - child: - Text(AppLocalizations.of(context)!.test)), - PopupMenuItem( - value: "...", - child: Text( - AppLocalizations.of(context)!.notest)) - ], - )) + // child: PopupMenuButton( + // icon: const Icon(Icons.arrow_drop_down), + // initialValue: selectedFFApiAddress, + // onSelected: (value) { + // setState(() { + // selectedFFApiAddress = value; + // ffApiAddress.text = value; + // }); + // }, + // itemBuilder: (context) => [ + // // PopupMenuItem( + // // value: + // // "http://192.168.1.106/www.dbb-wolf.de/data/app24.php", + // // child: + // // Text(AppLocalizations.of(context)!.test)), + // PopupMenuItem( + // value: "https://data.dbb-wolf.de/app24.php", + // child: Text( + // AppLocalizations.of(context)!.notest)) + // ], + // ), + //) + ) ], ), - const SizedBox(height: 10,), - + const SizedBox( + height: 10, + ), Align( - alignment: Alignment.bottomLeft, - child: Text(AppLocalizations.of(context)!.exApiAddress)), + alignment: Alignment.bottomLeft, + child: Text(AppLocalizations.of(context)!.exApiAddress)), Row( children: [ - Expanded( - flex: 4, - child: TextField( - decoration: InputDecoration( - hintText: - AppLocalizations.of(context)!.exApiAddress), - controller: exApiAddress, - ), - ), + // Expanded( + // flex: 4, + // child: TextField( + // decoration: InputDecoration( + // hintText: + // AppLocalizations.of(context)!.exApiAddress), + // controller: exApiAddress, + // ), + // ), Expanded( flex: 1, - child: PopupMenuButton( - icon: const Icon(Icons.arrow_drop_down), - initialValue: selectedEXApiAddress, - onSelected: (value) { - setState(() { - selectedEXApiAddress = value; - exApiAddress.text = value; - }); - }, - itemBuilder: (context) => [ - PopupMenuItem( - value: - "http://192.168.1.106/www.dbb-wolf.de/data/app24.php", - child: - Text(AppLocalizations.of(context)!.test)), - PopupMenuItem( - value: "...", - child: Text( - AppLocalizations.of(context)!.notest)) - ], - )) + child: TextField( + controller: exApiAddress, + ), + // child: PopupMenuButton( + // icon: const Icon(Icons.arrow_drop_down), + // initialValue: selectedEXApiAddress, + // onSelected: (value) { + // setState(() { + // selectedEXApiAddress = value; + // exApiAddress.text = value; + // }); + // }, + // itemBuilder: (context) => [ + // // PopupMenuItem( + // // value: + // // "http://192.168.1.106/www.dbb-wolf.de/data/app24.php", + // // child: + // // Text(AppLocalizations.of(context)!.test)), + // PopupMenuItem( + // value: + // "https://data.dbb-wolf.de/api_exkursion.php", + // child: Text( + // AppLocalizations.of(context)!.notest)) + // ], + // ) + ) ], ) ], diff --git a/lib/screens/sharedMethods/http_request.dart b/lib/screens/sharedMethods/http_request.dart index 1f95684..b70db03 100644 --- a/lib/screens/sharedMethods/http_request.dart +++ b/lib/screens/sharedMethods/http_request.dart @@ -3,7 +3,7 @@ import 'package:dio/dio.dart'; import 'package:shared_preferences/shared_preferences.dart'; -class HttpRequest { +class HttpRequestService { static Future httpRequest({Map? saveDataMap, String? saveDataString}) async { // print(jsonEncode(place)); @@ -25,7 +25,6 @@ class HttpRequest { response = await dio.post(prefs.getString('exkursionenApiAddress') ?? "", data: saveDataMap == null ? saveDataString : jsonEncode(saveDataMap)); } - return response.statusCode!; } on DioException { return response.statusCode ?? 400; diff --git a/lib/screens/sharedMethods/send_file.dart b/lib/screens/sharedMethods/send_file.dart index ded642e..e5a6b4e 100644 --- a/lib/screens/sharedMethods/send_file.dart +++ b/lib/screens/sharedMethods/send_file.dart @@ -1,69 +1,80 @@ -import 'dart:convert'; -import 'package:dio/dio.dart'; -import 'package:fforte/l10n/app_localizations.dart'; +import 'package:fforte/screens/sharedMethods/http_request.dart'; +import 'package:file_picker/file_picker.dart'; import 'dart:io'; -import 'package:flutter/material.dart'; -import 'package:shared_preferences/shared_preferences.dart'; -class SendFile extends StatefulWidget { - const SendFile({super.key}); +class SendFile { + static Future sendFile() async { + File? pickedFile; - @override - State createState() => _SendFileState(); -} + FilePickerResult? result = await FilePicker.platform.pickFiles(); -class _SendFileState extends State { - File? pickedFile; - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar(), - body: Column( - children: [ - ElevatedButton( - onPressed: () async { - // FilePickerResult? result = - // await FilePicker.platform.pickFiles(); - - // if (result != null) { - // pickedFile = File(result.files.single.path!); - // } else { - // pickedFile = File(""); - // } - }, - child: Text(AppLocalizations.of(context)!.pickfile)), - Text(pickedFile.toString()), - ElevatedButton( - onPressed: () async { - final dio = Dio(); - final SharedPreferences prefs = await SharedPreferences.getInstance(); - String? fileContent = await pickedFile?.readAsString(); - - dio.options.responseType = ResponseType.plain; - Response response = Response( - requestOptions: RequestOptions(path: ''), statusCode: 400); - - try { - response = await dio.post(prefs.getString('apiAddress') ?? "", - data: jsonEncode(fileContent)); - } on DioException catch (e) { - if (e.response?.statusCode == 500) { -/* print('-------------------------'); - print('code 500'); */ - return; - } - } - if (response.statusCode == 201) { - // print(response.statusCode); - } else { - //print(response.statusCode); - } - }, - child: Text(AppLocalizations.of(context)!.sendtoserver)) - ], - ), - ); + if (result != null) { + pickedFile = File(result.files.single.path!); + String fileContent = await pickedFile.readAsString(); + await HttpRequestService.httpRequest(saveDataString: fileContent); + } } } + +// class SendFile extends StatefulWidget { +// const SendFile({super.key}); +// +// @override +// State createState() => _SendFileState(); +// } +// +// class _SendFileState extends State { +// File? pickedFile; +// +// @override +// Widget build(BuildContext context) { +// return Scaffold( +// appBar: AppBar(), +// body: Column( +// children: [ +// ElevatedButton( +// onPressed: () async { +// FilePickerResult? result = +// await FilePicker.platform.pickFiles(); +// +// if (result != null) { +// pickedFile = File(result.files.single.path!); +// } else { +// pickedFile = File(""); +// } +// }, +// child: Text(AppLocalizations.of(context)!.pickfile)), +// Text(pickedFile.toString()), +// ElevatedButton( +// onPressed: () async { +// final dio = Dio(); +// final SharedPreferences prefs = await SharedPreferences.getInstance(); +// String? fileContent = await pickedFile?.readAsString(); +// +// dio.options.responseType = ResponseType.plain; +// Response response = Response( +// requestOptions: RequestOptions(path: ''), statusCode: 400); +// +// try { +// response = await dio.post(prefs.getString('apiAddress') ?? "", +// data: jsonEncode(fileContent)); +// } on DioException catch (e) { +// if (e.response?.statusCode == 500) { +// /* print('-------------------------'); +// print('code 500'); */ +// return; +// } +// } +// if (response.statusCode == 201) { +// // print(response.statusCode); +// } else { +// //print(response.statusCode); +// } +// }, +// child: Text(AppLocalizations.of(context)!.sendtoserver)) +// ], +// ), +// ); +// } +// } diff --git a/lib/screens/viewEntries/view_cams.dart b/lib/screens/viewEntries/view_cams.dart index 7aae8da..8f8937b 100644 --- a/lib/screens/viewEntries/view_cams.dart +++ b/lib/screens/viewEntries/view_cams.dart @@ -185,7 +185,7 @@ class _ViewEntriesState extends State { mainEntries[index]["Datum"])), ), trailing: Checkbox( - value: mainEntries[index]['Sent'] == 0 + value: mainEntries[index]['Sent'] == "0" || mainEntries[index]["Sent"] == "" ? false : true, onChanged: null, diff --git a/lib/services/tracking_service.dart b/lib/services/tracking_service.dart index c375bba..900dc0c 100644 --- a/lib/services/tracking_service.dart +++ b/lib/services/tracking_service.dart @@ -8,16 +8,23 @@ import 'package:geolocator/geolocator.dart'; import 'package:latlong2/latlong.dart'; import 'package:shared_preferences/shared_preferences.dart'; +/// Service, with the Singleton design pattern, that runs the geolocator service that tracks the position of the device. +/// This is needed for excursions +/// +/// Start the tracking service via [startTracking] +/// Manage the position stream via [pauseTracking], [stopTracking] and [resumeTracking] class TrackingService { + // Singleton stuff static TrackingService? _instance; - + factory TrackingService() { _instance ??= TrackingService._internal(); return _instance!; } - + TrackingService._internal(); + /// Resets all values, making it possible to start tracking again. static void resetInstance() { if (_instance != null) { _instance!.dispose(); @@ -25,48 +32,55 @@ class TrackingService { } } + // Variables + // - Stores the tracked coordinates List pathList = []; + // - Stores all gotten accuracies List accuracyList = []; + // - Stores timer so that is responsible vor the periodically tracking Timer? _positionTimer; bool isTracking = false; + // - Some more Singleton stuff (i guess. Vibecoded it because of lack of time) BuildContext? _lastContext; final _positionController = StreamController.broadcast(); final _statsController = StreamController.broadcast(); + // - Getter Stream get positionStream$ => _positionController.stream; Stream get statsStream$ => _statsController.stream; - double? currentAccuracy; - + // - Stores the last measured accuracy so that it can be displayed in the excursions view double? currentAccuracy; + + // Name says it all double _calculateMedianAccuracy(List accuracies) { + // if one or less values for accuracy are available return that accuracy or 0 if (accuracies.isEmpty) return 0; if (accuracies.length == 1) return accuracies.first; - - // Kopiere die Liste, um die Originaldaten nicht zu verändern + + // Copy the list so that the original data doesnt get modified var sorted = List.from(accuracies)..sort(); - + + // Calculates median (not arithmetic mean!!). That is because often the firsed tracked accuracy is about 9000m if (sorted.length % 2 == 0) { - // Bei gerader Anzahl: Durchschnitt der beiden mittleren Werte int midIndex = sorted.length ~/ 2; return (sorted[midIndex - 1] + sorted[midIndex]) / 2; } else { - // Bei ungerader Anzahl: Der mittlere Wert return sorted[sorted.length ~/ 2]; } } - + + /// Starts tracking Future startTracking(BuildContext context) async { if (isTracking) return; - final LocationSettings locationSettings = LocationSettings( - accuracy: LocationAccuracy.high - ); + final LocationSettings locationSettings = + LocationSettings(accuracy: LocationAccuracy.high); _lastContext = context; await NotificationService().initNotification(); if (context.mounted) { NotificationService().showNotification( - title: AppLocalizations.of(context)!.trackingRunningInBackground, - ); + title: AppLocalizations.of(context)!.trackingRunningInBackground, + ); } // Get tracking interval from settings @@ -74,13 +88,12 @@ class TrackingService { final intervalSeconds = prefs.getInt('trackingInterval') ?? 60; // Create a timer that triggers position updates - _positionTimer = Timer.periodic(Duration(seconds: intervalSeconds), (_) async { + _positionTimer = + Timer.periodic(Duration(seconds: intervalSeconds), (_) async { try { - final Position position = await Geolocator.getCurrentPosition( - locationSettings: locationSettings - ); - + locationSettings: locationSettings); + pathList.add(LatLng(position.latitude, position.longitude)); accuracyList.add(position.accuracy); currentAccuracy = position.accuracy; @@ -95,9 +108,8 @@ class TrackingService { // Get initial position immediately try { final Position position = await Geolocator.getCurrentPosition( - locationSettings: locationSettings - ); - + locationSettings: locationSettings); + pathList.add(LatLng(position.latitude, position.longitude)); accuracyList.add(position.accuracy); currentAccuracy = position.accuracy; @@ -117,31 +129,28 @@ class TrackingService { void _updateStats() { if (pathList.isEmpty) { _lastStats = TrackingStats( - currentAccuracy: currentAccuracy ?? 0, - averageAccuracy: 0, - totalDistanceMeters: 0 - ); + currentAccuracy: currentAccuracy ?? 0, + averageAccuracy: 0, + totalDistanceMeters: 0); _statsController.add(_lastStats!); return; } - + double totalDistance = 0; for (int i = 1; i < pathList.length; i++) { totalDistance += _calculateDistance( - pathList[i-1].latitude, - pathList[i-1].longitude, - pathList[i].latitude, - pathList[i].longitude - ); + pathList[i - 1].latitude, + pathList[i - 1].longitude, + pathList[i].latitude, + pathList[i].longitude); } double medianAccuracy = _calculateMedianAccuracy(accuracyList); _lastStats = TrackingStats( - currentAccuracy: currentAccuracy ?? 0, - averageAccuracy: medianAccuracy, - totalDistanceMeters: totalDistance - ); + currentAccuracy: currentAccuracy ?? 0, + averageAccuracy: medianAccuracy, + totalDistanceMeters: totalDistance); _statsController.add(_lastStats!); } @@ -149,19 +158,22 @@ class TrackingService { _updateStats(); } - double _calculateDistance(double lat1, double lon1, double lat2, double lon2) { + double _calculateDistance( + double lat1, double lon1, double lat2, double lon2) { const double earthRadius = 6371000; // Erdradius in Metern - + double lat1Rad = lat1 * math.pi / 180; double lat2Rad = lat2 * math.pi / 180; double deltaLat = (lat2 - lat1) * math.pi / 180; double deltaLon = (lon2 - lon1) * math.pi / 180; - double a = math.sin(deltaLat/2) * math.sin(deltaLat/2) + - math.cos(lat1Rad) * math.cos(lat2Rad) * - math.sin(deltaLon/2) * math.sin(deltaLon/2); - - double c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a)); + double a = math.sin(deltaLat / 2) * math.sin(deltaLat / 2) + + math.cos(lat1Rad) * + math.cos(lat2Rad) * + math.sin(deltaLon / 2) * + math.sin(deltaLon / 2); + + double c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a)); return earthRadius * c; } @@ -214,4 +226,4 @@ class TrackingStats { required this.averageAccuracy, required this.totalDistanceMeters, }); -} +} diff --git a/time.txt b/time.txt index ebed645..9607e07 100644 --- a/time.txt +++ b/time.txt @@ -97,3 +97,4 @@ 4 jun 3h 45min 5 jun 2h 10min +6 jun 3h 30min