added tracking function. Not testet on a walk yet. Also the notifiation doesnt work

time

forgot to save tracking file
This commit is contained in:
Nico
2025-05-15 22:01:36 +02:00
parent 0e621ba1ef
commit 895bcf1d5c
10 changed files with 301 additions and 6 deletions

View File

@@ -1,4 +1,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<application
android:label="fforte"
android:name="${applicationName}"

View File

@@ -156,6 +156,9 @@
"heulen": "Heulen",
"sonstiges": "Sonstiges",
"hinweise": "Hinweise",
"zurueckgelegteStrecke": "Zurückgelegte Strecke"
"zurueckgelegteStrecke": "Zurückgelegte Strecke",
"tracking": "Tracking",
"couldntDeterminePosition": "Position konnte nicht ermittelt werden",
"trackingRunningInBackground": "Die Tracking funktion läuft im Hintergrund"
}

View File

@@ -661,6 +661,11 @@
"hinweise": "clues",
"zurueckgelegteStrecke": "Distance traveled"
"zurueckgelegteStrecke": "Distance traveled",
"tracking": "Tracking",
"couldntDeterminePosition": "couldn't determine position",
"trackingRunningInBackground": "The tracking service is running in the background"
}

View File

@@ -1048,6 +1048,24 @@ abstract class AppLocalizations {
/// In en, this message translates to:
/// **'Distance traveled'**
String get zurueckgelegteStrecke;
/// No description provided for @tracking.
///
/// In en, this message translates to:
/// **'Tracking'**
String get tracking;
/// No description provided for @couldntDeterminePosition.
///
/// In en, this message translates to:
/// **'couldn\'t determine position'**
String get couldntDeterminePosition;
/// No description provided for @trackingRunningInBackground.
///
/// In en, this message translates to:
/// **'The tracking service is running in the background'**
String get trackingRunningInBackground;
}
class _AppLocalizationsDelegate extends LocalizationsDelegate<AppLocalizations> {

View File

@@ -484,4 +484,13 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get zurueckgelegteStrecke => 'Zurückgelegte Strecke';
@override
String get tracking => 'Tracking';
@override
String get couldntDeterminePosition => 'Position konnte nicht ermittelt werden';
@override
String get trackingRunningInBackground => 'Die Tracking funktion läuft im Hintergrund';
}

View File

@@ -484,4 +484,13 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get zurueckgelegteStrecke => 'Distance traveled';
@override
String get tracking => 'Tracking';
@override
String get couldntDeterminePosition => 'couldn\'t determine position';
@override
String get trackingRunningInBackground => 'The tracking service is running in the background';
}

View File

@@ -37,10 +37,10 @@ class ExcursionDBHelper implements IDb {
@override
onCreateDatabases(Database excursionDB, int version) async {
await excursionDB.execute(
'CREATE TABLE excursion (ID INTEGER PRIMARY KEY AUTOINCREMENT, LogDat TEXT, Rudel TEXT, Teilnehmer TEXT, Jahr TEXT, Dauer TEXT, MHund INTEGER, MLeine TEXT, BLand TEXT, Lkr TEXT, BeiOrt TEXT, BimaName TEXT, Wetter TEXT, Temperat TEXT, RegenVor TEXT, KmRad TEXT, KmAuto TEXT, KmFuss TEXT, KmTotal TEXT, KmAuProz TEXT, KmFuProz TEXT, KmRaProz TEXT, SpGut TEXT, SpMittel, SpSchlecht TEXT, SpurFund TEXT, SpurLang TEXT, SpurTiere Text, SpSicher TEXT, WelpenSp TEXT, WelpenAnz TEXT, WpSicher TEXT, LosungGes TEXT, LosungAnz TEXT, LosungGen TEXT, UrinAnz TEXT, UrinGen TEXT, OestrAnz TEXT, OestrGen TEXT, HaarAnz TEXT, HaarGen TEXT, LosungKm TEXT, GenetiKm TEXT, Hinweise TEXT, Bemerk TEXT, IntKomm TEXT, BimaNr TEXT, BimaNutzer TEXT, BimaAGV TEXT, FallNum INTEGER, Sent INTEGER DEFAULT 0)',
'CREATE TABLE excursion (ID INTEGER PRIMARY KEY AUTOINCREMENT, LogDat TEXT, Rudel TEXT, Teilnehmer TEXT, Jahr TEXT, Dauer TEXT, MHund INTEGER, MLeine TEXT, BLand TEXT, Lkr TEXT, BeiOrt TEXT, BimaName TEXT, Wetter TEXT, Temperat TEXT, RegenVor TEXT, KmRad TEXT, KmAuto TEXT, KmFuss TEXT, KmTotal TEXT, KmAuProz TEXT, KmFuProz TEXT, KmRaProz TEXT, SpGut TEXT, SpMittel, SpSchlecht TEXT, SpurFund TEXT, SpurLang TEXT, SpurTiere Text, SpSicher TEXT, WelpenSp TEXT, WelpenAnz TEXT, WpSicher TEXT, LosungGes TEXT, LosungAnz TEXT, LosungGen TEXT, UrinAnz TEXT, UrinGen TEXT, OestrAnz TEXT, OestrGen TEXT, HaarAnz TEXT, HaarGen TEXT, LosungKm TEXT, GenetiKm TEXT, Hinweise TEXT, Bemerk TEXT, IntKomm TEXT, BimaNr TEXT, BimaNutzer TEXT, BimaAGV TEXT, FallNum INTEGER, Weg TEXT, Sent INTEGER DEFAULT 0)',
);
await excursionDB.execute(
'CREATE TABLE excursionTemplates (ID INTEGER PRIMARY KEY AUTOINCREMENT, LogDat TEXT, Rudel TEXT, Teilnehmer TEXT, Jahr TEXT, Dauer TEXT, MHund INTEGER, MLeine TEXT, BLand TEXT, Lkr TEXT, BeiOrt TEXT, BimaName TEXT, Wetter TEXT, Temperat TEXT, RegenVor TEXT, KmRad TEXT, KmAuto TEXT, KmFuss TEXT, KmTotal TEXT, KmAuProz TEXT, KmFuProz TEXT, KmRaProz TEXT, SpGut TEXT, SpMittel, SpSchlecht TEXT, SpurFund TEXT, SpurLang TEXT, SpurTiere Text, SpSicher TEXT, WelpenSp TEXT, WelpenAnz TEXT, WpSicher TEXT, LosungGes TEXT, LosungAnz TEXT, LosungGen TEXT, UrinAnz TEXT, UrinGen TEXT, OestrAnz TEXT, OestrGen TEXT, HaarAnz TEXT, HaarGen TEXT, LosungKm TEXT, GenetiKm TEXT, Hinweise TEXT, Bemerk TEXT, IntKomm TEXT, BimaNr TEXT, BimaNutzer TEXT, BimaAGV TEXT, FallNum INTEGER)',
'CREATE TABLE excursionTemplates (ID INTEGER PRIMARY KEY AUTOINCREMENT, LogDat TEXT, Rudel TEXT, Teilnehmer TEXT, Jahr TEXT, Dauer TEXT, MHund INTEGER, MLeine TEXT, BLand TEXT, Lkr TEXT, BeiOrt TEXT, BimaName TEXT, Wetter TEXT, Temperat TEXT, RegenVor TEXT, KmRad TEXT, KmAuto TEXT, KmFuss TEXT, KmTotal TEXT, KmAuProz TEXT, KmFuProz TEXT, KmRaProz TEXT, SpGut TEXT, SpMittel, SpSchlecht TEXT, SpurFund TEXT, SpurLang TEXT, SpurTiere Text, SpSicher TEXT, WelpenSp TEXT, WelpenAnz TEXT, WpSicher TEXT, LosungGes TEXT, LosungAnz TEXT, LosungGen TEXT, UrinAnz TEXT, UrinGen TEXT, OestrAnz TEXT, OestrGen TEXT, HaarAnz TEXT, HaarGen TEXT, LosungKm TEXT, GenetiKm TEXT, Hinweise TEXT, Bemerk TEXT, IntKomm TEXT, BimaNr TEXT, BimaNutzer TEXT, BimaAGV TEXT, FallNum INTEGER, Weg TEXT)',
);
}

View File

@@ -1,5 +1,8 @@
import 'package:animations/animations.dart';
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/widgets/anzahlen.dart';
import 'package:fforte/screens/excursion/widgets/bima_nutzer.dart';
import 'package:fforte/screens/excursion/widgets/hinweise.dart';
@@ -7,13 +10,17 @@ import 'package:fforte/screens/excursion/widgets/hund_u_leine.dart';
import 'package:fforte/screens/excursion/widgets/letzter_niederschlag.dart';
import 'package:fforte/screens/excursion/widgets/spur_gefunden.dart';
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/sharedMethods/check_required.dart';
import 'package:fforte/screens/sharedMethods/save_template.dart';
import 'package:fforte/screens/sharedWidgets/datum.dart';
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;
@@ -34,6 +41,18 @@ class ExcursionMain extends StatefulWidget {
class _ExcursionMainState extends State<ExcursionMain> {
int currentStep = 0;
late bool isTemplate;
Position currentPosition = Position(
longitude: 10.0,
latitude: 51.0,
timestamp: DateTime.now(),
accuracy: 0.0,
altitude: 0.0,
heading: 0.0,
speed: 0.0,
speedAccuracy: 0.0,
altitudeAccuracy: 0.0,
headingAccuracy: 0.0,
);
// all TextEditingController because its easier
Map<String, Map<String, dynamic>> rmap = {
@@ -55,6 +74,7 @@ class _ExcursionMainState extends State<ExcursionMain> {
"BimaAGV": {"controller": TextEditingController(), "required": false},
// Step 2
"Weg": {"controller": TextEditingController(), "required": false},
"Wetter": {"controller": TextEditingController(), "required": false},
"Temperat": {"controller": TextEditingController(), "required": false},
"RegenVor": {"controller": TextEditingController(), "required": false},
@@ -100,6 +120,27 @@ class _ExcursionMainState extends State<ExcursionMain> {
@override
void initState() {
GeolocatorService.deteterminePosition()
.then((result) => currentPosition = result)
.catchError((error) {
if (error is LocationDisabledException) {
if (mounted) {
SnackBarHelper.showSnackBarMessage(
context,
AppLocalizations.of(context)!.locationDisabled,
);
}
} else if (error is LocationForbiddenException) {
if (mounted) {
SnackBarHelper.showSnackBarMessage(
context,
AppLocalizations.of(context)!.locationForbidden,
);
}
}
return currentPosition;
});
if (widget.existingData?.isNotEmpty ?? false) {
for (var key in widget.existingData!.keys) {
rmap[key]!["controller"]!.text =
@@ -130,7 +171,6 @@ class _ExcursionMainState extends State<ExcursionMain> {
return puff;
}
@override
Widget build(BuildContext context) {
List<Step> getSteps() => [
@@ -253,6 +293,27 @@ class _ExcursionMainState extends State<ExcursionMain> {
title: Text(AppLocalizations.of(context)!.umstaendeUndAktionen),
content: Column(
children: [
// ---------- Tracking
ElevatedButton(
onPressed:
() => Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return Tracking(
weg: rmap["Weg"]!["controller"]!,
startPosition: LatLng(
currentPosition.latitude,
currentPosition.longitude,
),
);
},
),
),
child: Text(AppLocalizations.of(context)!.tracking),
),
const SizedBox(height: 10),
// ---------- Weather
VarTextField(
textController: rmap["Wetter"]!["controller"]!,
@@ -390,7 +451,6 @@ class _ExcursionMainState extends State<ExcursionMain> {
currentStep += 1;
});
} else {
if (widget.isSent) {
Navigator.pushNamedAndRemoveUntil(
context,

View File

@@ -0,0 +1,186 @@
import 'dart:async';
import 'dart:math';
import 'package:fforte/l10n/app_localizations.dart';
import 'package:fforte/screens/helper/snack_bar_helper.dart';
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:geolocator/geolocator.dart';
import 'package:latlong2/latlong.dart';
class Tracking extends StatefulWidget {
final LatLng startPosition;
final TextEditingController weg;
const Tracking({super.key, required this.startPosition, required this.weg});
@override
State<Tracking> createState() => _TrackingState();
}
class _TrackingState extends State<Tracking> {
List<LatLng> pathList = [];
StreamSubscription<Position>? positionStream;
bool positionStreamRunning = false;
Random rand = Random();
@override
void initState() {
// TODO debugging (i guess)
pathList.add(
LatLng(widget.startPosition.latitude, widget.startPosition.longitude),
);
if (widget.weg.text.isNotEmpty) {
for (var element in widget.weg.text.split(";")) {
List<String> posSplit = element.split(",");
try {
posSplit[0] = posSplit[0].substring(0, 9);
posSplit[1] = posSplit[1].substring(0, 9);
} on RangeError {
// ignore because the double is short enough then
}
pathList.add(
LatLng(double.parse(posSplit.first), double.parse(posSplit[1])),
);
}
}
super.initState();
}
@override
void dispose() {
if (positionStream != null) positionStream!.cancel();
bool isFirst = true;
if (pathList.isNotEmpty) {
for (var pos in pathList) {
if (!isFirst) {
widget.weg.text += ";";
} else {
isFirst = false;
}
widget.weg.text += "${pos.latitude},${pos.longitude}";
}
}
super.dispose();
}
final LocationSettings streamLocationSettings = LocationSettings(
accuracy: LocationAccuracy.high,
distanceFilter: 10,
);
void onTrackingStart() async {
// // notification handling for tracking in background notification
// PermissionStatus permissionStatus =
// await NotificationPermissions.getNotificationPermissionStatus();
//
// if (permissionStatus != PermissionStatus.granted) {
// await NotificationPermissions.requestNotificationPermissions();
// }
positionStream = Geolocator.getPositionStream(
locationSettings: AndroidSettings(
accuracy: LocationAccuracy.high,
distanceFilter: 0,
foregroundNotificationConfig:
mounted
? ForegroundNotificationConfig(
notificationTitle:
AppLocalizations.of(context)!.trackingRunningInBackground,
notificationText: "",
)
: null,
),
).listen((Position? position) {
if (position != null) {
// pathList.add(LatLng(position.latitude, position.longitude));
setState(() {
pathList.add(LatLng(rand.nextInt(5) + 40, position.longitude));
});
} else {
if (mounted) {
SnackBarHelper.showSnackBarMessage(
context,
AppLocalizations.of(context)!.couldntDeterminePosition,
);
}
}
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(AppLocalizations.of(context)!.tracking),
// leading: IconButton(onPressed: () {}, icon: Icon(Icons.arrow_back_rounded)),
actions: [
if (positionStreamRunning)
IconButton(
onPressed: () {
setState(() {
positionStreamRunning = false;
positionStream!.cancel();
});
},
icon: Icon(Icons.stop_rounded),
),
IconButton(
onPressed: () {
if (positionStreamRunning) {
positionStreamRunning = false;
positionStream?.pause();
} else {
positionStreamRunning = true;
onTrackingStart();
}
setState(() {});
},
icon:
positionStreamRunning
? Icon(Icons.pause)
: Icon(Icons.play_arrow),
),
],
),
body: FlutterMap(
mapController: MapController(),
options: MapOptions(
interactionOptions: const InteractionOptions(
flags:
InteractiveFlag.pinchZoom |
InteractiveFlag.drag |
InteractiveFlag.pinchMove,
),
initialCenter: widget.startPosition,
initialZoom: 16.0,
),
children: [
TileLayer(
urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
userAgentPackageName: 'com.example.app',
),
if (pathList.isNotEmpty)
PolylineLayer(
polylines: [
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,
),
],
),
],
),
);
}
}

View File

@@ -82,3 +82,4 @@
11.mai 1h 15min
12.mai 5h 30min
13.mai 1h 50min
15.mai 2h 30min