diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index b14c7ba..cde5d44 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -172,5 +172,14 @@ "trackingStart": "Tracking starten", "trackingStop": "Tracking stoppen", "trackingPause": "Tracking pausieren", - "accuracy": "Genauigkeit" + "accuracy": "Genauigkeit", + "leavePageTitle": "Seite verlassen?", + "leavePageContent": "Möchten Sie die Seite wirklich verlassen?", + "leaveWithoutSaving": "Seite verlassen ohne zu speichern", + "leaveAndSaveTemplate": "Als Template speichern und verlassen", + "trackingPermissionTitle": "Standort-Berechtigung für Tracking", + "trackingPermissionContent": "Für das Tracking wird die dauerhafte Standort-Berechtigung benötigt. Nach dem Öffnen der Einstellungen:\n\n1. Tippen Sie auf 'Berechtigungen' und dann 'Standort'\n2. Wählen Sie 'immer'\n3. Kehren Sie zur App zurück\n\nDies ermöglicht das Aufzeichnen Ihrer Position auch im Hintergrund.", + "openSettings": "Einstellungen öffnen", + "returnToApp": "Zurück zur App", + "permissionNotGranted": "Berechtigung nicht erteilt. Tracking nicht möglich." } diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 9e0afc8..7fac935 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -686,5 +686,15 @@ "accuracy": "Accuracy", "@accuracy": { "description": "Accuracy of GPS position" - } + }, + + "leavePageTitle": "Leave page?", + "leavePageContent": "Do you really want to leave the page?", + "leaveWithoutSaving": "Leave without saving", + "leaveAndSaveTemplate": "Save as template and leave", + "trackingPermissionTitle": "Location Permission for Tracking", + "trackingPermissionContent": "Tracking requires permanent location permission. After opening settings:\n\n1. Tap on 'Permissions' and then on 'Location'\n2. Select 'Allow all the time'\n3. Return to the app\n\nThis allows recording your position in the background.", + "openSettings": "Open Settings", + "returnToApp": "Return to App", + "permissionNotGranted": "Permission not granted. Tracking not possible." } diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 8ce4691..eeea19c 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -1132,6 +1132,60 @@ abstract class AppLocalizations { /// In en, this message translates to: /// **'Accuracy'** String get accuracy; + + /// No description provided for @leavePageTitle. + /// + /// In en, this message translates to: + /// **'Leave page?'** + String get leavePageTitle; + + /// No description provided for @leavePageContent. + /// + /// In en, this message translates to: + /// **'Do you really want to leave the page?'** + String get leavePageContent; + + /// No description provided for @leaveWithoutSaving. + /// + /// In en, this message translates to: + /// **'Leave without saving'** + String get leaveWithoutSaving; + + /// No description provided for @leaveAndSaveTemplate. + /// + /// In en, this message translates to: + /// **'Save as template and leave'** + String get leaveAndSaveTemplate; + + /// No description provided for @trackingPermissionTitle. + /// + /// In en, this message translates to: + /// **'Location Permission for Tracking'** + String get trackingPermissionTitle; + + /// No description provided for @trackingPermissionContent. + /// + /// In en, this message translates to: + /// **'Tracking requires permanent location permission. After opening settings:\n\n1. Tap on \'Permissions\' and then on \'Location\'\n2. Select \'Allow all the time\'\n3. Return to the app\n\nThis allows recording your position in the background.'** + String get trackingPermissionContent; + + /// No description provided for @openSettings. + /// + /// In en, this message translates to: + /// **'Open Settings'** + String get openSettings; + + /// No description provided for @returnToApp. + /// + /// In en, this message translates to: + /// **'Return to App'** + String get returnToApp; + + /// No description provided for @permissionNotGranted. + /// + /// In en, this message translates to: + /// **'Permission not granted. Tracking not possible.'** + String get permissionNotGranted; } class _AppLocalizationsDelegate extends LocalizationsDelegate { diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index af325aa..bd475b2 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -526,4 +526,31 @@ class AppLocalizationsDe extends AppLocalizations { @override String get accuracy => 'Genauigkeit'; + + @override + String get leavePageTitle => 'Seite verlassen?'; + + @override + String get leavePageContent => 'Möchten Sie die Seite wirklich verlassen?'; + + @override + String get leaveWithoutSaving => 'Seite verlassen ohne zu speichern'; + + @override + String get leaveAndSaveTemplate => 'Als Template speichern und verlassen'; + + @override + String get trackingPermissionTitle => 'Standort-Berechtigung für Tracking'; + + @override + String get trackingPermissionContent => 'Für das Tracking wird die dauerhafte Standort-Berechtigung benötigt. Nach dem Öffnen der Einstellungen:\n\n1. Tippen Sie auf \'Berechtigungen\' und dann \'Standort\'\n2. Wählen Sie \'immer\'\n3. Kehren Sie zur App zurück\n\nDies ermöglicht das Aufzeichnen Ihrer Position auch im Hintergrund.'; + + @override + String get openSettings => 'Einstellungen öffnen'; + + @override + String get returnToApp => 'Zurück zur App'; + + @override + String get permissionNotGranted => 'Berechtigung nicht erteilt. Tracking nicht möglich.'; } diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index 2cee217..d0781e6 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -526,4 +526,31 @@ class AppLocalizationsEn extends AppLocalizations { @override String get accuracy => 'Accuracy'; + + @override + String get leavePageTitle => 'Leave page?'; + + @override + String get leavePageContent => 'Do you really want to leave the page?'; + + @override + String get leaveWithoutSaving => 'Leave without saving'; + + @override + String get leaveAndSaveTemplate => 'Save as template and leave'; + + @override + String get trackingPermissionTitle => 'Location Permission for Tracking'; + + @override + String get trackingPermissionContent => 'Tracking requires permanent location permission. After opening settings:\n\n1. Tap on \'Permissions\' and then on \'Location\'\n2. Select \'Allow all the time\'\n3. Return to the app\n\nThis allows recording your position in the background.'; + + @override + String get openSettings => 'Open Settings'; + + @override + String get returnToApp => 'Return to App'; + + @override + String get permissionNotGranted => 'Permission not granted. Tracking not possible.'; } diff --git a/lib/screens/excursion/excursion_main.dart b/lib/screens/excursion/excursion_main.dart index 34db4e5..0da0bc1 100644 --- a/lib/screens/excursion/excursion_main.dart +++ b/lib/screens/excursion/excursion_main.dart @@ -124,7 +124,7 @@ class _ExcursionMainState extends State { @override void initState() { GeolocatorService.deteterminePosition( - alwaysOnNeeded: true, + alwaysOnNeeded: false, ).then((result) => currentPosition = result).catchError((error) async { if (error is LocationDisabledException) { if (mounted) { @@ -142,9 +142,16 @@ class _ExcursionMainState extends State { } } else if (error is NeedAlwaysLocation) { if (mounted) { - bool reload = await AddEntriesDialogHelper.locationSettingsDialog(context); - if (reload) GeolocatorService.deteterminePosition().then((res) => currentPosition = res).catchError((error) {return currentPosition;}); -} + bool reload = + await AddEntriesDialogHelper.locationSettingsDialog(context); + if (reload) { + GeolocatorService.deteterminePosition() + .then((res) => currentPosition = res) + .catchError((error) { + return currentPosition; + }); + } + } } return currentPosition; }); @@ -346,6 +353,62 @@ class _ExcursionMainState extends State { // ---------- Tracking ElevatedButton( onPressed: () async { + // Check for always permission before starting tracking + LocationPermission permission = await Geolocator.checkPermission(); + if (permission != LocationPermission.always) { + if (mounted) { + bool? shouldContinue = await showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text(AppLocalizations.of(context)!.trackingPermissionTitle), + content: Text(AppLocalizations.of(context)!.trackingPermissionContent), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(false), + child: Text(AppLocalizations.of(context)!.cancel), + ), + TextButton( + onPressed: () async { + await Geolocator.openAppSettings(); + Navigator.of(context).pop(true); + }, + child: Text(AppLocalizations.of(context)!.openSettings), + ), + ], + ), + ); + + if (shouldContinue != true) { + return; + } + + // Wait for user to change settings and return + // Try checking the permission multiple times + for (int i = 0; i < 5; i++) { + await Future.delayed(const Duration(seconds: 1)); + if (!mounted) return; + + permission = await Geolocator.checkPermission(); + if (permission == LocationPermission.always) { + break; + } + + // If this is the last attempt and we still don't have permission + if (i == 4 && permission != LocationPermission.always) { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(AppLocalizations.of(context)!.permissionNotGranted), + duration: const Duration(seconds: 3), + ), + ); + } + return; + } + } + } + } + await Navigator.push(context, MaterialPageRoute( builder: (context) { return Tracking( @@ -356,8 +419,7 @@ class _ExcursionMainState extends State { )); setState(() {}); }, - child: - Text(AppLocalizations.of(context)!.trackingAnAusschalten), + child: Text(AppLocalizations.of(context)!.trackingAnAusschalten), ), const SizedBox(height: 10), @@ -461,29 +523,32 @@ class _ExcursionMainState extends State { return PopScope( canPop: false, - onPopInvoked: (didPop) async { + onPopInvokedWithResult: (bool didPop, Object? res) async { if (didPop) { return; } - + // Show confirmation dialog final result = await showDialog( context: context, builder: (context) => AlertDialog( - title: const Text('Seite verlassen?'), - content: const Text('Möchten Sie die Seite wirklich verlassen?'), + title: Text(AppLocalizations.of(context)!.leavePageTitle), + content: Text(AppLocalizations.of(context)!.leavePageContent), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(0), - child: const Text('Nein'), + child: Text(AppLocalizations.of(context)!.nein), ), TextButton( - onPressed: () => Navigator.of(context).pop(1), - child: const Text('Ja und Template speichern'), + onPressed: () { + saveTemplate(getFieldsText(), DatabasesEnum.excursion); + Navigator.of(context).pop(1); + }, + child: Text(AppLocalizations.of(context)!.leaveAndSaveTemplate), ), TextButton( onPressed: () => Navigator.of(context).pop(2), - child: const Text('Ja ohne Template'), + child: Text(AppLocalizations.of(context)!.leaveWithoutSaving), ), ], ), @@ -517,9 +582,11 @@ class _ExcursionMainState extends State { actions: [ // Text(TrackingService().isTracking ? "Tracking" : "Not tracking") Image.asset( - TrackingService().isTracking ? "assets/icons/tracking_on.png" : "assets/icons/tracking_off.png", + TrackingService().isTracking + ? "assets/icons/tracking_on.png" + : "assets/icons/tracking_off.png", width: 40, - ), + ), ], ), body: PageTransitionSwitcher( diff --git a/lib/screens/helper/add_entries_dialog_helper.dart b/lib/screens/helper/add_entries_dialog_helper.dart index 200c107..ce5fb13 100644 --- a/lib/screens/helper/add_entries_dialog_helper.dart +++ b/lib/screens/helper/add_entries_dialog_helper.dart @@ -258,7 +258,7 @@ class AddEntriesDialogHelper { ); } } catch (e) { - print(e.toString()); + debugPrint(e.toString()); } }, child: Text(AppLocalizations.of(context)!.justsave), diff --git a/lib/screens/viewEntries/view_cams.dart b/lib/screens/viewEntries/view_cams.dart index 724b153..7aae8da 100644 --- a/lib/screens/viewEntries/view_cams.dart +++ b/lib/screens/viewEntries/view_cams.dart @@ -181,7 +181,8 @@ class _ViewEntriesState extends State { title: Text( '${widget.dbType == DatabasesEnum.place ? AppLocalizations.of(context)!.justplace : AppLocalizations.of(context)!.excursion} ${mainEntries[index]["ID"]}'), subtitle: Text( - dateFormat.format(DateTime.parse(mainEntries[index]["Datum"])), + dateFormat.format(DateTime.parse( + mainEntries[index]["Datum"])), ), trailing: Checkbox( value: mainEntries[index]['Sent'] == 0 @@ -191,7 +192,7 @@ class _ViewEntriesState extends State { ), onTap: () async { if (widget.dbType == DatabasesEnum.place) { - Navigator.push( + await Navigator.push( context, MaterialPageRoute( builder: (context) => AddCamMain( @@ -204,7 +205,7 @@ class _ViewEntriesState extends State { ); } else if (widget.dbType == DatabasesEnum.excursion) { - Navigator.push( + await Navigator.push( context, MaterialPageRoute( builder: (context) => ExcursionMain( @@ -215,6 +216,9 @@ class _ViewEntriesState extends State { ), ), ); + setState(() { + reloadAllEntries(); + }); } }, ), @@ -269,9 +273,9 @@ class _ViewEntriesState extends State { ], ), child: ListTile( - onTap: () { + onTap: () async { if (widget.dbType == DatabasesEnum.place) { - Navigator.push( + await Navigator.push( context, MaterialPageRoute( builder: (context) => AddCamMain( @@ -282,7 +286,7 @@ class _ViewEntriesState extends State { ); } else if (widget.dbType == DatabasesEnum.excursion) { - Navigator.push( + await Navigator.push( context, MaterialPageRoute( builder: (context) => ExcursionMain( @@ -291,12 +295,16 @@ class _ViewEntriesState extends State { ), ), ); + setState(() { + reloadAllEntries(); + }); } }, title: Text( '${widget.dbType == DatabasesEnum.place ? AppLocalizations.of(context)!.justplace : AppLocalizations.of(context)!.excursion} ${templates[index]["ID"]}'), subtitle: Text( - dateFormat.format(DateTime.parse(templates[index]["Datum"])), + dateFormat.format( + DateTime.parse(templates[index]["Datum"])), ), ), ); diff --git a/lib/services/tracking_service.dart b/lib/services/tracking_service.dart index de2c8f7..c375bba 100644 --- a/lib/services/tracking_service.dart +++ b/lib/services/tracking_service.dart @@ -57,12 +57,17 @@ class TrackingService { Future startTracking(BuildContext context) async { if (isTracking) return; + final LocationSettings locationSettings = LocationSettings( + accuracy: LocationAccuracy.high + ); _lastContext = context; await NotificationService().initNotification(); - NotificationService().showNotification( + if (context.mounted) { + NotificationService().showNotification( title: AppLocalizations.of(context)!.trackingRunningInBackground, ); + } // Get tracking interval from settings final prefs = await SharedPreferences.getInstance(); @@ -71,8 +76,9 @@ class TrackingService { // Create a timer that triggers position updates _positionTimer = Timer.periodic(Duration(seconds: intervalSeconds), (_) async { try { + final Position position = await Geolocator.getCurrentPosition( - desiredAccuracy: LocationAccuracy.high, + locationSettings: locationSettings ); pathList.add(LatLng(position.latitude, position.longitude)); @@ -89,7 +95,7 @@ class TrackingService { // Get initial position immediately try { final Position position = await Geolocator.getCurrentPosition( - desiredAccuracy: LocationAccuracy.high, + locationSettings: locationSettings ); pathList.add(LatLng(position.latitude, position.longitude)); @@ -208,4 +214,4 @@ class TrackingStats { required this.averageAccuracy, required this.totalDistanceMeters, }); -} \ No newline at end of file +}