Commit cf419147 authored by Nicolas Richard Walter Boeckh's avatar Nicolas Richard Walter Boeckh 💬

Merge branch 'ux-upgrades' into 'main'

Ux upgrades

See merge request !8
parents 8d46dd31 90c4dbfe
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"connectivity","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\connectivity-0.4.9+2\\\\","dependencies":[]},{"name":"flutter_blue","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\flutter_blue-0.7.2\\\\","dependencies":[]},{"name":"geolocator","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\geolocator-5.2.1\\\\","dependencies":["google_api_availability","location_permissions"]},{"name":"google_api_availability","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\google_api_availability-2.0.2\\\\","dependencies":[]},{"name":"location_permissions","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\location_permissions-2.0.4+1\\\\","dependencies":[]},{"name":"path_provider","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\path_provider-1.6.0\\\\","dependencies":[]},{"name":"permission_handler","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\permission_handler-4.2.0+hotfix.3\\\\","dependencies":[]},{"name":"shared_preferences","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\shared_preferences-0.5.7+2\\\\","dependencies":[]},{"name":"sqflite","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\sqflite-1.2.0\\\\","dependencies":[]}],"android":[{"name":"connectivity","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\connectivity-0.4.9+2\\\\","dependencies":[]},{"name":"flutter_blue","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\flutter_blue-0.7.2\\\\","dependencies":[]},{"name":"geolocator","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\geolocator-5.2.1\\\\","dependencies":["google_api_availability","location_permissions"]},{"name":"google_api_availability","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\google_api_availability-2.0.2\\\\","dependencies":[]},{"name":"location_permissions","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\location_permissions-2.0.4+1\\\\","dependencies":[]},{"name":"path_provider","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\path_provider-1.6.0\\\\","dependencies":[]},{"name":"permission_handler","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\permission_handler-4.2.0+hotfix.3\\\\","dependencies":[]},{"name":"shared_preferences","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\shared_preferences-0.5.7+2\\\\","dependencies":[]},{"name":"sqflite","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\sqflite-1.2.0\\\\","dependencies":[]}],"macos":[{"name":"connectivity_macos","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\connectivity_macos-0.1.0+4\\\\","dependencies":[]},{"name":"shared_preferences_macos","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\shared_preferences_macos-0.0.1+8\\\\","dependencies":[]},{"name":"sqflite","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\sqflite-1.2.0\\\\","dependencies":[]}],"linux":[],"windows":[],"web":[{"name":"connectivity_for_web","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\connectivity_for_web-0.3.1+2\\\\","dependencies":[]},{"name":"shared_preferences_web","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\shared_preferences_web-0.1.2+5\\\\","dependencies":[]}]},"dependencyGraph":[{"name":"connectivity","dependencies":["connectivity_macos","connectivity_for_web"]},{"name":"connectivity_for_web","dependencies":[]},{"name":"connectivity_macos","dependencies":[]},{"name":"flutter_blue","dependencies":[]},{"name":"geolocator","dependencies":["google_api_availability","location_permissions"]},{"name":"google_api_availability","dependencies":[]},{"name":"location_permissions","dependencies":[]},{"name":"path_provider","dependencies":[]},{"name":"permission_handler","dependencies":[]},{"name":"shared_preferences","dependencies":["shared_preferences_macos","shared_preferences_web"]},{"name":"shared_preferences_macos","dependencies":[]},{"name":"shared_preferences_web","dependencies":[]},{"name":"sqflite","dependencies":[]}],"date_created":"2020-09-07 01:52:18.814120","version":"1.21.0-10.0.pre.193"}
\ No newline at end of file
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"app_settings","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\app_settings-4.0.3\\\\","dependencies":[]},{"name":"connectivity","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\connectivity-0.4.9+2\\\\","dependencies":[]},{"name":"flutter_blue","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\flutter_blue-0.7.2\\\\","dependencies":[]},{"name":"geolocator","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\geolocator-5.2.1\\\\","dependencies":["google_api_availability","location_permissions"]},{"name":"google_api_availability","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\google_api_availability-2.0.2\\\\","dependencies":[]},{"name":"location_permissions","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\location_permissions-2.0.4+1\\\\","dependencies":[]},{"name":"path_provider","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\path_provider-1.6.0\\\\","dependencies":[]},{"name":"permission_handler","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\permission_handler-4.2.0+hotfix.3\\\\","dependencies":[]},{"name":"shared_preferences","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\shared_preferences-0.5.7+2\\\\","dependencies":[]},{"name":"sqflite","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\sqflite-1.2.0\\\\","dependencies":[]}],"android":[{"name":"app_settings","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\app_settings-4.0.3\\\\","dependencies":[]},{"name":"connectivity","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\connectivity-0.4.9+2\\\\","dependencies":[]},{"name":"flutter_blue","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\flutter_blue-0.7.2\\\\","dependencies":[]},{"name":"geolocator","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\geolocator-5.2.1\\\\","dependencies":["google_api_availability","location_permissions"]},{"name":"google_api_availability","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\google_api_availability-2.0.2\\\\","dependencies":[]},{"name":"location_permissions","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\location_permissions-2.0.4+1\\\\","dependencies":[]},{"name":"path_provider","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\path_provider-1.6.0\\\\","dependencies":[]},{"name":"permission_handler","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\permission_handler-4.2.0+hotfix.3\\\\","dependencies":[]},{"name":"shared_preferences","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\shared_preferences-0.5.7+2\\\\","dependencies":[]},{"name":"sqflite","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\sqflite-1.2.0\\\\","dependencies":[]}],"macos":[{"name":"connectivity_macos","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\connectivity_macos-0.1.0+4\\\\","dependencies":[]},{"name":"shared_preferences_macos","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\shared_preferences_macos-0.0.1+8\\\\","dependencies":[]},{"name":"sqflite","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\sqflite-1.2.0\\\\","dependencies":[]}],"linux":[],"windows":[],"web":[{"name":"connectivity_for_web","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\connectivity_for_web-0.3.1+2\\\\","dependencies":[]},{"name":"shared_preferences_web","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\shared_preferences_web-0.1.2+5\\\\","dependencies":[]}]},"dependencyGraph":[{"name":"app_settings","dependencies":[]},{"name":"connectivity","dependencies":["connectivity_macos","connectivity_for_web"]},{"name":"connectivity_for_web","dependencies":[]},{"name":"connectivity_macos","dependencies":[]},{"name":"flutter_blue","dependencies":[]},{"name":"geolocator","dependencies":["google_api_availability","location_permissions"]},{"name":"google_api_availability","dependencies":[]},{"name":"location_permissions","dependencies":[]},{"name":"path_provider","dependencies":[]},{"name":"permission_handler","dependencies":[]},{"name":"shared_preferences","dependencies":["shared_preferences_macos","shared_preferences_web"]},{"name":"shared_preferences_macos","dependencies":[]},{"name":"shared_preferences_web","dependencies":[]},{"name":"sqflite","dependencies":[]}],"date_created":"2020-09-14 03:13:52.288184","version":"1.21.0-10.0.pre.193"}
\ No newline at end of file
......@@ -15,6 +15,25 @@ flutter pub pub run intl_translation:extract_to_arb --output-dir=lib/localizatio
flutter pub pub run intl_translation:generate_from_arb --output-dir=lib/localization/l10n --no-use-deferred-loading lib/localization/localization.dart lib/localization/l10n/intl_en.arb lib/localization/l10n/intl_fr.arb
```
## Documentation
You can generate the application documentation yourself using the following command :
```bash
dartdoc
```
<details>
<summary>In case of "dartdoc failed: Invalid argument(s): join(null, "bin", "cache", "dart-sdk")"</summary>
Set the following environment variable:
```bash
FLUTTER_ROOT=<path_to_flutter_root_folder>
```
</details>
## File Structure
The project is divided in multiple components:
......
......@@ -6,6 +6,9 @@
additional functionality it is fine to subclass or reimplement
FlutterApplication and put your custom class here. -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<application
android:name="io.flutter.app.FlutterApplication"
android:label="logair.io"
......
......@@ -12,7 +12,7 @@ class AppLocalizationDelegate extends LocalizationsDelegate<AppLocalization> {
'fr'
].contains(locale.languageCode);
/// Delegates the loading of [Locale] to an [async] task, which enables it to be done in the background.
/// Delegates the loading of [Locale] to an async task, which enables it to be done in the background.
@override
Future<AppLocalization> load(Locale locale) => AppLocalization.load(locale);
......
......@@ -16,6 +16,7 @@ class HomeController {
factory HomeController() => _singleton;
/// [HomeController] initializer that sets the default initial values.
HomeController._internal() {
this._currentPage = 0;
this._previousPage = 0;
......
......@@ -4,14 +4,14 @@ import 'package:flutter/material.dart';
import 'package:flutter_map/plugin_api.dart';
import 'package:latlong/latlong.dart';
import 'package:logair_application/logic/handlers/data_handler.dart';
import 'package:logair_application/logic/handlers/database_handler.dart';
import 'package:logair_application/logic/handlers/main_database_handler.dart';
import 'package:logair_application/logic/handlers/network_handler.dart';
import 'package:logair_application/logic/data_header.dart';
import 'package:logair_application/logic/data_packet.dart';
import 'package:logair_application/logic/handlers/position_handler.dart';
import 'package:logair_application/utils/enums/pm_symbol.dart';
/// The [MapDisplayController] is a [Singleton] that handles the mapping of information.
/// The [MapDisplayController] is a singleton that handles the mapping of information.
class MapDisplayController {
/// Whether or not the user has assumed manual control of the [FlutterMap],
bool _manualMotion = false;
......@@ -35,20 +35,20 @@ class MapDisplayController {
Marker _marker;
Future<void> _rebuildMarkers() async {
List<DataPacket> packets = await DatabaseHandler().getLastPackets(200);
List<DataPacket> packets = await MainDatabaseHandler().getLastPackets(200);
packets.forEach((DataPacket packet) {
switch (_pmSymbol) {
case PMSymbol.PM1:
this.addToList(this._currentPos, packet.pm1(), 200);
this.addToList(this._currentPos, packet.pm1, 200);
break;
case PMSymbol.PM1:
this.addToList(this._currentPos, packet.pm2_5(), 200);
this.addToList(this._currentPos, packet.pm2_5, 200);
break;
case PMSymbol.PM4:
this.addToList(this._currentPos, packet.pm4(), 200);
this.addToList(this._currentPos, packet.pm4, 200);
break;
case PMSymbol.PM10:
this.addToList(this._currentPos, packet.pm10(), 200);
this.addToList(this._currentPos, packet.pm10, 200);
break;
default:
break;
......@@ -90,7 +90,7 @@ class MapDisplayController {
}
/// This function is executed on first initialization of the [MapDisplayController]
/// TODO: Throttle to 1 in 5 updates for efficiency ?
// TODO: Throttle to 1 in 5 updates for efficiency ?
MapDisplayController._internal() {
/// A new [MapController] is used as the global Controller.
/// This avoids Garbage Collection rules and enables the [FlutterMap] to survive [State] changes, and other such events.
......@@ -103,10 +103,9 @@ class MapDisplayController {
return;
/// If a [BluetoothDevice] is connected, then the [DataHandler] will have a [DataPacket] available.
DataPacket latest = DataHandler().getLatestData();
DataPacket latest = null;
/// If the [DataPacket] is older than 5 seconds we invalidate it.
if (latest != null && latest.timestamp() <= (DateTime.now().millisecondsSinceEpoch - 5000))
if (latest != null && latest.timestamp <= (DateTime.now().millisecondsSinceEpoch - 5000))
latest = null;
/// Keep track of the last [Position] to draw [Polyline]s.
......@@ -120,6 +119,7 @@ class MapDisplayController {
this._controller.move(this._currentPos, this._zoom);
/// Move the [Marker] to the current [Position].
// TODO: Use StreamBuilder in Map.
this._marker = this._buildLocationMarker(this._currentPos);
if ((this._lastPos != null && this._currentPos != null)) {// && this._distance.as(LengthUnit.Meter, this._lastPos, this._currentPos) >= 2)) {
......@@ -136,20 +136,20 @@ class MapDisplayController {
if (latest != null) {
switch (_pmSymbol) {
case PMSymbol.PM1:
if (latest.pm1() != null)
this.addToList(this._currentPos, latest.pm1(), 200);
if (latest.pm1 != null)
this.addToList(this._currentPos, latest.pm1, 200);
break;
case PMSymbol.PM2_5:
if (latest.pm2_5() != null)
this.addToList(this._currentPos, latest.pm2_5(), 200);
if (latest.pm2_5 != null)
this.addToList(this._currentPos, latest.pm2_5, 200);
break;
case PMSymbol.PM4:
if (latest.pm4() != null)
this.addToList(this._currentPos, latest.pm4(), 200);
if (latest.pm4 != null)
this.addToList(this._currentPos, latest.pm4, 200);
break;
case PMSymbol.PM10:
if (latest.pm10() != null)
this.addToList(this._currentPos, latest.pm10(), 200);
if (latest.pm10 != null)
this.addToList(this._currentPos, latest.pm10, 200);
break;
default:
break;
......@@ -161,7 +161,7 @@ class MapDisplayController {
new Timer.periodic(Duration(minutes: 1), (Timer t) {
if (this._controller != null && this._controller.bounds != null) {
//print('${this._controller.bounds.southWest}, ${this._controller.bounds.northEast}');
NetworkHandler().getProximityPoints(DataHeader().deviceID, this._controller.bounds);
NetworkHandler().getProximityPoints(DataHeader().deviceId, this._controller.bounds);
}
});
}
......
......@@ -19,19 +19,28 @@ class DataHeader {
bool _headerSet;
String _deviceId;
String _url;
void setHeader(List<int> header) {
this._headerData = String.fromCharCodes(header.sublist(1, header.length - 1)).split(',');
this._deviceId = this._headerData[0];
this._url = this._headerData[1];
this._headerSet = true;
}
void setHeaderFromExisting({String deviceId = '', String url = ''}) {
this._headerData = [deviceID, url];
void setHeaderFromExisting({@required String deviceId, @required String url}) {
this._headerData = [deviceId, url];
this._deviceId = deviceId;
this._url = url;
this._headerSet = true;
}
/// Retrieve the deviceID from the header
String get deviceID => (this._headerData != null && this._headerData.length > 0) ? this._headerData[0] : '';
String get url => (this._headerData != null && this._headerData.length > 0) ? this._headerData[1] : '';
String get deviceId => (this._headerSet && this._deviceId != null) ? this._deviceId : '';
String get url => (this._headerSet && this._url != null) ? this._url : '';
/// @Getter for _headerSet;
bool get headerSet => this._headerSet;
......@@ -46,7 +55,7 @@ class DataHeader {
bool isEqual(List<int> that) => ListEquality().equals(_headerData, that);
// TODO
Map<String, dynamic> jsonify() => { 'device_id' : this._headerData[0], 'url': '000000000000' };
Map<String, dynamic> jsonify() => { 'device_id' : this.deviceId, 'url': this.url };
void dispose() {
this._headerSet = false;
......@@ -55,7 +64,7 @@ class DataHeader {
Stream<String> deviceIdStream() async* {
while(true) {
yield this.deviceID;
yield this._deviceId;
await Future.delayed(Duration(seconds: 1));
}
}
......
......@@ -11,6 +11,7 @@ class DataPacket {
List<int> _data;
int _timestamp;
String _deviceId;
String _url;
/* Location variables */
double _latitude;
......@@ -31,26 +32,32 @@ class DataPacket {
double _pm10;
// TODO Firmware v2
// ignore: unused_field
String _extraData;
int timestamp() => _timestamp;
String deviceId() => _deviceId ?? '';
double latitude() => _latitude;
double longitude() => _longitude;
int altitude() => _altitude;
int heading() => _heading;
double speed() => _speed;
double temperature() => _temperature;
double pressure() => _pressure;
double relativeHumidity() => _relativeHumidity;
double pm1() => _pm1;
double pm2_5() => _pm2_5;
double pm4() => _pm4;
double pm10() => _pm10;
int get timestamp => this._timestamp;
String get deviceId => this._deviceId ?? '';
String get url => this._url ?? '';
double get latitude => this._latitude;
double get longitude => this._longitude;
int get altitude => this._altitude;
int get heading => this._heading;
double get speed => this._speed;
double get temperature => this._temperature;
double get pressure => this._pressure;
double get relativeHumidity => this._relativeHumidity;
double get pm1 => this._pm1;
double get pm2_5 => this._pm2_5;
double get pm4 => this._pm4;
double get pm10 => this._pm10;
set deviceId(String newId) => this._deviceId = newId;
set url(String newUrl) => this._url = newUrl;
DataPacket(this._data) {
_timestamp = DateTime.now().millisecondsSinceEpoch;
List<String> acquiredData = String.fromCharCodes(this._data.sublist(1, this._data.length - 1)).split(',');
this._latitude = (acquiredData[0] != '') ? parseDoubleWrapper(acquiredData[0]) : null;
this._longitude = (acquiredData[1] != '') ? parseDoubleWrapper(acquiredData[1]) : null;
this._altitude = (acquiredData[2] != '') ? int.tryParse(acquiredData[2]) : null;
......@@ -80,9 +87,15 @@ class DataPacket {
DataPacket.fromData(this._timestamp, this._latitude, this._longitude, this._altitude, this._speed, this._heading, this._temperature, this._pressure, this._relativeHumidity, this._pm1, this._pm2_5, this._pm4, this._pm10, this._extraData);
// TODO Guard clause
DataPacket.fromMappedData(Map<String, dynamic> data) {
List<String> guardSet = ['device_id', 'url', 'timestamp_nix', 'latitude', 'longitude', 'altitude', 'speed', 'heading', 'temperature', 'pressure', 'relative_humidity', 'pm_1', 'pm_2_5', 'pm_4', 'pm_10', 'extra'];
if(data.keys.toList().where((String key) => guardSet.contains(key)).length < guardSet.length) {
throw Exception('Mapped Data Error');
}
this._deviceId = data['device_id'];
this._url = data['url'];
this._timestamp = data['timestamp_nix'];
this._latitude = data['latitude'];
this._longitude = data['longitude'];
......@@ -90,8 +103,8 @@ class DataPacket {
this._speed = data['speed'];
this._heading = data['heading'];
this._temperature = data['temperature'];
this._relativeHumidity = data['relative_humidity'];
this._pressure = data['pressure'];
this._relativeHumidity = data['relative_humidity'];
this._pm1 = data['pm_1'];
this._pm2_5 = data['pm_2_5'];
this._pm4 = data['pm_4'];
......@@ -117,6 +130,7 @@ class DataPacket {
this._pm10
];
/// Retrieves the DB insertable reference to this packet.
Map<String, dynamic> toMap() {
return {
'timestamp_nix': this._timestamp,
......@@ -126,13 +140,16 @@ class DataPacket {
'speed': this._speed,
'heading': this._heading,
'temperature': this._temperature,
'relative_humidity': this._temperature,
'relative_humidity': this.relativeHumidity,
'pressure': this._pressure,
'pm_1': this._pm1,
'pm_2_5': this._pm2_5,
'pm_4': this._pm4,
'pm_10': this._pm10,
'extra': ''
'extra': '',
'device_id': this._deviceId,
'url': this._url,
};
}
......
......@@ -6,11 +6,12 @@ import 'package:flutter_blue/flutter_blue.dart';
import 'package:kotlin_flavor/scope_functions.dart';
import 'package:logair_application/logic/handlers/bluetooth_wake_handler.dart';
import 'package:logair_application/logic/handlers/preference_handler.dart';
import 'package:logair_application/services/wake_service.dart';
import 'package:logair_application/utils/enums/bluetooth_connection_status.dart';
import 'package:logair_application/logic/handlers/data_handler.dart';
import 'package:logair_application/utils/enums/preference_keys.dart';
// TODO Attempt reacquiring device after 5 seconds inactivity.
/// Serves as a wrapper class around [FlutterBlue], and helps integrate it into the application with extensions
class BTLEHandler {
/// [Singleton]() instantiation factory.
......@@ -60,7 +61,7 @@ class BTLEHandler {
Stream<BluetoothConnectionStatus> getConnectionStatus() async* {
while (true) {
yield _bluetoothConnectionStatus;
await Future.delayed(Duration(seconds: 2));
await Future.delayed(Duration(milliseconds: 250));
}
}
......
import 'dart:async';
import 'package:logair_application/services/wake_service.dart';
/// Handler that wraps the [WakeService]
/// TODO flutter_blue seems to do so already, needs testing and iOS solution
// TODO: Relative end / start -> Start and end with guard instead of switch.
class BTWakeHandler {
factory BTWakeHandler() => _singleton;
......
import 'package:logair_application/logic/handlers/bluetooth_le_handler.dart';
import 'package:logair_application/logic/data_header.dart';
import 'package:logair_application/logic/data_packet.dart';
import 'package:logair_application/logic/handlers/database_handler.dart';
import 'package:logair_application/logic/handlers/main_database_handler.dart';
import 'package:logair_application/logic/handlers/network_handler.dart';
class DataHandler {
......@@ -57,53 +57,56 @@ class DataHandler {
bool wasSet = DataHeader().headerSet;
DataHeader().setHeader(packet);
/// Retroactively add all previous packets
if (!wasSet && DataHeader().headerSet)
_sortedData.forEach((packet) => DatabaseHandler().insertData(packet, DataHeader().deviceID, DataHeader().url));
if (!wasSet && DataHeader().headerSet) {
_sortedData.forEach((dataPacket) {
dataPacket.deviceId = DataHeader().deviceId;
dataPacket.url = DataHeader().url;
MainDatabaseHandler().insertData(dataPacket);
});
}
/// Or if it is a [DataPacket] instance.
} else if (packet[0] == 91) {
_sortedData.add(DataPacket(packet));
print('PACKET ${String.fromCharCodes(packet)}');
if (DataHeader().headerSet)
DatabaseHandler().insertData(_sortedData.last, DataHeader().deviceID, DataHeader().url);
DataPacket dataPacket = DataPacket(packet);
if (DataHeader().headerSet) {
dataPacket.deviceId = DataHeader().deviceId;
dataPacket.url = DataHeader().url;
MainDatabaseHandler().insertData(dataPacket);
} else
_sortedData.add(dataPacket);
}
}
/// TODO Used for debugging, later
void printLatest() {
print(_sortedData.last);
}
/// Get the entirety of the [DataPacket]s.
@deprecated
List<DataPacket> getData() => _sortedData;
/// Remove the entirety of the acquired data.
@deprecated
void clearData() {
_sortedData.removeWhere((element) => true);
}
/// Remove the first element of the [DataPacket]s. Used for surgical removal.
void pop() {
if (_sortedData.isNotEmpty)
_sortedData.removeAt(0);
@deprecated
void pop(int timestamp) {
if (_sortedData.isNotEmpty)
_sortedData.removeWhere((DataPacket d) => d.timestamp == timestamp);
}
/// Get the latest [DataPacket]
@deprecated
DataPacket getLatestData() => (_sortedData.length > 0) ? _sortedData.last : null;
/// Stream the latest confirmed [DataPacket] received by the device.
@deprecated
Stream<DataPacket> getDataStream() async* {
while(true) {
yield (_sortedData.length > 0) ? _sortedData.last : null;
await Future.delayed(Duration(seconds: 1));
}
}
Stream<DataPacket> getMockDataStream() async* {
while(true) {
yield new DataPacket.fromData(1597957282000, 5, 42, 420, 3.0, 32, 24, 10123, 32, null, 5, null, 9, "");
await Future.delayed(Duration(seconds: 1));
}
}
/// This destroys the elements it depends on to avoid duplication or wrong information
void dispose() {
......
import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:logair_application/logic/data_packet.dart';
import 'package:logair_application/utils/helpers/location_of_interest.dart';
import 'package:path/path.dart';
import 'package:rxdart/rxdart.dart';
import 'package:sqflite/sqflite.dart';
/// Handler that integrates a mysqli database into the application's workflow.
class LocationsOfInterestDatabaseHandler {
factory LocationsOfInterestDatabaseHandler() => _singleton;
LocationsOfInterestDatabaseHandler._internal();
static final LocationsOfInterestDatabaseHandler _singleton = new LocationsOfInterestDatabaseHandler._internal();
/// Reference to the internalized [Database] within this instance.
Future<Database> _loidb;
/// [Database] initialization string, to reduce repetition.
final String _createLOIDBString = '''CREATE TABLE loi(
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT, latitude REAL, longitude REAL
)''';
/// Retrieves the internal [Database] instance, and if not instantiated, instantiates it.
Future<Database> get _loiDatabase async {
if (_loidb == null)
this._loidb = openDatabase(
join((await getDatabasesPath()), 'logair_loi.db'),
version: 0,
onCreate: (db, version) => db.execute(this._createLOIDBString),
);
return this._loidb;
}
/// Purges the [Database] of all internal data.
Future<void> recreateDB() async {
final Database database = await _loiDatabase;
await database.rawQuery('DROP TABLE IF EXISTS loi');
await database.rawQuery(this._createLOIDBString);
this.onUpdate.value = 1;
}
/// Inserts data into the [Database].
Future<void> insertData({@required String name, @required double latitude, @required double longitude}) async {
final Database database = await this._loiDatabase;
Map<String, dynamic> insert = {
'name': name,
'latitude': latitude,
'longitude': longitude
};
await database.insert(
'loi',
insert,
conflictAlgorithm: ConflictAlgorithm.replace
);
this.onUpdate.value = 1;
}
/// Retrieves all of the [DataPacket]s inserted into the [Database].
Future<List<LocationOfInterest>> get data async {
final Database database = await this._loiDatabase;
final List<Map<String, dynamic>> data = await database.query('loi');
return List.generate(data.length, (i) => LocationOfInterest(
id: data[i]['id'],
name: data[i]['name'],
latitude: data[i]['latitude'],
longitude: data[i]['longitude']
));
}
/// Closes the [Database] instance.
Future<void> close() async => (await this._loiDatabase).close();
/// Async capable variable used to trigger updates.
BehaviorSubject<void> onUpdate = BehaviorSubject<void>();
LocationsOfInterestDatabaseHandler.dispose() {
onUpdate.close();
this.close();
}
}
\ No newline at end of file
......@@ -5,63 +5,52 @@ import 'package:sqflite/sqflite.dart';
import 'package:tuple/tuple.dart';
/// Handler that integrates a mysqli database into the application's workflow.
class DatabaseHandler {
factory DatabaseHandler() => _singleton;
class MainDatabaseHandler {
factory MainDatabaseHandler() => _singleton;
DatabaseHandler._internal();
MainDatabaseHandler._internal();
static final DatabaseHandler _singleton = new DatabaseHandler._internal();
static final MainDatabaseHandler _singleton = new MainDatabaseHandler._internal();
/// Reference to the internalized [Database] within this instance.
Future<Database> _db;
Future<Database> _maindb;
/// [Database] initialization string, to reduce repetition.
final String createString = '''CREATE TABLE data(
final String _createMainDBString = '''CREATE TABLE data(
id INTEGER PRIMARY KEY AUTOINCREMENT,
device_id TEXT,
url TEXT,
device_id TEXT, url TEXT,
timestamp_nix INTEGER,
latitude REAL,
longitude REAL,
altitude INTEGER,
speed REAL,
heading INTEGER,
temperature FLOAT,
relative_humidity FLOAT,
pressure FLOAT,
pm_1 FLOAT,
pm_2_5 FLOAT,
pm_4 FLOAT,
pm_10 FLOAT,
latitude REAL, longitude REAL,
altitude INTEGER, speed REAL, heading INTEGER,
temperature FLOAT, relative_humidity FLOAT, pressure FLOAT,
pm_1 FLOAT, pm_2_5 FLOAT, pm_4 FLOAT, pm_10 FLOAT,
extra TEXT,
exported TINYINT DEFAULT 0
)''';
/// Retrieves the internal [Database] instance, and if not instantiated, instantiates it.
Future<Database> get _database async {
if (_db == null)
this._db = openDatabase(
Future<Database> get _mainDatabase async {
if (_maindb == null)
this._maindb = openDatabase(
join((await getDatabasesPath()), 'logair_data.db'),
version: 2,
onCreate: (db, version) => db.execute(createString)
version: 3,
onCreate: (db, version) => db.execute(this._createMainDBString)
);
return this._db;