diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index fc8627b..9d452b8 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -22,7 +22,7 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId = "com.example.fforte" + applicationId = "de.lupus.apps" // You can update the following values to match your application needs. // For more information, see: https://flutter.dev/to/review-gradle-config. minSdk = flutter.minSdkVersion diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 1fa7564..e705f7a 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,6 +1,7 @@ + { diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index 9b8840a..f49bcbc 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -493,4 +493,10 @@ class AppLocalizationsDe extends AppLocalizations { @override String get trackingRunningInBackground => 'Die Tracking funktion läuft im Hintergrund'; + + @override + String get needsAlwaysLocation => 'Diese app braucht die Standort berechtigung auf immer gesetzt'; + + @override + String get deleteWholeRouteBody => 'Sind Sie sicher, dass die gesamte bisher gegangene Route gelöscht werden soll?'; } diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index 1aeb305..5c89736 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -493,4 +493,10 @@ class AppLocalizationsEn extends AppLocalizations { @override String get trackingRunningInBackground => 'The tracking service is running in the background'; + + @override + String get needsAlwaysLocation => 'This app needs the location permission set to always'; + + @override + String get deleteWholeRouteBody => 'Do you really want to delete the existing route?'; } diff --git a/lib/screens/addCam/services/geolocator_service.dart b/lib/screens/addCam/services/geolocator_service.dart index 8b9bbb1..fa2d555 100644 --- a/lib/screens/addCam/services/geolocator_service.dart +++ b/lib/screens/addCam/services/geolocator_service.dart @@ -1,10 +1,11 @@ import 'package:fforte/screens/addCam/exceptions/location_disabled_exception.dart'; import 'package:fforte/screens/addCam/exceptions/location_forbidden_exception.dart'; +import 'package:fforte/screens/excursion/exceptions/need_always_location_exception.dart'; import 'package:geolocator/geolocator.dart'; class GeolocatorService { // determine live position with checks for denied permission and turned off location service - static Future deteterminePosition() async { + static Future deteterminePosition({bool alwaysOnNeeded = false}) async { bool locationEnabled; LocationPermission permissionGiven; @@ -20,6 +21,10 @@ class GeolocatorService { throw LocationForbiddenException(); } } + + if (alwaysOnNeeded && permissionGiven != LocationPermission.always) { + throw NeedAlwaysLocation(); + } return await Geolocator.getCurrentPosition(); } diff --git a/lib/screens/addCam/widgets/karte.dart b/lib/screens/addCam/widgets/karte.dart index f69535f..0cb4003 100644 --- a/lib/screens/addCam/widgets/karte.dart +++ b/lib/screens/addCam/widgets/karte.dart @@ -106,7 +106,7 @@ class KarteState extends State { children: [ TileLayer( urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', - userAgentPackageName: 'com.example.app', + userAgentPackageName: 'de.lupus.apps', ), MarkerLayer(markers: [currentMarker!]), ]), diff --git a/lib/screens/excursion/exceptions/need_always_location_exception.dart b/lib/screens/excursion/exceptions/need_always_location_exception.dart new file mode 100644 index 0000000..a9eae53 --- /dev/null +++ b/lib/screens/excursion/exceptions/need_always_location_exception.dart @@ -0,0 +1 @@ +class NeedAlwaysLocation implements Exception {} diff --git a/lib/screens/excursion/excursion_main.dart b/lib/screens/excursion/excursion_main.dart index 610b13f..0410f0d 100644 --- a/lib/screens/excursion/excursion_main.dart +++ b/lib/screens/excursion/excursion_main.dart @@ -3,6 +3,7 @@ import 'package:fforte/enums/databases.dart'; import 'package:fforte/screens/addCam/exceptions/location_disabled_exception.dart'; import 'package:fforte/screens/addCam/exceptions/location_forbidden_exception.dart'; import 'package:fforte/screens/addCam/services/geolocator_service.dart'; +import 'package:fforte/screens/excursion/exceptions/need_always_location_exception.dart'; import 'package:fforte/screens/excursion/widgets/anzahlen.dart'; import 'package:fforte/screens/excursion/widgets/bima_nutzer.dart'; import 'package:fforte/screens/excursion/widgets/hinweise.dart'; @@ -13,6 +14,7 @@ import 'package:fforte/screens/excursion/widgets/strecke_u_spurbedingungen.dart' import 'package:fforte/screens/excursion/widgets/tracking.dart'; import 'package:fforte/screens/helper/add_entries_dialog_helper.dart'; import 'package:fforte/screens/helper/snack_bar_helper.dart'; +import 'package:fforte/screens/helper/view_entries_dialog_helper.dart'; import 'package:fforte/screens/sharedMethods/check_required.dart'; import 'package:fforte/screens/sharedMethods/save_template.dart'; import 'package:fforte/screens/sharedWidgets/datum.dart'; @@ -20,7 +22,6 @@ import 'package:fforte/screens/sharedWidgets/var_text_field.dart'; import 'package:fforte/l10n/app_localizations.dart'; import 'package:flutter/material.dart'; import 'package:geolocator/geolocator.dart'; -import 'package:latlong2/latlong.dart'; class ExcursionMain extends StatefulWidget { final bool isTemplate; @@ -120,7 +121,7 @@ class _ExcursionMainState extends State { @override void initState() { - GeolocatorService.deteterminePosition() + GeolocatorService.deteterminePosition(alwaysOnNeeded: true) .then((result) => currentPosition = result) .catchError((error) { if (error is LocationDisabledException) { @@ -137,6 +138,10 @@ class _ExcursionMainState extends State { AppLocalizations.of(context)!.locationForbidden, ); } + } else if (error is NeedAlwaysLocation) { + if (mounted) { + AddEntriesDialogHelper.locationSettingsDialog(context); + } } return currentPosition; }); @@ -302,10 +307,7 @@ class _ExcursionMainState extends State { builder: (context) { return Tracking( weg: rmap["Weg"]!["controller"]!, - startPosition: LatLng( - currentPosition.latitude, - currentPosition.longitude, - ), + startPosition: currentPosition, ); }, ), diff --git a/lib/screens/excursion/widgets/tracking.dart b/lib/screens/excursion/widgets/tracking.dart index a606864..f35d068 100644 --- a/lib/screens/excursion/widgets/tracking.dart +++ b/lib/screens/excursion/widgets/tracking.dart @@ -2,15 +2,17 @@ import 'dart:async'; import 'dart:math'; import 'package:fforte/l10n/app_localizations.dart'; +import 'package:fforte/screens/helper/add_entries_dialog_helper.dart'; import 'package:fforte/screens/helper/snack_bar_helper.dart'; import 'package:fforte/services/notification_service.dart'; import 'package:flutter/material.dart'; import 'package:flutter_map/flutter_map.dart'; +import 'package:flutter_map_location_marker/flutter_map_location_marker.dart'; import 'package:geolocator/geolocator.dart'; import 'package:latlong2/latlong.dart'; class Tracking extends StatefulWidget { - final LatLng startPosition; + final Position startPosition; final TextEditingController weg; const Tracking({super.key, required this.startPosition, required this.weg}); @@ -21,6 +23,7 @@ class Tracking extends StatefulWidget { class _TrackingState extends State { List pathList = []; StreamSubscription? positionStream; + LocationMarkerPosition? locationMarkerPosition; bool positionStreamRunning = false; MapController mapController = MapController(); @@ -48,6 +51,13 @@ class _TrackingState extends State { ); } } + + locationMarkerPosition = LocationMarkerPosition( + latitude: widget.startPosition.latitude, + longitude: widget.startPosition.longitude, + accuracy: widget.startPosition.accuracy, + ); + super.initState(); } @@ -66,6 +76,8 @@ class _TrackingState extends State { widget.weg.text += "${pos.latitude},${pos.longitude}"; } } + NotificationService().deleteNotification(); + super.dispose(); } @@ -88,20 +100,26 @@ class _TrackingState extends State { locationSettings: AndroidSettings( accuracy: LocationAccuracy.high, distanceFilter: 0, - // foregroundNotificationConfig: - // mounted - // ? ForegroundNotificationConfig( - // notificationTitle: - // AppLocalizations.of(context)!.trackingRunningInBackground, - // notificationText: "", - // ) - // : null, + foregroundNotificationConfig: + mounted + ? ForegroundNotificationConfig( + notificationTitle: + AppLocalizations.of(context)!.trackingRunningInBackground, + notificationText: "", + ) + : null, ), ).listen((Position? position) { if (position != null) { setState(() { - pathList.add(LatLng(position.latitude, position.longitude)); - // pathList.add(LatLng(rand.nextInt(5) + 40, position.longitude)); + pathList.add(LatLng(position.latitude, position.longitude)); + // pathList.add(LatLng(rand.nextInt(5) + 40, position.longitude)); + + locationMarkerPosition = LocationMarkerPosition( + latitude: position.latitude, + longitude: position.longitude, + accuracy: position.accuracy, + ); }); } else { if (mounted) { @@ -112,6 +130,10 @@ class _TrackingState extends State { } } }); + positionStream!.onError((e) { + NotificationService().deleteNotification(); + NotificationService().showNotification(title: "ERROR: $e"); + }); } @override @@ -121,6 +143,25 @@ class _TrackingState extends State { title: Text(AppLocalizations.of(context)!.tracking), // leading: IconButton(onPressed: () {}, icon: Icon(Icons.arrow_back_rounded)), actions: [ + if (!positionStreamRunning) + IconButton( + onPressed: () async { + bool delete = + await AddEntriesDialogHelper.deleteCompleteRouteDialog( + context, + ); + + if (delete) { + setState(() { + pathList = []; + }); + } + }, + icon: Icon( + Icons.delete, + color: Theme.of(context).colorScheme.errorContainer, + ), + ), if (positionStreamRunning) IconButton( onPressed: () { @@ -130,7 +171,10 @@ class _TrackingState extends State { NotificationService().deleteNotification(); }); }, - icon: Icon(Icons.stop_rounded), + icon: Icon( + Icons.stop_rounded, + color: Theme.of(context).colorScheme.errorContainer, + ), ), IconButton( onPressed: () { @@ -152,7 +196,13 @@ class _TrackingState extends State { ), floatingActionButton: FloatingActionButton( onPressed: () { - mapController.move(pathList.last, 16); + mapController.move( + LatLng( + locationMarkerPosition!.latitude, + locationMarkerPosition!.longitude, + ), + 16, + ); }, child: Icon(Icons.my_location), ), @@ -165,13 +215,16 @@ class _TrackingState extends State { InteractiveFlag.drag | InteractiveFlag.pinchMove, ), - initialCenter: widget.startPosition, + initialCenter: LatLng( + widget.startPosition.latitude, + widget.startPosition.longitude, + ), initialZoom: 16.0, ), children: [ TileLayer( urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', - userAgentPackageName: 'com.example.app', + userAgentPackageName: 'de.lupus.apps', ), if (pathList.isNotEmpty) PolylineLayer( @@ -179,16 +232,17 @@ class _TrackingState extends State { Polyline(strokeWidth: 2.0, points: pathList, color: Colors.red), ], ), - CircleLayer( - circles: [ - CircleMarker( - color: Colors.blue, - point: pathList.isEmpty ? widget.startPosition : pathList.last, - radius: 5, - useRadiusInMeter: true, - ), - ], - ), + // CircleLayer( + // circles: [ + // CircleMarker( + // color: Colors.blue, + // point: pathList.isEmpty ? widget.startPosition : pathList.last, + // radius: 5, + // useRadiusInMeter: true, + // ), + // ], + // ), + CurrentLocationLayer(), ], ), ); diff --git a/lib/screens/helper/add_entries_dialog_helper.dart b/lib/screens/helper/add_entries_dialog_helper.dart index 106ea64..e8737b0 100644 --- a/lib/screens/helper/add_entries_dialog_helper.dart +++ b/lib/screens/helper/add_entries_dialog_helper.dart @@ -6,6 +6,8 @@ import 'package:fforte/screens/sharedMethods/save_template.dart'; import 'package:fforte/screens/sharedMethods/save_main_entry.dart'; import 'package:flutter/material.dart'; import 'package:fforte/l10n/app_localizations.dart'; +import 'package:geolocator/geolocator.dart'; +import 'package:latlong2/latlong.dart'; class AddEntriesDialogHelper { // Function to show the dialog where the user has to choose if he want to safe his values as a template @@ -258,4 +260,67 @@ class AddEntriesDialogHelper { }, ); } + + static void locationSettingsDialog(BuildContext context) async { + return showDialog( + context: context, + barrierDismissible: true, + builder: (BuildContext context) { + return AlertDialog( + content: Text(AppLocalizations.of(context)!.needsAlwaysLocation), + actions: [ + TextButton( + onPressed: () { + Geolocator.openAppSettings(); + Navigator.pop(context); + }, + child: Text("Ok"), + ), + TextButton( + onPressed: () {}, + child: Text(AppLocalizations.of(context)!.cancel), + ), + ], + ); + }, + ); + } + + static Future deleteCompleteRouteDialog(BuildContext context) async { + bool confirmed = false; + + await showDialog( + context: context, + barrierDismissible: true, + builder: (BuildContext context) { + return AlertDialog( + title: Text(AppLocalizations.of(context)!.deleteEverything), + content: SingleChildScrollView( + child: ListBody( + children: [ + Text(AppLocalizations.of(context)!.deleteWholeRouteBody), + ], + ), + ), + actions: [ + TextButton( + onPressed: () { + confirmed = true; + Navigator.of(context).pop(); + }, + child: Text("Ok"), + ), + TextButton( + onPressed: () => { + Navigator.of(context).pop() + }, + child: Text(AppLocalizations.of(context)!.cancel), + ), + ], + ); + }, + ); + + return confirmed; + } } diff --git a/lib/screens/helper/view_entries_dialog_helper.dart b/lib/screens/helper/view_entries_dialog_helper.dart index 6486938..4d06849 100644 --- a/lib/screens/helper/view_entries_dialog_helper.dart +++ b/lib/screens/helper/view_entries_dialog_helper.dart @@ -3,6 +3,7 @@ import 'package:fforte/l10n/app_localizations.dart'; import 'package:fforte/screens/sharedMethods/delete_main_entries.dart'; import 'package:fforte/screens/sharedMethods/delete_templates.dart'; import 'package:flutter/material.dart'; +import 'package:geolocator/geolocator.dart'; class ViewEntriesDialogHelper { static void deleteAllMainEntries( @@ -43,32 +44,41 @@ class ViewEntriesDialogHelper { ); } - static void deleteAllTemplates(BuildContext context, DatabasesEnum dbType) async{ + static void deleteAllTemplates( + BuildContext context, + DatabasesEnum dbType, + ) async { return showDialog( - context: context, - barrierDismissible: false, - builder: (BuildContext context) { - return AlertDialog( - title: Text(AppLocalizations.of(context)!.deleteEverything), - content: SingleChildScrollView( - child: ListBody(children: [ - Text(AppLocalizations.of(context)!.deleteEverythingContent) - ]), + context: context, + barrierDismissible: false, + builder: (BuildContext context) { + return AlertDialog( + title: Text(AppLocalizations.of(context)!.deleteEverything), + content: SingleChildScrollView( + child: ListBody( + children: [ + Text(AppLocalizations.of(context)!.deleteEverythingContent), + ], ), - actions: [ - TextButton( - onPressed: () { + ), + actions: [ + TextButton( + onPressed: () { DeleteTemplates.deleteAll(dbType); - Navigator.of(context).pop(); - }, - child: Text(AppLocalizations.of(context)!.deleteEverything)), - TextButton( - onPressed: () { - Navigator.of(context).pop(); - }, - child: Text(AppLocalizations.of(context)!.cancel)) - ], - ); - }); + Navigator.of(context).pop(); + }, + child: Text(AppLocalizations.of(context)!.deleteEverything), + ), + TextButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: Text(AppLocalizations.of(context)!.cancel), + ), + ], + ); + }, + ); } + } diff --git a/pubspec.lock b/pubspec.lock index 9b8047f..722be7a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -235,6 +235,14 @@ packages: url: "https://pub.dev" source: hosted version: "8.1.1" + flutter_map_location_marker: + dependency: "direct main" + description: + name: flutter_map_location_marker + sha256: "474695ec9052c17e307bdef98b66be2c183324f956efad24d86ad34a71942e4d" + url: "https://pub.dev" + source: hosted + version: "10.1.0" flutter_plugin_android_lifecycle: dependency: transitive description: @@ -251,6 +259,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.0.6" + flutter_rotation_sensor: + dependency: transitive + description: + name: flutter_rotation_sensor + sha256: "7944dbadf3d05be128a81cb0f37b2ba2b53247fef22b85f8096c60b06c1e50a4" + url: "https://pub.dev" + source: hosted + version: "0.1.1" flutter_shaders: dependency: transitive description: @@ -437,6 +453,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.5.0" + logging: + dependency: transitive + description: + name: logging + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 + url: "https://pub.dev" + source: hosted + version: "1.3.0" matcher: dependency: transitive description: @@ -469,6 +493,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.0" + native_device_orientation: + dependency: transitive + description: + name: native_device_orientation + sha256: "0c330c068575e4be72cce5968ca479a3f8d5d1e5dfce7d89d5c13a1e943b338c" + url: "https://pub.dev" + source: hosted + version: "2.0.3" path: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 4fd07c8..0e0e9ee 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -34,6 +34,7 @@ dependencies: geocoding: ^3.0.0 flutter_slidable: ^4.0.0 flutter_local_notifications: ^19.2.0 + flutter_map_location_marker: ^10.1.0 dev_dependencies: flutter_lints: ^5.0.0 diff --git a/time.txt b/time.txt index 2b6b821..1f91244 100644 --- a/time.txt +++ b/time.txt @@ -85,3 +85,4 @@ 15.mai 2h 30min 17.mai 1h 30min 18.mai 35min +19.mai 2h 15min