Update
This commit is contained in:
parent
84a37b9603
commit
5dbac7f8f5
|
|
@ -1,4 +1,4 @@
|
||||||
# Eagle Tr@cker
|
# Eagle Tr@cker
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
flutter build --release
|
flutter run --release
|
||||||
|
|
@ -23,7 +23,7 @@ if (flutterVersionName == null) {
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace "com.example.gps_map_flowpoint"
|
namespace "com.example.eagletracker"
|
||||||
compileSdkVersion flutter.compileSdkVersion
|
compileSdkVersion flutter.compileSdkVersion
|
||||||
ndkVersion flutter.ndkVersion
|
ndkVersion flutter.ndkVersion
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:label="gps_map_flowpoint"
|
android:label="eagletracker"
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
android:icon="@mipmap/ic_launcher">
|
android:icon="@mipmap/ic_launcher">
|
||||||
<activity
|
<activity
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
|
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class for the BLE provider
|
||||||
|
*/
|
||||||
|
|
||||||
class BLEProvider with ChangeNotifier {
|
class BLEProvider with ChangeNotifier {
|
||||||
List<ScanResult> _scanResults = [];
|
List<ScanResult> _scanResults = [];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,9 @@ import 'dart:convert';
|
||||||
|
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class to save the devices pinned by the user
|
||||||
|
*/
|
||||||
class DevicesSaved {
|
class DevicesSaved {
|
||||||
String deviceName = '';
|
String deviceName = '';
|
||||||
String deviceAddress = '';
|
String deviceAddress = '';
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,9 @@ class GeoPosition {
|
||||||
final double altitude;
|
final double altitude;
|
||||||
final double speed;
|
final double speed;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class for the geographical position element needed for tracking the bird
|
||||||
|
*/
|
||||||
GeoPosition({
|
GeoPosition({
|
||||||
required this.latitude,
|
required this.latitude,
|
||||||
required this.longitude,
|
required this.longitude,
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,9 @@ import 'dart:convert';
|
||||||
|
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class to save the preferences of the user
|
||||||
|
*/
|
||||||
class PreferenceSaved {
|
class PreferenceSaved {
|
||||||
int timeTakingPointSecond = 0;
|
int timeTakingPointSecond = 0;
|
||||||
|
|
||||||
|
|
|
||||||
20
lib/config.dart
Normal file
20
lib/config.dart
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
/*
|
||||||
|
* Singleton class for configuration
|
||||||
|
*/
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
|
class Config {
|
||||||
|
static final Config _instance = Config._internal();
|
||||||
|
|
||||||
|
Config._internal();
|
||||||
|
|
||||||
|
factory Config() {
|
||||||
|
return _instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
String projectName = 'Eagle Tr@cker';
|
||||||
|
bool debug = kDebugMode; // Set to true to enable debug logs
|
||||||
|
int manufacturerId = 49406; // Manufacturer ID setted in arduino code
|
||||||
|
int defaultTimeCapture = 10; // Default time capture in seconds
|
||||||
|
}
|
||||||
|
|
@ -1,9 +1,15 @@
|
||||||
|
import 'package:eagletracker/config.dart';
|
||||||
|
import 'package:eagletracker/function.dart';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
|
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
|
||||||
import 'package:flutter_osm_plugin/flutter_osm_plugin.dart';
|
|
||||||
import 'package:eagletracker/class/BLEProvider.dart';
|
import 'package:eagletracker/class/BLEProvider.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class for the device advertisement scan information screen
|
||||||
|
*/
|
||||||
class DeviceAdvertizinScan extends StatefulWidget {
|
class DeviceAdvertizinScan extends StatefulWidget {
|
||||||
const DeviceAdvertizinScan(
|
const DeviceAdvertizinScan(
|
||||||
{super.key,
|
{super.key,
|
||||||
|
|
@ -20,38 +26,66 @@ class DeviceAdvertizinScan extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _DeviceAdvertizinScanState extends State<DeviceAdvertizinScan> {
|
class _DeviceAdvertizinScanState extends State<DeviceAdvertizinScan> {
|
||||||
|
Config config = Config();
|
||||||
List<ScanResult> scanResults = [];
|
List<ScanResult> scanResults = [];
|
||||||
|
|
||||||
TextEditingController tecTakingPoint = TextEditingController();
|
double latitude = 0;
|
||||||
MapController mapController = MapController(
|
double longitude = 0;
|
||||||
initPosition: GeoPoint(latitude: 47.4358055, longitude: 8.4737324),
|
double altitude = 0;
|
||||||
);
|
double speed = 0;
|
||||||
|
|
||||||
String value = 'Aucune valeur reçue';
|
String value = 'Aucune valeur reçue';
|
||||||
|
|
||||||
|
TextEditingController tecTakingPoint = TextEditingController();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
//Get the BLE provider and the scan results
|
||||||
final bleProvider = Provider.of<BLEProvider>(context);
|
final bleProvider = Provider.of<BLEProvider>(context);
|
||||||
scanResults = bleProvider.scanResults;
|
scanResults = bleProvider.scanResults;
|
||||||
|
|
||||||
|
//Check if the device is in the list of scanned devices, make sure the manufacturer data is not empty and the remoteId is the same as the one selected
|
||||||
if (scanResults
|
if (scanResults
|
||||||
.where((element) =>
|
.where((element) =>
|
||||||
element.advertisementData.manufacturerData.isNotEmpty &&
|
element.advertisementData.manufacturerData.isNotEmpty &&
|
||||||
element.device.remoteId.toString() == widget.remoteId)
|
element.device.remoteId.toString() == widget.remoteId)
|
||||||
.isNotEmpty) {
|
.isNotEmpty) {
|
||||||
value = scanResults
|
value = scanResults
|
||||||
.where((element) =>
|
.where((element) {
|
||||||
element.device.remoteId.toString() == widget.remoteId)
|
if (element.device.remoteId.toString() == widget.remoteId) {
|
||||||
.map((e) => e.advertisementData.toString())
|
if (element.advertisementData.manufacturerData[49406] != null) {
|
||||||
.first;
|
List<int> data =
|
||||||
|
element.advertisementData.manufacturerData[49406]!;
|
||||||
|
|
||||||
// Faites quelque chose avec la variable 'value' ici
|
//Convert the data to hexadecimal
|
||||||
|
List<String> hexList =
|
||||||
|
data.map((int value) => value.toRadixString(16)).toList();
|
||||||
|
|
||||||
|
if (config.debug) {
|
||||||
|
print(data);
|
||||||
|
print(hexList);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Get the values from the advertisement data
|
||||||
|
List<double> values = getValueFromAdvData(data);
|
||||||
|
latitude = values[0];
|
||||||
|
longitude = values[1];
|
||||||
|
altitude = values[2];
|
||||||
|
speed = values[3];
|
||||||
|
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return element.device.remoteId.toString() == widget.remoteId;
|
||||||
|
})
|
||||||
|
.map((e) => e.toString())
|
||||||
|
.first;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title:
|
title: Text(config.projectName, style: TextStyle(color: Colors.white)),
|
||||||
const Text("Eagle Tr@cker", style: TextStyle(color: Colors.white)),
|
|
||||||
backgroundColor: Colors.blue,
|
backgroundColor: Colors.blue,
|
||||||
leading: IconButton(
|
leading: IconButton(
|
||||||
icon: const Icon(Icons.arrow_back, color: Colors.white),
|
icon: const Icon(Icons.arrow_back, color: Colors.white),
|
||||||
|
|
@ -97,6 +131,13 @@ class _DeviceAdvertizinScanState extends State<DeviceAdvertizinScan> {
|
||||||
child: Text(value,
|
child: Text(value,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: Colors.black, fontWeight: FontWeight.bold))),
|
color: Colors.black, fontWeight: FontWeight.bold))),
|
||||||
|
Container(
|
||||||
|
margin: const EdgeInsets.all(10),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: Text(
|
||||||
|
"Valeurs de la longitude ($longitude), latitude ($latitude), altitude ($altitude) et vitesse ($speed)",
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.black, fontWeight: FontWeight.bold)))
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
import 'package:eagletracker/config.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
|
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
|
||||||
import 'package:flutter_osm_plugin/flutter_osm_plugin.dart';
|
import 'package:flutter_osm_plugin/flutter_osm_plugin.dart';
|
||||||
import 'package:eagletracker/class/BLEProvider.dart';
|
import 'package:eagletracker/class/BLEProvider.dart';
|
||||||
|
|
@ -10,6 +12,9 @@ import 'package:eagletracker/class/preferenceSaved.dart';
|
||||||
import 'package:eagletracker/function.dart';
|
import 'package:eagletracker/function.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class for the device flow map screen
|
||||||
|
*/
|
||||||
class DeviceFlowMap extends StatefulWidget {
|
class DeviceFlowMap extends StatefulWidget {
|
||||||
const DeviceFlowMap(
|
const DeviceFlowMap(
|
||||||
{super.key, required this.deviceName, required this.deviceAddress});
|
{super.key, required this.deviceName, required this.deviceAddress});
|
||||||
|
|
@ -22,46 +27,146 @@ class DeviceFlowMap extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _DeviceFlowMapState extends State<DeviceFlowMap> {
|
class _DeviceFlowMapState extends State<DeviceFlowMap> {
|
||||||
|
final Config config = Config();
|
||||||
|
PreferenceSaved preferenceSaved = PreferenceSaved.empty();
|
||||||
|
|
||||||
MapController mapController = MapController(
|
MapController mapController = MapController(
|
||||||
initPosition: GeoPoint(latitude: 47.4358055, longitude: 8.4737324),
|
initPosition: GeoPoint(latitude: 46.1723, longitude: 7.1841));
|
||||||
);
|
|
||||||
|
|
||||||
List<ScanResult> scanResults = [];
|
List<ScanResult> scanResults = [];
|
||||||
List<GeoPosition> geoPositions = [];
|
List<GeoPosition> geoPositions = [];
|
||||||
|
List<int> advDataLastReception = [];
|
||||||
|
|
||||||
Timer? timer;
|
Timer? timer;
|
||||||
double progression = 0.0;
|
double progression = 0.0;
|
||||||
int timeTakingPointSecond = 10;
|
int timeTakingPointSecond = 10;
|
||||||
int updateInterval = 500; // Intervalle de mise à jour en millisecondes
|
int updateInterval = 500;
|
||||||
|
|
||||||
PreferenceSaved preferenceSaved = PreferenceSaved.empty();
|
double vitesseMax = 0.0;
|
||||||
|
double altitudeMax = 0.0;
|
||||||
|
|
||||||
|
double latitude = 0;
|
||||||
|
double longitude = 0;
|
||||||
|
double altitude = 0;
|
||||||
|
double speed = 0;
|
||||||
|
|
||||||
|
bool targetTracking = false;
|
||||||
|
bool drawRoad = false;
|
||||||
|
|
||||||
|
GeoPoint targetTrackingPoint = GeoPoint(latitude: 0, longitude: 0);
|
||||||
|
GeoPoint targetTrackingPointHistory = GeoPoint(latitude: 0, longitude: 0);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
//Positions de test
|
SystemChrome.setPreferredOrientations([
|
||||||
geoPositions = [
|
DeviceOrientation.portraitUp,
|
||||||
GeoPosition(latitude: 1.0, longitude: 2.0, altitude: 100.0, speed: 10.0),
|
DeviceOrientation.portraitDown,
|
||||||
GeoPosition(latitude: 3.0, longitude: 4.0, altitude: 200.0, speed: 20.0),
|
DeviceOrientation.landscapeLeft,
|
||||||
];
|
DeviceOrientation.landscapeRight,
|
||||||
|
]);
|
||||||
|
|
||||||
restoreLocal().then((value) {
|
restoreLocal().then((value) async {
|
||||||
setState(() {
|
preferenceSaved = value;
|
||||||
preferenceSaved = value;
|
timeTakingPointSecond = preferenceSaved.timeTakingPointSecond;
|
||||||
timeTakingPointSecond = preferenceSaved.timeTakingPointSecond;
|
|
||||||
|
|
||||||
if (timeTakingPointSecond > 0) {
|
// set the default time capture if the value is 0
|
||||||
int totalUpdates = (timeTakingPointSecond * 1000) ~/ updateInterval;
|
if (timeTakingPointSecond == 0) {
|
||||||
|
timeTakingPointSecond = config.defaultTimeCapture;
|
||||||
|
}
|
||||||
|
|
||||||
timer = Timer.periodic(Duration(milliseconds: updateInterval),
|
int totalUpdates = (timeTakingPointSecond * 1000) ~/ updateInterval;
|
||||||
(Timer timer) {
|
|
||||||
setState(() {
|
timer = Timer.periodic(Duration(milliseconds: updateInterval),
|
||||||
progression = (timer.tick % totalUpdates) / totalUpdates * 100.0;
|
(Timer timer) async {
|
||||||
});
|
setState(() {
|
||||||
});
|
progression = (timer.tick % totalUpdates) / totalUpdates * 100.0;
|
||||||
} else {
|
});
|
||||||
print('Erreur: timeTakingPointSecond doit être supérieur à zéro.');
|
|
||||||
|
// Check if the manufacturer data is not empty and the last reception is not empty
|
||||||
|
if (advDataLastReception.isNotEmpty && timer.tick % totalUpdates == 0) {
|
||||||
|
List<String> hexList = advDataLastReception
|
||||||
|
.map((int value) => value.toRadixString(16))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
if (config.debug) {
|
||||||
|
print(advDataLastReception);
|
||||||
|
print(hexList);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Get the values from the advertisement data
|
||||||
|
List<double> values = getValueFromAdvData(advDataLastReception);
|
||||||
|
latitude = values[0];
|
||||||
|
longitude = values[1];
|
||||||
|
altitude = values[2];
|
||||||
|
speed = values[3];
|
||||||
|
|
||||||
|
//Ajout de la position
|
||||||
|
GeoPosition geoPosition = GeoPosition(
|
||||||
|
latitude: latitude,
|
||||||
|
longitude: longitude,
|
||||||
|
altitude: altitude,
|
||||||
|
);
|
||||||
|
|
||||||
|
geoPositions.add(geoPosition);
|
||||||
|
|
||||||
|
//Vitesse max et altitude max
|
||||||
|
if (speed > vitesseMax) {
|
||||||
|
vitesseMax = speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (altitude > altitudeMax) {
|
||||||
|
altitudeMax = altitude;
|
||||||
|
}
|
||||||
|
|
||||||
|
targetTrackingPoint = GeoPoint(
|
||||||
|
latitude: latitude,
|
||||||
|
longitude: longitude,
|
||||||
|
);
|
||||||
|
|
||||||
|
//Draw the road if the option is enabled
|
||||||
|
mapController.clearAllRoads();
|
||||||
|
if (drawRoad) {
|
||||||
|
List<GeoPoint> linedsPoints = [];
|
||||||
|
|
||||||
|
for (var geoPosition in geoPositions) {
|
||||||
|
GeoPoint geoPoint = GeoPoint(
|
||||||
|
latitude: geoPosition.latitude,
|
||||||
|
longitude: geoPosition.longitude,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!linedsPoints.contains(geoPoint)) {
|
||||||
|
linedsPoints.add(geoPoint);
|
||||||
|
}
|
||||||
|
if (config.debug) {
|
||||||
|
print(linedsPoints.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (linedsPoints.length > 1) {
|
||||||
|
// Dessiner la route
|
||||||
|
await mapController.drawRoadManually(
|
||||||
|
linedsPoints,
|
||||||
|
const RoadOption(roadColor: Colors.red, zoomInto: false),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mapController.removeMarker(targetTrackingPointHistory);
|
||||||
|
mapController.addMarker(targetTrackingPoint,
|
||||||
|
markerIcon: const MarkerIcon(
|
||||||
|
icon: Icon(
|
||||||
|
Icons.star_outlined,
|
||||||
|
color: Colors.red,
|
||||||
|
size: 48,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
targetTrackingPointHistory = targetTrackingPoint;
|
||||||
|
if (targetTracking) {
|
||||||
|
mapController.changeLocation(targetTrackingPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
setState(() {});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -78,6 +183,16 @@ class _DeviceFlowMapState extends State<DeviceFlowMap> {
|
||||||
final bleProvider = Provider.of<BLEProvider>(context);
|
final bleProvider = Provider.of<BLEProvider>(context);
|
||||||
scanResults = bleProvider.scanResults;
|
scanResults = bleProvider.scanResults;
|
||||||
|
|
||||||
|
for (var scanResult in scanResults) {
|
||||||
|
if (scanResult.device.remoteId.toString() == widget.deviceAddress) {
|
||||||
|
if (scanResult.advertisementData.manufacturerData[49406] != null) {
|
||||||
|
advDataLastReception =
|
||||||
|
scanResult.advertisementData.manufacturerData[49406]!;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title:
|
title:
|
||||||
|
|
@ -86,6 +201,10 @@ class _DeviceFlowMapState extends State<DeviceFlowMap> {
|
||||||
leading: IconButton(
|
leading: IconButton(
|
||||||
icon: const Icon(Icons.arrow_back, color: Colors.white),
|
icon: const Icon(Icons.arrow_back, color: Colors.white),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
|
SystemChrome.setPreferredOrientations([
|
||||||
|
DeviceOrientation.portraitUp,
|
||||||
|
DeviceOrientation.portraitDown,
|
||||||
|
]);
|
||||||
mapController.dispose();
|
mapController.dispose();
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
},
|
},
|
||||||
|
|
@ -96,7 +215,7 @@ class _DeviceFlowMapState extends State<DeviceFlowMap> {
|
||||||
controller: mapController,
|
controller: mapController,
|
||||||
onMapIsReady: (bool value) async {
|
onMapIsReady: (bool value) async {
|
||||||
if (value) {
|
if (value) {
|
||||||
Future.delayed(const Duration(milliseconds: 500), () async {
|
Future.delayed(const Duration(milliseconds: 750), () async {
|
||||||
await mapController.currentLocation();
|
await mapController.currentLocation();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -149,14 +268,28 @@ class _DeviceFlowMapState extends State<DeviceFlowMap> {
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'Altitude: ${geoPositions.last.altitude.toStringAsFixed(2)} m',
|
'Altitude: ${altitude.toStringAsFixed(2)} m',
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'Vitesse: ${geoPositions.last.speed.toStringAsFixed(2)} km/h',
|
'Vitesse: ${speed.toStringAsFixed(2)} km/h',
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.black,
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'Alt. max: ${altitudeMax.toStringAsFixed(2)} m',
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.black,
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'Vit. max: ${vitesseMax.toStringAsFixed(2)} km/h',
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
|
|
@ -191,15 +324,17 @@ class _DeviceFlowMapState extends State<DeviceFlowMap> {
|
||||||
floatingActionButton:
|
floatingActionButton:
|
||||||
Row(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [
|
Row(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [
|
||||||
FloatingActionButton(
|
FloatingActionButton(
|
||||||
|
tooltip: 'Centrer la carte sur ma position actuelle',
|
||||||
heroTag: 'actionFab1',
|
heroTag: 'actionFab1',
|
||||||
backgroundColor: Colors.blue,
|
backgroundColor: Colors.blue,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
mapController.currentLocation();
|
mapController.currentLocation();
|
||||||
},
|
},
|
||||||
child: const Icon(Icons.map_outlined, color: Colors.white),
|
child: const Icon(Icons.gps_not_fixed_sharp, color: Colors.white),
|
||||||
),
|
),
|
||||||
FloatingActionButton(
|
FloatingActionButton(
|
||||||
heroTag: 'actionFab2',
|
heroTag: 'actionFab2',
|
||||||
|
tooltip: 'Réinitialiser les points',
|
||||||
backgroundColor: Colors.blue,
|
backgroundColor: Colors.blue,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
showDialog(
|
showDialog(
|
||||||
|
|
@ -241,6 +376,29 @@ class _DeviceFlowMapState extends State<DeviceFlowMap> {
|
||||||
child: const Icon(Icons.delete_sharp, color: Colors.white),
|
child: const Icon(Icons.delete_sharp, color: Colors.white),
|
||||||
),
|
),
|
||||||
FloatingActionButton(
|
FloatingActionButton(
|
||||||
|
heroTag: 'actionFab4',
|
||||||
|
tooltip: 'Centrer la carte sur la cible',
|
||||||
|
backgroundColor: targetTracking ? Colors.green : Colors.blue,
|
||||||
|
onPressed: () {
|
||||||
|
setState(() {
|
||||||
|
targetTracking = !targetTracking;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: const Icon(Icons.gps_fixed, color: Colors.white),
|
||||||
|
),
|
||||||
|
FloatingActionButton(
|
||||||
|
tooltip: 'Dessiner la route',
|
||||||
|
heroTag: 'actionFab6',
|
||||||
|
backgroundColor: drawRoad ? Colors.green : Colors.blue,
|
||||||
|
onPressed: () {
|
||||||
|
setState(() {
|
||||||
|
drawRoad = !drawRoad;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: const Icon(Icons.roundabout_left, color: Colors.white),
|
||||||
|
),
|
||||||
|
FloatingActionButton(
|
||||||
|
tooltip: 'Envoyer les positions par email',
|
||||||
heroTag: 'actionFab3',
|
heroTag: 'actionFab3',
|
||||||
backgroundColor: Colors.blue,
|
backgroundColor: Colors.blue,
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
|
|
@ -259,33 +417,6 @@ class _DeviceFlowMapState extends State<DeviceFlowMap> {
|
||||||
},
|
},
|
||||||
child: const Icon(Icons.mail, color: Colors.white),
|
child: const Icon(Icons.mail, color: Colors.white),
|
||||||
),
|
),
|
||||||
FloatingActionButton(
|
|
||||||
heroTag: 'actionFab4',
|
|
||||||
backgroundColor: Colors.blue,
|
|
||||||
onPressed: () {
|
|
||||||
List<GeoPoint> linedsPoints = [];
|
|
||||||
|
|
||||||
for (var geoPosition in geoPositions) {
|
|
||||||
GeoPoint geoPoint = GeoPoint(
|
|
||||||
latitude: geoPosition.latitude,
|
|
||||||
longitude: geoPosition.longitude,
|
|
||||||
);
|
|
||||||
|
|
||||||
linedsPoints.add(geoPoint);
|
|
||||||
|
|
||||||
mapController.clearAllRoads();
|
|
||||||
|
|
||||||
if (linedsPoints.length > 1 &&
|
|
||||||
linedsPoints.toSet().length == linedsPoints.length) {
|
|
||||||
mapController.drawRoadManually(
|
|
||||||
linedsPoints,
|
|
||||||
const RoadOption(roadColor: Colors.red),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: const Icon(Icons.draw, color: Colors.white),
|
|
||||||
),
|
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
|
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
|
||||||
import 'package:eagletracker/class/BLEProvider.dart';
|
import 'package:eagletracker/class/BLEProvider.dart';
|
||||||
import 'package:eagletracker/class/devicesSaved.dart' as DevicesSaved;
|
import 'package:eagletracker/class/devicesSaved.dart' as DevicesSaved;
|
||||||
|
|
@ -7,6 +8,9 @@ import 'package:eagletracker/deviceAdvertizinScan.dart';
|
||||||
import 'package:eagletracker/deviceFlowMap.dart';
|
import 'package:eagletracker/deviceFlowMap.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class for the device selection screen
|
||||||
|
*/
|
||||||
class DeviceSelection extends StatefulWidget {
|
class DeviceSelection extends StatefulWidget {
|
||||||
const DeviceSelection({super.key});
|
const DeviceSelection({super.key});
|
||||||
|
|
||||||
|
|
@ -25,13 +29,17 @@ class _DeviceSelectionState extends State<DeviceSelection> {
|
||||||
|
|
||||||
TextEditingController tecTimeTakingPointSecond = TextEditingController();
|
TextEditingController tecTimeTakingPointSecond = TextEditingController();
|
||||||
|
|
||||||
// Initialize Bluetooth scanning and subscription in initState
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
|
// Set the orientation to portrait
|
||||||
|
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
|
||||||
|
|
||||||
|
// Scanning for BLE devices in the default options
|
||||||
isScanning = true;
|
isScanning = true;
|
||||||
|
|
||||||
|
// Restore the saved preferences
|
||||||
PreferenceSaved.restoreLocal().then((value) {
|
PreferenceSaved.restoreLocal().then((value) {
|
||||||
setState(() {
|
setState(() {
|
||||||
preferenceSaved = value;
|
preferenceSaved = value;
|
||||||
|
|
@ -354,6 +362,9 @@ class _DeviceSelectionState extends State<DeviceSelection> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Function to check if the scan is activated
|
||||||
|
*/
|
||||||
bool checkIsScanning(BuildContext context, bool isScanning) {
|
bool checkIsScanning(BuildContext context, bool isScanning) {
|
||||||
if (!isScanning) {
|
if (!isScanning) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,14 @@
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:csv/csv.dart';
|
import 'package:csv/csv.dart';
|
||||||
import 'package:flutter_email_sender/flutter_email_sender.dart';
|
import 'package:flutter_email_sender/flutter_email_sender.dart';
|
||||||
import 'package:eagletracker/class/geoPosition.dart';
|
import 'package:eagletracker/class/geoPosition.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Function to generate a CSV file with the positions array
|
||||||
|
*/
|
||||||
Future<File> generateCsv(List<GeoPosition> positions) async {
|
Future<File> generateCsv(List<GeoPosition> positions) async {
|
||||||
List<List<dynamic>> csvData = [
|
List<List<dynamic>> csvData = [
|
||||||
['latitude', 'longitude', 'altitude', 'spped'] // Header
|
['latitude', 'longitude', 'altitude', 'spped'] // Header
|
||||||
|
|
@ -26,6 +30,9 @@ Future<File> generateCsv(List<GeoPosition> positions) async {
|
||||||
return csvFile;
|
return csvFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Function to send an email with the CSV file as an attachment
|
||||||
|
*/
|
||||||
void sendEmailWithAttachment(File csvFile) async {
|
void sendEmailWithAttachment(File csvFile) async {
|
||||||
final Email email = Email(
|
final Email email = Email(
|
||||||
subject: 'Eagle Tr@cker - Positions',
|
subject: 'Eagle Tr@cker - Positions',
|
||||||
|
|
@ -40,3 +47,43 @@ void sendEmailWithAttachment(File csvFile) async {
|
||||||
throw 'Failed to send email: $error';
|
throw 'Failed to send email: $error';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Function to get the values from the manufacturer data according to the data structure
|
||||||
|
*/
|
||||||
|
List<double> getValueFromAdvData(List<int> data) {
|
||||||
|
double latitude = 0;
|
||||||
|
double longitude = 0;
|
||||||
|
double altitude = 0;
|
||||||
|
double speed = 0;
|
||||||
|
|
||||||
|
List<double> values = [];
|
||||||
|
//Latitude
|
||||||
|
List<int> dataLatitude = data.sublist(0, 4);
|
||||||
|
ByteData byteDataLatitude =
|
||||||
|
ByteData.sublistView(Uint8List.fromList(dataLatitude));
|
||||||
|
latitude = byteDataLatitude.getFloat32(0, Endian.little);
|
||||||
|
values.add(latitude);
|
||||||
|
|
||||||
|
//Longitude
|
||||||
|
List<int> dataLongitude = data.sublist(4, 8);
|
||||||
|
ByteData byteDataLongitude =
|
||||||
|
ByteData.sublistView(Uint8List.fromList(dataLongitude));
|
||||||
|
longitude = byteDataLongitude.getFloat32(0, Endian.little);
|
||||||
|
values.add(longitude);
|
||||||
|
|
||||||
|
//Altitude
|
||||||
|
List<int> dataAltitude = data.sublist(8, 12);
|
||||||
|
ByteData byteDataAltitude =
|
||||||
|
ByteData.sublistView(Uint8List.fromList(dataAltitude));
|
||||||
|
altitude = byteDataAltitude.getFloat32(0, Endian.little);
|
||||||
|
values.add(altitude);
|
||||||
|
|
||||||
|
//Speed
|
||||||
|
List<int> dataSpeed = data.sublist(12, 16);
|
||||||
|
ByteData byteDataSpeed = ByteData.sublistView(Uint8List.fromList(dataSpeed));
|
||||||
|
speed = byteDataSpeed.getFloat32(0, Endian.little);
|
||||||
|
values.add(speed);
|
||||||
|
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import 'package:provider/provider.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
runApp(
|
runApp(
|
||||||
|
// Add the ChangeNotifierProvider to the root of the widget tree
|
||||||
MultiProvider(
|
MultiProvider(
|
||||||
providers: [
|
providers: [
|
||||||
ChangeNotifierProvider(create: (_) => BLEProvider()),
|
ChangeNotifierProvider(create: (_) => BLEProvider()),
|
||||||
|
|
@ -17,7 +18,6 @@ void main() {
|
||||||
class MyApp extends StatelessWidget {
|
class MyApp extends StatelessWidget {
|
||||||
const MyApp({super.key});
|
const MyApp({super.key});
|
||||||
|
|
||||||
// This widget is the root of your application.
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user