diff --git a/build/app/outputs/flutter-apk/app-release.apk b/build/app/outputs/flutter-apk/app-release.apk index 61fea711588d38279726ae4f8adf9ccccdec831c..e8b361e9384f9ad7a6cf9ef54a5637ae9a053bcb 100644 Binary files a/build/app/outputs/flutter-apk/app-release.apk and b/build/app/outputs/flutter-apk/app-release.apk differ diff --git a/lib/addevent.dart b/lib/addevent.dart index 2429951dcad9d6cb34cf56749bc945bc21e33be9..53e1fbdec09fa6e1b3d1a7cb3fd2a371949867f5 100644 --- a/lib/addevent.dart +++ b/lib/addevent.dart @@ -1,4 +1,3 @@ -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_datetime_picker/flutter_datetime_picker.dart'; @@ -26,6 +25,7 @@ class _AddEventPageState extends State<AddEvent> { var database = DatabaseInstance(); Future startGNSS() async { + final EventStoreInstance eventStore = EventStoreInstance(); debugPrint("Check Location Permission"); bool serviceStatus = false; bool hasPermission = false; @@ -66,7 +66,7 @@ class _AddEventPageState extends State<AddEvent> { alt = position.altitude.toString(); accuracy = position.accuracy; - if (syncGNSSData == true) { + if (eventStore.gnssSync == true) { if(mounted){ setState(() { //refresh UI on update @@ -86,7 +86,6 @@ class _AddEventPageState extends State<AddEvent> { @override void initState() { startGNSS(); - super.initState(); } @@ -140,14 +139,14 @@ class _AddEventPageState extends State<AddEvent> { } bool _validateInput(){ - final EventStoreInstance eventsStore = EventStoreInstance(); + final EventStoreInstance eventStore = EventStoreInstance(); if (RegExp(r'^[a-z A-Z . \- 0-9 , ( ) + - _ :]+$').hasMatch( - eventsStore.currentEvent.label)) { + eventStore.currentEvent.label)) { if (RegExp(r'^[a-z A-Z . \- 0-9 , ( ) + - _ :]+$').hasMatch( - eventsStore.currentEvent.description)) { - if(_validateLatitude(eventsStore.currentEvent.latitude)){ - if(_validateLongitude(eventsStore.currentEvent.longitude)){ - if(_validateElevation(eventsStore.currentEvent.elevation)){ + eventStore.currentEvent.description)) { + if(_validateLatitude(eventStore.currentEvent.latitude)){ + if(_validateLongitude(eventStore.currentEvent.longitude)){ + if(_validateElevation(eventStore.currentEvent.elevation)){ debugPrint('All inputs valid'); return true; @@ -160,30 +159,31 @@ class _AddEventPageState extends State<AddEvent> { } void _storeCurrentEvent() { - final EventStoreInstance eventsStore = EventStoreInstance(); + final EventStoreInstance eventStore = EventStoreInstance(); final ConfigurationStoreInstance configuration = ConfigurationStoreInstance(); - eventsStore.currentEvent.typeId = configuration.getEventIdFromName(eventsStore.currentEvent.type); - eventsStore.currentEvent.status = "PENDING"; - database.addEvent(eventsStore.currentEvent); //TODO: display feedback after this async function + eventStore.currentEvent.typeId = configuration.getEventIdFromName(eventStore.currentEvent.type); + eventStore.currentEvent.status = "PENDING"; + database.addEvent(eventStore.currentEvent); //TODO: display feedback after this async function + } @override Widget build(BuildContext context) { /* Get singletons to access relevant data here.*/ - final EventStoreInstance eventsStore = EventStoreInstance(); + final EventStoreInstance eventStore = EventStoreInstance(); final ConfigurationStoreInstance configuration = ConfigurationStoreInstance(); String gnssStatusText = ""; - if (true == syncGNSSData){ + if (true == eventStore.gnssSync){ // Update current event coordinates from GNSS stream - eventsStore.currentEvent.latitude = lat; - eventsStore.currentEvent.longitude = long; - eventsStore.currentEvent.elevation = alt; + eventStore.currentEvent.latitude = lat; + eventStore.currentEvent.longitude = long; + eventStore.currentEvent.elevation = alt; var date = DateTime.now().toUtc(); var isoDate = date.toIso8601String(); - eventsStore.currentEvent.startDate = isoDate; - eventsStore.currentEvent.endDate = isoDate; + eventStore.currentEvent.startDate = isoDate; + eventStore.currentEvent.endDate = isoDate; if(accuracy == 0.0){ gnssStatusText = "No-Fix"; @@ -208,14 +208,14 @@ class _AddEventPageState extends State<AddEvent> { children: <Widget>[ const SizedBox(height: 15.0), TextFormField( - initialValue: eventsStore.currentEvent.label, + initialValue: eventStore.currentEvent.label, autovalidateMode: AutovalidateMode.always, decoration: const InputDecoration( border: OutlineInputBorder(), labelText: 'Label', ), onChanged: (value){ - eventsStore.currentEvent.label = value; + eventStore.currentEvent.label = value; setState(() {}); }, validator: (value) { @@ -229,7 +229,7 @@ class _AddEventPageState extends State<AddEvent> { ), const SizedBox(height: 15.0), DropdownButtonFormField( - value: eventsStore.currentEvent.type, + value: eventStore.currentEvent.type, isExpanded: true, decoration: const InputDecoration( border: OutlineInputBorder(), @@ -243,12 +243,12 @@ class _AddEventPageState extends State<AddEvent> { ); }).toList(), onChanged: (value) { - eventsStore.currentEvent.type = value.toString(); + eventStore.currentEvent.type = value.toString(); } ), const SizedBox(height: 15.0), DropdownButtonFormField( - value: eventsStore.currentEvent.urn, + value: eventStore.currentEvent.urn, isExpanded: true, decoration: const InputDecoration( border: OutlineInputBorder(), @@ -262,21 +262,21 @@ class _AddEventPageState extends State<AddEvent> { ); }).toList(), onChanged: (value) { - eventsStore.currentEvent.urn = value.toString(); - eventsStore.currentEvent.urnId = + eventStore.currentEvent.urn = value.toString(); + eventStore.currentEvent.urnId = configuration.getDeviceIdFromUrn(value.toString()); } ), const SizedBox(height: 15.0), TextFormField( - initialValue: eventsStore.currentEvent.description, + initialValue: eventStore.currentEvent.description, autovalidateMode: AutovalidateMode.always, decoration: const InputDecoration( border: OutlineInputBorder(), labelText: 'Description' ), onChanged: (value){ - eventsStore.currentEvent.description = value; + eventStore.currentEvent.description = value; setState(() {}); }, validator: (value) { @@ -284,7 +284,7 @@ class _AddEventPageState extends State<AddEvent> { value!)) { return "Only: a-z , A-Z , _ , 0-9 , ,(Comma) , ( , ) , + , - , . , :"; } else { - //eventsStore.currentEvent.label = value; + //eventStore.currentEvent.label = value; return null; // Entered Text is valid } }, @@ -296,7 +296,7 @@ class _AddEventPageState extends State<AddEvent> { Flexible(child: TextFormField( controller: TextEditingController( - text: eventsStore.currentEvent.startDate), + text: eventStore.currentEvent.startDate), readOnly: true, decoration: const InputDecoration( labelText: 'Timestamp', @@ -309,8 +309,8 @@ class _AddEventPageState extends State<AddEvent> { onConfirm: (date) { //Only one field for start and end date. var isoDate = date.toIso8601String(); - eventsStore.currentEvent.startDate = isoDate; - eventsStore.currentEvent.endDate = isoDate; + eventStore.currentEvent.startDate = isoDate; + eventStore.currentEvent.endDate = isoDate; debugPrint('Date set to : $isoDate'); setState(() {}); }, @@ -323,10 +323,10 @@ class _AddEventPageState extends State<AddEvent> { onPressed: () { var date = DateTime.now().toUtc(); var isoDate = date.toIso8601String(); - eventsStore.currentEvent.startDate = isoDate; - eventsStore.currentEvent.endDate = isoDate; + eventStore.currentEvent.startDate = isoDate; + eventStore.currentEvent.endDate = isoDate; debugPrint('Date set to : ' + - eventsStore.currentEvent.endDate.toString()); + eventStore.currentEvent.endDate.toString()); setState(() {}); }, child: const Text('Now'), @@ -336,20 +336,20 @@ class _AddEventPageState extends State<AddEvent> { const SizedBox(height: 15.0), TextFormField( readOnly: false, - enabled: !syncGNSSData, + enabled: !eventStore.gnssSync, keyboardType: TextInputType.number, autovalidateMode: AutovalidateMode.onUserInteraction, controller: TextEditingController( - text: eventsStore.currentEvent.latitude.toString()), + text: eventStore.currentEvent.latitude.toString()), decoration: const InputDecoration( labelText: 'Latitude', border: OutlineInputBorder(), ), onChanged: (value) { - eventsStore.currentEvent.latitude = value; + eventStore.currentEvent.latitude = value; }, onFieldSubmitted: (value){ - if (!syncGNSSData) { + if (!eventStore.gnssSync) { setState(() {}); } }, @@ -369,21 +369,21 @@ class _AddEventPageState extends State<AddEvent> { const SizedBox(height: 15.0), TextFormField( readOnly: false, - enabled: !syncGNSSData, + enabled: !eventStore.gnssSync, keyboardType: TextInputType.number, autovalidateMode: AutovalidateMode.onUserInteraction, controller: TextEditingController( - text: eventsStore.currentEvent.longitude.toString()), + text: eventStore.currentEvent.longitude.toString()), decoration: const InputDecoration( labelText: 'Longitude', border: OutlineInputBorder(), ), onChanged: (value) { - eventsStore.currentEvent.longitude = value; + eventStore.currentEvent.longitude = value; }, onFieldSubmitted: (value){ - if (!syncGNSSData) { + if (!eventStore.gnssSync) { setState(() {}); } }, @@ -404,20 +404,20 @@ class _AddEventPageState extends State<AddEvent> { const SizedBox(height: 15.0), TextFormField( readOnly: false, - enabled: !syncGNSSData, + enabled: !eventStore.gnssSync, keyboardType: TextInputType.number, autovalidateMode: AutovalidateMode.onUserInteraction, controller: TextEditingController( - text: eventsStore.currentEvent.elevation.toString()), + text: eventStore.currentEvent.elevation.toString()), decoration: const InputDecoration( labelText: 'Elevation', border: OutlineInputBorder(), ), onChanged: (value) { - eventsStore.currentEvent.elevation = value; + eventStore.currentEvent.elevation = value; }, onFieldSubmitted: (value){ - if (!syncGNSSData) { + if (!eventStore.gnssSync) { setState(() {}); } }, @@ -442,10 +442,10 @@ class _AddEventPageState extends State<AddEvent> { Text(gnssStatusText), const SizedBox(width: 10), Switch( - value: syncGNSSData, + value: eventStore.gnssSync, onChanged: (value) { - syncGNSSData = value; - debugPrint('Switched to:' + syncGNSSData.toString()); + eventStore.gnssSync = value; + debugPrint('Switched to:' + eventStore.gnssSync.toString()); setState(() { //refresh the UI }); @@ -464,8 +464,8 @@ class _AddEventPageState extends State<AddEvent> { //Update time for next event var date = DateTime.now().toUtc(); var isoDate = date.toIso8601String(); - eventsStore.currentEvent.startDate = isoDate; - eventsStore.currentEvent.endDate = isoDate; + eventStore.currentEvent.startDate = isoDate; + eventStore.currentEvent.endDate = isoDate; debugPrint(date.toIso8601String()); } @@ -509,4 +509,3 @@ class _AddEventPageState extends State<AddEvent> { } //TODO: update shared preferences current event on adding event! -//TODO: add configuration bit GNSS and store in shared preferences! \ No newline at end of file diff --git a/lib/databaseconnector.dart b/lib/databaseconnector.dart index 2c80fd86bd7e17d6d47a5c7bce5e047b4f041b69..9d2c46d499b88f96fb80678d6d043e6bdf26b859 100644 --- a/lib/databaseconnector.dart +++ b/lib/databaseconnector.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + import 'package:sqflite/sqflite.dart'; import 'package:path/path.dart'; import 'dart:async'; @@ -45,13 +47,13 @@ abstract class DatabaseConnector{ } Future<int> addEvent(Event event) async { - int rowId = 0; - rowId = await database.insert( - eventTable, - event.toMapNoId(), - conflictAlgorithm: ConflictAlgorithm.fail, - ); - return rowId; + int rowId = 0; + rowId = await database.insert( + eventTable, + event.toMapNoId(), + conflictAlgorithm: ConflictAlgorithm.fail, + ); + return rowId; } // Ensure the events id is the correct row id! @@ -116,4 +118,16 @@ abstract class DatabaseConnector{ ); }); } + + // Create a json readable event dump + Future<String> eventDump() async { + List<Event> events = await getEvents(); + String ev = '['; + for (var event in events) { + ev += jsonEncode(event); + ev += ','; + } + ev += ']'; + return ev; + } } diff --git a/lib/datamodel.dart b/lib/datamodel.dart index 4f7975cbd21a43f4d0400b97bed7715e5ed49bc0..9eaeaed529cb818bcc2635de3cd7f3c6cf3b07c3 100644 --- a/lib/datamodel.dart +++ b/lib/datamodel.dart @@ -348,6 +348,7 @@ class ConfigurationStoreInstance extends ConfigurationStoreBase { abstract class EventStoreBase{ + bool gnssSync = true; Event currentEvent = Event( id: 0, urnId:-1, @@ -366,33 +367,15 @@ abstract class EventStoreBase{ Map<String, dynamic> toMap() { return { - 'currentEvent' : currentEvent.toJson() + 'currentEvent' : currentEvent.toJson(), + 'gnssSync' : gnssSync }; } void fromMap(Map<String, dynamic> map) { currentEvent = Event.fromJson(map['currentEvent']); + gnssSync = map['gnssSync']; } - - -//String getEventDump(List<Event> events){ -// String ev = '['; - -// for (var event in events) { -// ev += jsonEncode(event); -// ev += ','; -// } -// ev += ']'; -// return ev; -//} - -//fromEventDump(dump){ -// events = []; -// for (var entry in dump) { -// debugPrint('Added Event from Storage: ' + entry.toString()); -// events.add(Event.fromJson(entry)); -// } -//} } diff --git a/lib/main.dart b/lib/main.dart index 951db9b76d9c34f4c698d34b3ddf8b9ab501fda5..25b97d4151416adbf94cdf6cd2dc587f7b6b2bf6 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -33,7 +33,6 @@ Future<void> loadConfiguration() async { debugPrint('Failed to load Current Event configuration from shared preferences.'); } - //const storage = FlutterSecureStorage(); //TODO: move login information to secure storage //Map<String, String> allValues = await storage.readAll(); //print('Secure Storage: ' + allValues.toString()); @@ -41,7 +40,7 @@ Future<void> loadConfiguration() async { var database = DatabaseInstance(); await database.connect(); //Do once at start of APP! - //TODO: there should be a loding screen until configuration is loaded and database connection is established. + //TODO: there should be a loading screen until configuration is loaded and database connection is established. } void main() { diff --git a/lib/viewevents.dart b/lib/viewevents.dart index 880614c3faa54729b14fa6ede9ee9a0e181dfba3..5704da022ab71f6f0b1f55aa36220f5a05f42794 100644 --- a/lib/viewevents.dart +++ b/lib/viewevents.dart @@ -1,10 +1,10 @@ -import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'; +import 'package:path_provider/path_provider.dart'; import 'datamodel.dart'; -import 'package:http/http.dart' as http; import 'sensorconnector.dart'; import 'databaseconnector.dart'; +import 'package:path/path.dart'; class ViewEvents extends StatefulWidget { const ViewEvents({Key? key}) : super(key: key); @@ -17,7 +17,6 @@ class _ViewEvents extends State<ViewEvents> { // Get singleton to access locally stored events: final EventStoreInstance _events = EventStoreInstance(); String _syncStatus = ''; - var database = DatabaseInstance(); List<Event> localEvents = []; @@ -35,6 +34,24 @@ class _ViewEvents extends State<ViewEvents> { setState(() {}); //Got events from database, so update UI } + Future<void> dumpToFile() async{ + //localEvents = await database.getEvents(); + var date = DateTime.now().toUtc(); + var isoDate = date.toIso8601String(); + + String eventsJson = await database.eventDump(); + + final Directory? directory = await getExternalStorageDirectory(); + if(directory != null) { + String filename = 'time_event_dump.json'; + String filepath = join(directory.path, filename); + + final File file = File(filepath); + await file.writeAsString(eventsJson); + debugPrint('Stored file at: ' + filepath); + } + } + @override void initState() { _syncStatus = ''; @@ -262,6 +279,8 @@ class _ViewEvents extends State<ViewEvents> { label: const Text('Sync'), onPressed: () { syncEvents(); + + dumpToFile(); //TODO. Move to right button. }, ), ), @@ -271,4 +290,5 @@ class _ViewEvents extends State<ViewEvents> { ); } } -//TODO: allow editing fields here. Check validation of input value! \ No newline at end of file +//TODO: allow editing fields here. Check validation of input value! +//TODO: dump events to json for local backups in the field. "dumpToFile" \ No newline at end of file