Commit 4464e765 authored by Nicolas Richard Walter Boeckh's avatar Nicolas Richard Walter Boeckh 💬

Documentation, overview restructuring and lightening

Added:
- Overview Widgets documentation
- `NetworkHandler` new features documentation.
- `Graph` documetation.

Modified:
- Generalized overview widget elements construction.

Fixed:
- Unknown references in docs
- `DataHandler` pop function removed regardless of whether data was pulled from DH or DB (TODO transition to full db mode).
- Moved battery level to icon function to utils because future use in `MainHeader` as well.
parent b83a909e
{"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-09 00:35:24.671242","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":"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-09 14:34:23.725205","version":"1.22.0-2.0.pre.18"}
\ 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:
......
......@@ -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;
......
......@@ -11,7 +11,7 @@ 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;
......@@ -39,16 +39,16 @@ class MapDisplayController {
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;
......@@ -106,7 +106,7 @@ class MapDisplayController {
DataPacket latest = DataHandler().getLatestData();
/// 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 +120,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 +137,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;
......
import 'package:collection/collection.dart';
import 'package:flutter/cupertino.dart';
/// @ToDeprecate Describes the contents of the data packets
/// @ReplaceBy Includes the device name and a config_file URL
......
......@@ -31,22 +31,23 @@ 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 => _timestamp;
String get deviceId => _deviceId ?? '';
double get latitude => _latitude;
double get longitude => _longitude;
int get altitude => _altitude;
int get heading => _heading;
double get speed => _speed;
double get temperature => _temperature;
double get pressure => _pressure;
double get relativeHumidity => _relativeHumidity;
double get pm1 => _pm1;
double get pm2_5 => _pm2_5;
double get pm4 => _pm4;
double get pm10 => _pm10;
DataPacket(this._data) {
_timestamp = DateTime.now().millisecondsSinceEpoch;
......
......@@ -3,7 +3,7 @@ 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;
......
......@@ -82,9 +82,9 @@ class DataHandler {
}
/// Remove the first element of the [DataPacket]s. Used for surgical removal.
void pop() {
if (_sortedData.isNotEmpty)
_sortedData.removeAt(0);
void pop(int timestamp) {
if (_sortedData.isNotEmpty)
_sortedData.removeWhere((DataPacket d) => d.timestamp == timestamp);
}
/// Get the latest [DataPacket]
......@@ -98,6 +98,7 @@ class DataHandler {
}
}
@deprecated
Stream<DataPacket> getMockDataStream() async* {
while(true) {
yield new DataPacket.fromData(1597957282000, 5, 42, 420, 3.0, 32, 24, 10123, 32, null, 5, null, 9, "");
......
......@@ -5,6 +5,7 @@ import 'package:sqflite/sqflite.dart';
import 'package:tuple/tuple.dart';
/// Handler that integrates a mysqli database into the application's workflow.
// TODO: Table for data locations
class DatabaseHandler {
factory DatabaseHandler() => _singleton;
......@@ -48,7 +49,7 @@ class DatabaseHandler {
return this._db;
}
/// Purges the [Database] of all internal.
/// Purges the [Database] of all internal data.
Future<void> recreateDB() async {
final Database database = await _database;
......
......@@ -19,9 +19,11 @@ import 'package:tuple/tuple.dart';
class NetworkHandler {
factory NetworkHandler() => _singleton;
/// [NetworkHandler] initializer that sets up listeners on the [PreferencesHandler].
NetworkHandler._internal() {
this._pushFrequency.listen((value) {
if (value != this._oldPushFrequency) {
// Resets the timer if the value has changed.
if (this._timer != null) {
this._timer.cancel();
}
......@@ -32,10 +34,13 @@ class NetworkHandler {
}
});
/// Define the interval between attempts to send the data to the server. (default: 1 minute).
/// Retrieve the interval between attempts to send the data to the server. (default: 1 minute).
PreferencesHandler().getPreferencesInt(PreferenceKeys.NET__MAX_ITEMS_PER_PUSH.key).then((value) => this._pushAmount.value = value);
/// Retrieve the interval between attempts to send the data to the server. (default: 1 minute).
PreferencesHandler().getPreferencesInt(PreferenceKeys.NET__TIME_TO_PUSH.key).then((value) => this._pushFrequency.value = value);
/// Checks the [PreferencesHandler] every 20 seconds on Preference updates.
new Timer.periodic(Duration(seconds: 20), (Timer t) async {
this._pushAmount.value = await PreferencesHandler().getPreferencesInt(PreferenceKeys.NET__MAX_ITEMS_PER_PUSH.key);
this._pushFrequency.value = await PreferencesHandler().getPreferencesInt(PreferenceKeys.NET__TIME_TO_PUSH.key);
......@@ -43,6 +48,7 @@ class NetworkHandler {
}
/// Kills the timer various timers and [BehaviorSubject]s.
@override
NetworkHandler.dispose() {
if (this._timer != null) {
......@@ -55,16 +61,28 @@ class NetworkHandler {
static final NetworkHandler _singleton = new NetworkHandler._internal();
/// The reference [DateTime] of the last time data was sent to LogAir's servers.
DateTime _lastServerTransmission;
int a = 0;
/// [BehaviorSubject] concerning the amount of data that should be pushed to the server per attempt.
BehaviorSubject<int> _pushAmount = BehaviorSubject<int>();
/// [BehaviorSubject] concerning the frequency of attempts to push to the server.
BehaviorSubject<int> _pushFrequency = BehaviorSubject<int>();
/// A reference to the old frequency in order to limit updates.
int _oldPushFrequency = -1;
/// The frequency of the updates.
Duration _duration;
Timer _timer;
/// The [Timer] that triggers the attempt to push to the server.
Timer _timer;
/// Proceeds to check whether an attempt to push to the server should be attempted.
///
/// If the user set [PreferenceKeys.NET__USE_MOBILE_NET] is set to true and 3G/4G/5G or Wi-Fi is available, then it will attempt to send data to the server.
/// Else it will only attempt when Wi-Fi is available.
Future<bool> checkNetworkAllowed() async {
ConnectivityResult connectivity = await Connectivity().checkConnectivity();
return !(connectivity == ConnectivityResult.none ||
......@@ -82,21 +100,23 @@ class NetworkHandler {
/// The default endpoint.
String url = 'https://api.logair.unige.ch/v1/service';
// Send data in a serialized manner (FIFO)
if (await DatabaseHandler().getUnsentPacketsLength().first > 0) {
DataPacket source = (await DatabaseHandler().getUnsent(_pushAmount.value ?? PreferenceKeys.NET__MAX_ITEMS_PER_PUSH.defaultValue))[0].item2;
// TODO set url / clean up
DataHeader().setHeaderFromExisting(deviceId: source.deviceId(), url: '');
DataHeader().setHeaderFromExisting(deviceId: source.deviceId, url: '');
}
/// Break conditions ([DataHeader] unset or no [DataPacket] available).
// Break conditions ([DataHeader] unset or no [DataPacket] available).
else if (!DataHeader().headerSet || DataHandler().getData().length == 0)
return;
/// Get the map representation of a [DataHeader].
/// The map representation of a [DataHeader].
Map<String, dynamic> headerData = DataHeader().jsonify();
/// The indexed [DataPacket]s to be sent (user set or default value @see [PreferenceKeys.NET__MAX_ITEMS_PER_PUSH]).
List<Tuple2<int, DataPacket>> databaseData = await DatabaseHandler().getUnsent(_pushAmount.value ?? PreferenceKeys.NET__MAX_ITEMS_PER_PUSH.defaultValue);
/// Get all the [DataPacket]s to be sent (maximum 100).
/// The [DataPacket]s to be sent.
List<DataPacket> packets = databaseData.map((x) => x.item2).toList();
/// Convert the [DataPacket] list to a [Map] of their [List] representations.
......@@ -104,7 +124,7 @@ class NetworkHandler {
'data' : LinkedHashSet<List<dynamic>>.from(packets.map((e) => e.jsonify()).toList()).toList()
};
/// Merge all of the [Map]s into 1.
/// Merge all of the [Map]s.
Map<String, dynamic> postData = new Map<String, dynamic>();
postData.addAll(headerData);
postData.addAll(packetList);
......@@ -117,7 +137,7 @@ class NetworkHandler {
headers: { "accept": "application/json", "content-type": "application/json" },
body: json.encode(postData)
);
/// On error, the error should be ignored and not break the thread.
/// On error, the error should be ignored and not hang/break the thread.
} catch (e) {
print("${e.toString()} COULDN'T CONNECT");
response = null;
......@@ -125,9 +145,10 @@ class NetworkHandler {
/// If the response is defined as an instance of [http.Response], check it's status code and remove all of the packets from the log.
if (response != null && response.statusCode == 200) {
for (int i = 0; i < packets.length; i++) {
DataHandler().pop();
}
/// If the transfer was successful ()
for (int i = 0; i < packets.length; i++)
DataHandler().pop(packets[i].timestamp);
await DatabaseHandler().setExported(databaseData.map((x) => x.item1).toList());
print('RESPONSE ${response.statusCode}');
this._lastServerTransmission = DateTime.now();
......
import 'package:geolocator/geolocator.dart';
import 'package:logair_application/utils/enums/preference_keys.dart';
import 'package:shared_preferences/shared_preferences.dart';
......@@ -104,13 +105,14 @@ class PreferencesHandler {
await setter(pref.key, pref.defaultValue);
}
/// This is a generic function to reset all preferences to their default values.
/// Resets all preferences to their default values.
Future<void> resetAllPreferences() async {
PreferenceKeys.list.where((PreferenceKeys k) => k != PreferenceKeys.GEN__SHARE).forEach(
(PreferenceKeys e) async => await resetPreference(e.key)
);
}
/// Retrieves the [LocationAccuracy] value from the [PreferencesHandler].
Stream<int> get currentDesiredGPSAccuracy async* {
while (true) {
yield await getPreferencesInt(PreferenceKeys.GPS__ACCURACY.key);
......
......@@ -15,28 +15,7 @@ class DataWidget extends StatelessWidget {
Widget build(BuildContext context) {
Widget widget = Center(
child: Column(
children: [
/*Container(
height: 45,
decoration: BoxDecoration(
color: Colors.blue[300],
borderRadius: BorderRadius.all(Radius.circular(5)),
),
child: Row(
children: [
Expanded(
child: Text(
'Current',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 22
),
),
),
],
),
),*/
children: [
// Local data (Chart + Data points)
Graph(),
DataTile(stream: DataHandler().getDataStream(), isDistant: false),
......@@ -61,6 +40,7 @@ class DataWidget extends StatelessWidget {
),
),
IconButton(
// Open other (Scaffold + Form) with nominatim etc + adds to DB.
onPressed: () => {},
icon: Icon(Icons.add),
)
......
......@@ -5,7 +5,7 @@ import 'package:logair_application/ui/components/body/data/graph/graph_colors.da
import 'package:logair_application/ui/components/body/data/graph/graph_controller.dart';
import 'package:logair_application/ui/components/body/data/graph/indicator.dart';
/// This graph displays the average quality of the air in 5 second increments.
/// This [Graph] displays the average quality of the air in 5 second increments.
class Graph extends StatefulWidget {
Graph({Key key}) : super(key: key);
......@@ -13,6 +13,7 @@ class Graph extends StatefulWidget {
State<Graph> createState() => _GraphState();
}
/// The [_GraphState] reacts to changes in time (additional data), and updates it's [List<FlSpot>] accordingly to draw the line chart.
class _GraphState extends State<Graph> {
/// The first registered timestamp.
double start;
......@@ -23,9 +24,16 @@ class _GraphState extends State<Graph> {
/// The maximum y-axis coordinate.
double yMax = 0;
/// The [FlSpot]s representing collected PM 1 values.
List<FlSpot> spotsPM1 = [FlSpot(0, 0)];
/// The [FlSpot]s representing collected PM 2.5 values.
List<FlSpot> spotsPM2_5 = [FlSpot(0, 0)];
/// The [FlSpot]s representing collected PM 4 values.
List<FlSpot> spotsPM4 = [FlSpot(0, 0)];
/// The [FlSpot]s representing collected PM 10 values.
List<FlSpot> spotsPM10 = [FlSpot(0, 0)];
_GraphState() {
......@@ -91,6 +99,7 @@ class _GraphState extends State<Graph> {
DateTime dtValue = DateTime.fromMillisecondsSinceEpoch(
((value * 1000) + (start ?? DateTime.now().millisecondsSinceEpoch)).round()
);
// If the timestamp is at a value that is a multiple of 20 seconds, draw it in the x-Axis axis titles.
if (dtValue.second % 20 == 0 && dtValue.millisecondsSinceEpoch < DateTime.now().millisecondsSinceEpoch)
return '${dtValue.hour.toString().padLeft(2, '0')}:${dtValue.minute.toString().padLeft(2, '0')}:${dtValue.second.toString().padLeft(2, '0')}';
return '';
......@@ -124,6 +133,7 @@ class _GraphState extends State<Graph> {
maxX: (now ?? 0) + 10,
maxY: (this.yMax ?? 0) + 10,
minY: 0,
// StreamBuilder ?
lineBarsData: [
_generateLine(spotsPM1, GraphColors.PM1.color),
_generateLine(spotsPM2_5, GraphColors.PM2_5.color),
......@@ -134,6 +144,7 @@ class _GraphState extends State<Graph> {
show: true,
leftTitle: AxisTitle(
showTitle: true,
// TODO Internationalize
titleText: 'PM density µg/m³',
margin: 0,
textStyle: TextStyle(
......@@ -143,6 +154,7 @@ class _GraphState extends State<Graph> {
),
bottomTitle: AxisTitle(
showTitle: true,
// TODO Internationalize
titleText: 'Time',
margin: 0,
textStyle: TextStyle(
......@@ -161,11 +173,11 @@ class _GraphState extends State<Graph> {
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
// Draw Indicators with the same color / label data as the graph.
children: GraphColors.list.map((GraphColors gc) =>
Indicator(
text: gc.label,
color: gc.color,
isSquare: true,
)
).toList()
),
......
......@@ -2,9 +2,11 @@ import 'package:flutter/material.dart';
/// Small label [Widget] that serves as Caption for the [Graph]
class Indicator extends StatelessWidget {
/// The [Color] of the [Indicator].
final Color color;
/// The label of the [Indicator].
final String text;
final bool isSquare;
final double size;
final Color textColor;
......@@ -12,7 +14,6 @@ class Indicator extends StatelessWidget {
Key key,
this.color,
this.text,
this.isSquare,
this.size = 16,
this.textColor = const Color(0xff505050),
}) : super(key: key);
......@@ -27,7 +28,7 @@ class Indicator extends StatelessWidget {
width: size,
height: size,
decoration: BoxDecoration(
shape: isSquare ? BoxShape.rectangle : BoxShape.circle,
shape: BoxShape.rectangle,
color: color,
borderRadius: BorderRadius.circular(3)
),
......
......@@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
import 'package:logair_application/logic/data_packet.dart';
import 'package:logair_application/utils/enums/pm_symbol.dart';
//TODO Add weather ?
//TODO Add weather ? (Not feasible unless negotiate free API access)
/// Widget that displays the data specific to a specific place, depending on the stream provided.
/// Data displayed is PM1, PM2.5, PM4, PM10, Temperature, Pressure, Relative Humidity.
///
......@@ -62,16 +62,17 @@ class _DataTileState extends State<DataTile> {
_DataTileState(this.stream, this.isDistant, this.locationName) {
stream.listen((DataPacket newPacket) {
// Checks that the packet contains actual data.
if (newPacket != null && this.mounted)
if (newPacket != null && this.mounted) {
setState(() {
_pm1 = newPacket.pm1();
_pm2_5 = newPacket.pm2_5();
_pm4 = newPacket.pm4();
_pm10 = newPacket.pm10();
_temperature = newPacket.temperature();
_pressure = newPacket.pressure();
_relativeHumidity = newPacket.relativeHumidity();
_pm1 = newPacket.pm1;
_pm2_5 = newPacket.pm2_5;
_pm4 = newPacket.pm4;
_pm10 = newPacket.pm10;
_temperature = newPacket.temperature;
_pressure = newPacket.pressure;
_relativeHumidity = newPacket.relativeHumidity;
});
}
});
}
......@@ -216,6 +217,7 @@ class _DataTileState extends State<DataTile> {
child: InkWell(
child: tile,
splashColor: Color.fromARGB(0xAA, 0xFF, 0x00, 0x00),
// Delete location function (Remove from db) TODO
onLongPress: () => print(this.locationName),
)
);
......
......@@ -27,6 +27,7 @@ class MapContainer extends BaseWidget {
PolylineLayerOptions(
polylines: MapDisplayController().polylines,
),
// TODO: Make circle Behavior subject dependent.
CircleLayerOptions(
circles: MapDisplayController().pmMarkers,
),
......
......@@ -3,9 +3,15 @@ import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
/// Neumorphic-esque button that brings the user to another [Page] when clicked.
class OverviewNavigationButton extends StatelessWidget {
/// The leading [Icon]
final IconData icon;
/// The title of the button
final String title;
/// What should happen when the button is pressed.
final Function action;
OverviewNavigationButton({@required this.icon, @required this.title, @required this.action, Key key}) : super(key: key);
......
......@@ -14,6 +14,7 @@ import 'package:logair_application/ui/components/common/base_widget.dart';
class OverviewWidget extends StatelessWidget {
OverviewWidget({Key key}) : super(key: key);
/// Builds a [Container] (@see [child]) that holds various status indicators.
Container _buildOverviewCell({@required Widget child}) => Container(
width: double.infinity,
height: double.infinity,
......
......@@ -2,13 +2,48 @@ import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_blue/flutter_blue.dart';
import 'package:logair_application/logic/data_header.dart';
import 'package:logair_application/ui/components/body/overview/overview_widgets/decorations.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
import 'package:logair_application/ui/components/body/overview/overview_widgets/common/overview_data_cell.dart';
import 'package:logair_application/ui/components/body/overview/overview_widgets/common/decorations.dart';