diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 8cbe2e9..a2874b0 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -4,11 +4,13 @@ + + - - @@ -44,7 +37,5 @@ - - diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 296b146..da44bc1 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip diff --git a/assets/offline_layers/oaci_vfr/.gitkeep b/assets/offline_layers/oaci_vfr/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/bin/download_oaci_vfr.dart b/bin/download_oaci_vfr.dart index cd23575..a86bc9a 100644 --- a/bin/download_oaci_vfr.dart +++ b/bin/download_oaci_vfr.dart @@ -27,7 +27,7 @@ Future downloadTile(http.Client client, int x, int y, int zoom) async { String url = 'https://wxs.ign.fr/an7nvfzojv5wa96dsga5nk8w/geoportail/wmts?layer=GEOGRAPHICALGRIDSYSTEMS.MAPS.SCAN-OACI&style=normal&tilematrixset=PM&Service=WMTS&Request=GetTile&Version=1.0.0&Format=image/jpeg&TileMatrix=$zoom&TileCol=$y&TileRow=$x'; - var response = await client.get(url); + var response = await client.get(Uri.parse(url)); if (response.statusCode == 200) { print('Downloaded $zoom/$x-$y.'); diff --git a/lib/flight_recorder.dart b/lib/flight_recorder.dart index f11994e..b901577 100644 --- a/lib/flight_recorder.dart +++ b/lib/flight_recorder.dart @@ -1,15 +1,14 @@ import 'dart:io'; -import 'package:latlong/latlong.dart'; +import 'package:latlong2/latlong.dart'; import 'package:vfr_light/instruments_data_source.dart'; class FlightRecorder { IOSink _outputSink; - LatLng _lastPosition; + LatLng? _lastPosition; - FlightRecorder(File outputFile) { - _outputSink = outputFile.openWrite(); + FlightRecorder(File outputFile) : _outputSink = outputFile.openWrite() { _outputSink.writeln( 'longitude,latitude,altitudeInM,heightInM,speedInKmH,datetime'); } @@ -18,7 +17,7 @@ class FlightRecorder { if (_lastPosition == null || _lastPosition != data.position) { _lastPosition = data.position; _outputSink.writeln( - '${data.position.longitude},${data.position.latitude},${data.altitudeInM.round()},${data.heightInM.round()},${data.speedInKmH.round()},${data.time.toIso8601String()}'); + '${data.position.longitude},${data.position.latitude},${data.altitudeInM?.round() ?? ''},${data.heightInM?.round() ?? ''},${data.speedInKmH.round()},${data.time.toIso8601String()}'); } } diff --git a/lib/instruments.dart b/lib/instruments.dart index 969945d..58b5029 100644 --- a/lib/instruments.dart +++ b/lib/instruments.dart @@ -9,7 +9,7 @@ class Instruments extends StatelessWidget { final InstrumentsData data; - const Instruments({Key key, @required this.data}) : super(key: key); + const Instruments({Key? key, required this.data}) : super(key: key); @override Widget build(BuildContext context) { @@ -25,17 +25,14 @@ class Instruments extends StatelessWidget { style: Theme.of(context).textTheme.subtitle2), Text( data.heightInM != null - ? StandardAtmosphere.feetFromMeters(data.heightInM) + ? StandardAtmosphere.feetFromMeters(data.heightInM!) .round() .toString() : '???', style: commonStyle), Text('Height (m)', style: Theme.of(context).textTheme.subtitle2), - Text( - data.heightInM != null - ? data.heightInM.round().toString() - : '???', + Text(data.heightInM?.round().toString() ?? '???', style: commonStyle), ], ), @@ -46,17 +43,15 @@ class Instruments extends StatelessWidget { style: Theme.of(context).textTheme.subtitle2), Text( data.altitudeInM != null - ? StandardAtmosphere.feetFromMeters(data.altitudeInM) + ? StandardAtmosphere.feetFromMeters(data.altitudeInM!) .round() .toString() : '???', style: commonStyle), Text('Altitude (m)', style: Theme.of(context).textTheme.subtitle2), - Text( - data.altitudeInM != null - ? data.altitudeInM.round().toString() - : '???', + Text(data.altitudeInM?.round().toString() ?? '???', + // FIXME only one ?.? style: commonStyle), ], ), @@ -65,18 +60,10 @@ class Instruments extends StatelessWidget { children: [ Text('Heading (°)', style: Theme.of(context).textTheme.subtitle2), - Text( - data.headingInDeg != null - ? data.headingInDeg.round().toString() - : '???', - style: commonStyle), + Text(data.headingInDeg.round().toString(), style: commonStyle), Text('Speed (km/h)', style: Theme.of(context).textTheme.subtitle2), - Text( - data.speedInKmH != null - ? data.speedInKmH.round().toString() - : '???', - style: commonStyle), + Text(data.speedInKmH.round().toString(), style: commonStyle), ], ), ], diff --git a/lib/instruments_data_source.dart b/lib/instruments_data_source.dart index 30dfc6f..db3825f 100644 --- a/lib/instruments_data_source.dart +++ b/lib/instruments_data_source.dart @@ -1,26 +1,25 @@ import 'dart:async'; -import 'package:enviro_sensors/enviro_sensors.dart'; +import 'package:environment_sensors/environment_sensors.dart'; import 'package:geolocator/geolocator.dart'; -import 'package:meta/meta.dart'; import 'standard_atmosphere.dart'; -import 'package:latlong/latlong.dart'; +import 'package:latlong2/latlong.dart'; class InstrumentsData { final LatLng position; - final double speedInKmH; - final double altitudeInM; - final double heightInM; final double headingInDeg; final DateTime time; + final double speedInKmH; + final double? altitudeInM; + final double? heightInM; InstrumentsData( - {@required this.position, - @required this.speedInKmH, - @required this.altitudeInM, - @required this.heightInM, - @required this.headingInDeg, - @required this.time}); + {required this.position, + required this.headingInDeg, + required this.time, + required this.speedInKmH, + required this.altitudeInM, + required this.heightInM}); } class InstrumentsDataSource { @@ -31,23 +30,25 @@ class InstrumentsDataSource { final StreamController _instrumentsDataStream = StreamController.broadcast(); - StreamSubscription _pressureSubscription; - StreamSubscription _positionSubscription; + late StreamSubscription _pressureSubscription; + late StreamSubscription _positionSubscription; - LatLng _lastPosition; - double _lastSpeedInKmH; - double _lastAltitudeInM; - double _lastHeightInM; - double _lastHeadingInDeg; + LatLng? _lastPosition; + double? _lastSpeedInKmH; + double? _lastAltitudeInM; + double? _lastHeightInM; + double? _lastHeadingInDeg; + double? _lastPressure; - InstrumentsDataSource( - {@required this.qnh, @required this.surfaceAltitudeInM}) { + InstrumentsDataSource({required this.qnh, required this.surfaceAltitudeInM}) { _positionSubscription = - getPositionStream(desiredAccuracy: LocationAccuracy.high) + Geolocator.getPositionStream(desiredAccuracy: LocationAccuracy.high) .listen(_onNewPosition); - _pressureSubscription = - barometerEvents.asBroadcastStream().listen(_onNewPressure); + _pressureSubscription = EnvironmentSensors() + .pressure + .asBroadcastStream() + .listen(_onNewPressure); } void dispose() { @@ -55,42 +56,48 @@ class InstrumentsDataSource { _positionSubscription.cancel(); } - Future recalibrateAtSfc({@required double surfaceAltitudeInM}) async { + Future recalibrateAtSfc({required double surfaceAltitudeInM}) async { this.surfaceAltitudeInM = surfaceAltitudeInM; - qnh = StandardAtmosphere.qnh( - pressure: (await barometerEvents.asBroadcastStream().first).reading, - altitude: surfaceAltitudeInM); + if (_lastPressure != null) { + qnh = StandardAtmosphere.qnh( + pressure: _lastPressure!, altitude: surfaceAltitudeInM); + } } Stream get data => _instrumentsDataStream.stream; - LatLng get lastPosition => _lastPosition; + LatLng? get lastPosition => _lastPosition; void _sendData() { _instrumentsDataStream.add(InstrumentsData( - position: _lastPosition, - speedInKmH: _lastSpeedInKmH, + position: _lastPosition!, + speedInKmH: _lastSpeedInKmH!, + headingInDeg: _lastHeadingInDeg!, + time: DateTime.now(), altitudeInM: _lastAltitudeInM, - heightInM: _lastHeightInM, - headingInDeg: _lastHeadingInDeg, - time: DateTime.now())); + heightInM: _lastHeightInM)); } void _onNewPosition(Position position) { - this._lastPosition = LatLng(position.latitude, position.longitude); + _lastPosition = LatLng(position.latitude, position.longitude); _lastSpeedInKmH = position.speed * 3.6; _lastHeadingInDeg = position.heading; _sendData(); } - void _onNewPressure(BarometerEvent event) { + void _onNewPressure(double pressure) { _lastAltitudeInM = - StandardAtmosphere.altitude(qnh: qnh, pressure: event.reading); + StandardAtmosphere.altitude(qnh: qnh, pressure: pressure); - _lastHeightInM = _lastAltitudeInM - surfaceAltitudeInM; + _lastHeightInM = _lastAltitudeInM! - surfaceAltitudeInM; + _lastPressure = pressure; - _sendData(); + if (_lastPosition != null && + _lastSpeedInKmH != null && + _lastHeadingInDeg != null) { + _sendData(); + } } } diff --git a/lib/map/flight_map.dart b/lib/map/flight_map.dart index b79a6df..2e8ee6d 100644 --- a/lib/map/flight_map.dart +++ b/lib/map/flight_map.dart @@ -1,11 +1,10 @@ import 'dart:async'; -import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter_map/flutter_map.dart'; import 'package:vfr_light/instruments_data_source.dart'; -import 'package:latlong/latlong.dart'; +import 'package:latlong2/latlong.dart'; import '../preset_layers.dart'; class FlightMap extends StatefulWidget { @@ -14,10 +13,10 @@ class FlightMap extends StatefulWidget { final LongPressCallback onLongPress; const FlightMap( - {Key key, - @required this.dataStream, - @required this.onLongPress, - this.route}) + {Key? key, + required this.dataStream, + required this.onLongPress, + required this.route}) : super(key: key); @override @@ -27,10 +26,10 @@ class FlightMap extends StatefulWidget { class _FlightMapState extends State { bool _centerOnPosition = false; - Marker _planeMarker; + Marker? _planeMarker; MapController _mapController = MapController(); - StreamSubscription _dataSubscription; - Polyline _headingPolyline; + late StreamSubscription _dataSubscription; + Polyline? _headingPolyline; @override void initState() { @@ -41,7 +40,7 @@ class _FlightMapState extends State { @override void dispose() { - _dataSubscription?.cancel(); + _dataSubscription.cancel(); super.dispose(); } @@ -57,13 +56,16 @@ class _FlightMapState extends State { zoom: 10.0, swPanBoundary: LatLng(41.327326, -5.734863), nePanBoundary: LatLng(51.138001, 9.382324), - onLongPress: widget.onLongPress), + onLongPress: widget.onLongPress, + interactiveFlags: InteractiveFlag.drag | + InteractiveFlag.pinchZoom | + InteractiveFlag.pinchMove), layers: [ TileLayerOptions( urlTemplate: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", subdomains: ['a', 'b', 'c']), - // PresetLayers.oaciVfrFrance, + // PresetLayers.oaciVfrFrance, TileLayerOptions( opacity: 0.5, tileProvider: AssetTileProvider(), @@ -71,11 +73,11 @@ class _FlightMapState extends State { minNativeZoom: 8, maxNativeZoom: 11), PolylineLayerOptions(polylines: [ - if (_headingPolyline != null) _headingPolyline, - if (widget.route != null) widget.route + if (_headingPolyline != null) _headingPolyline!, + widget.route ]), MarkerLayerOptions(markers: [ - if (_planeMarker != null) _planeMarker, + if (_planeMarker != null) _planeMarker!, ...widget.route.points .asMap() .entries @@ -126,19 +128,17 @@ class _FlightMapState extends State { _mapController.move(data.position, _mapController.zoom); } - if (data.headingInDeg != null) { - _headingPolyline = _headingLine(data.position, data.headingInDeg); + _headingPolyline = _headingLine(data.position, data.headingInDeg); - _planeMarker = Marker( - point: data.position, - builder: (context) { - return Transform.rotate( - angle: data.headingInDeg * 2 * pi / 360, - child: Icon(Icons.airplanemode_active, - color: Colors.purpleAccent)); - }); + _planeMarker = Marker( + point: data.position, + builder: (context) { + return Transform.rotate( + angle: data.headingInDeg * 2 * pi / 360, + child: + Icon(Icons.airplanemode_active, color: Colors.purpleAccent)); + }); - setState(() {}); - } + setState(() {}); } } diff --git a/lib/map/map_screen.dart b/lib/map/map_screen.dart index 9b0a321..d971737 100644 --- a/lib/map/map_screen.dart +++ b/lib/map/map_screen.dart @@ -2,9 +2,10 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_background/flutter_background.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; -import 'package:latlong/latlong.dart'; +import 'package:latlong2/latlong.dart'; import 'package:wakelock/wakelock.dart'; import '../record_button.dart'; @@ -23,12 +24,10 @@ class MapScreen extends StatefulWidget { } class _MapScreenState extends State { - InstrumentsDataSource _instrumentsDataSource; - RouteManager _routeManager; - - NavigationDataSource _navigationDataSource; - - StreamSubscription _destinationStreamSubscription; + late InstrumentsDataSource _instrumentsDataSource; + late RouteManager _routeManager; + NavigationDataSource? _navigationDataSource; + late StreamSubscription _destinationStreamSubscription; @override void initState() { @@ -49,6 +48,14 @@ class _MapScreenState extends State { destination: event, stream: _instrumentsDataSource.data); setState(() {}); }); + + final androidConfig = FlutterBackgroundAndroidConfig( + notificationTitle: "VFR Light location service", + notificationText: + "Enables recording of GPS data while app is in the background.", + ); + Future success = + FlutterBackground.initialize(androidConfig: androidConfig); } @override @@ -113,7 +120,7 @@ class _MapScreenState extends State { }, ), SizedBox(height: 8), - RaisedButton( + ElevatedButton( child: Text('Submit'), onPressed: () { print(double.parse(val)); @@ -163,13 +170,12 @@ class _MapScreenState extends State { ), if (_navigationDataSource != null) StreamBuilder( - stream: _navigationDataSource.data, + stream: _navigationDataSource!.data, builder: (BuildContext context, AsyncSnapshot snapshot) { switch (snapshot.connectionState) { case ConnectionState.active: return NavigationInfo(data: snapshot.data); - break; case ConnectionState.none: case ConnectionState.waiting: @@ -181,7 +187,6 @@ class _MapScreenState extends State { style: Theme.of(context).textTheme.headline6, ), ); - break; } }), Expanded( @@ -196,7 +201,6 @@ class _MapScreenState extends State { switch (snapshot.connectionState) { case ConnectionState.active: return Instruments(data: snapshot.data); - break; case ConnectionState.none: case ConnectionState.waiting: @@ -208,7 +212,6 @@ class _MapScreenState extends State { style: Theme.of(context).textTheme.headline6, ), ); - break; } }), ], diff --git a/lib/navigation_data_source.dart b/lib/navigation_data_source.dart index b245d5b..6e2b696 100644 --- a/lib/navigation_data_source.dart +++ b/lib/navigation_data_source.dart @@ -1,30 +1,27 @@ import 'dart:async'; -import 'package:meta/meta.dart'; import 'instruments_data_source.dart'; -import 'package:latlong/latlong.dart'; +import 'package:latlong2/latlong.dart'; class NavigationData { - final double distanceInM; - final Duration duration; - final DateTime eta; + final double? distanceInM; + final Duration? duration; + final DateTime? eta; NavigationData( - {@required this.distanceInM, - @required this.duration, - @required this.eta}); + {required this.distanceInM, required this.duration, required this.eta}); } class NavigationDataSource { final StreamController _navigationDataStream = StreamController.broadcast(); - StreamSubscription _subscription; + late StreamSubscription _subscription; LatLng destination; NavigationDataSource( - {@required this.destination, @required Stream stream}) { + {required this.destination, required Stream stream}) { _subscription = stream.listen(_onNewData); } @@ -35,20 +32,18 @@ class NavigationDataSource { Stream get data => _navigationDataStream.stream; void _onNewData(InstrumentsData data) { - double distanceInM; - Duration duration; - DateTime eta; + double? distanceInM; + Duration? duration; + DateTime? eta; - if (data.position != null) { - Distance d = Distance(); - distanceInM = d.distance(data.position, destination); + Distance d = Distance(); + distanceInM = d.distance(data.position, destination).toDouble(); - if (data.speedInKmH != null && data.speedInKmH > 5) { - duration = Duration( - minutes: (distanceInM / 1000 / data.speedInKmH * 60).round()); + if (data.speedInKmH > 5) { + duration = Duration( + minutes: (distanceInM / 1000 / data.speedInKmH * 60).round()); - eta = DateTime.now().add(duration); - } + eta = DateTime.now().add(duration); } _navigationDataStream.add( diff --git a/lib/navigation_info.dart b/lib/navigation_info.dart index 0677bb5..2d17786 100644 --- a/lib/navigation_info.dart +++ b/lib/navigation_info.dart @@ -8,7 +8,7 @@ class NavigationInfo extends StatelessWidget { final NavigationData data; - const NavigationInfo({Key key, @required this.data}) : super(key: key); + const NavigationInfo({Key? key, required this.data}) : super(key: key); @override Widget build(BuildContext context) { @@ -24,7 +24,7 @@ class NavigationInfo extends StatelessWidget { style: Theme.of(context).textTheme.subtitle2), Text( data.distanceInM != null - ? (data.distanceInM / 1000).toStringAsFixed(1) + ? (data.distanceInM! / 1000).toStringAsFixed(1) : '???', style: commonStyle), ], @@ -35,7 +35,7 @@ class NavigationInfo extends StatelessWidget { Text('Duration', style: Theme.of(context).textTheme.subtitle2), Text( data.duration != null - ? '${data.duration.inHours}h${data.duration.inMinutes % 60}' + ? '${data.duration!.inHours}h${data.duration!.inMinutes % 60}' : '???', style: commonStyle), ], @@ -46,7 +46,7 @@ class NavigationInfo extends StatelessWidget { Text('ETA', style: Theme.of(context).textTheme.subtitle2), Text( data.eta != null - ? '${data.eta.hour}:${data.eta.minute}' + ? '${data.eta!.hour}:${data.eta!.minute}' : '???', style: commonStyle), ], diff --git a/lib/preset_layers.dart b/lib/preset_layers.dart index 49b2db7..2c4dc37 100644 --- a/lib/preset_layers.dart +++ b/lib/preset_layers.dart @@ -5,28 +5,27 @@ import 'package:path_provider/path_provider.dart'; import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:path/path.dart' as p; -class LongTermCacheManager extends BaseCacheManager { - static const key = 'libCachedImageDataLongTerm'; - - static LongTermCacheManager _instance; - - factory LongTermCacheManager() { - _instance ??= LongTermCacheManager._(); - return _instance; - } - - LongTermCacheManager._() : super(key); - - @override - Future getFilePath() async { - var directory = (await getExternalCacheDirectories())[0]; - return p.join(directory.path, key); - } -} +// +// class LongTermCacheManager extends BaseCacheManager { +// static const key = 'libCachedImageDataLongTerm'; +// +// static LongTermCacheManager _instance; +// +// factory LongTermCacheManager() { +// _instance ??= LongTermCacheManager._(); +// return _instance; +// } +// +// LongTermCacheManager._() : super(key, maxNrOfCacheObjects: 10000); +// +// @override +// Future getFilePath() async { +// var directory = (await getExternalCacheDirectories())[0]; +// return p.join(directory.path, key); +// } +// } class PresetLayers { - - static TileLayerOptions oaciVfrFrance = TileLayerOptions( opacity: 0.5, urlTemplate: diff --git a/lib/record_button.dart b/lib/record_button.dart index 0841fe9..ec60f95 100644 --- a/lib/record_button.dart +++ b/lib/record_button.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'dart:io'; import 'package:flutter/material.dart'; -import 'package:flutter_foreground_plugin/flutter_foreground_plugin.dart'; +import 'package:flutter_background/flutter_background.dart'; import 'package:path_provider/path_provider.dart'; import 'flight_recorder.dart'; @@ -11,7 +11,7 @@ import 'instruments_data_source.dart'; class RecordButton extends StatefulWidget { final InstrumentsDataSource dataSource; - const RecordButton({Key key, @required this.dataSource}) : super(key: key); + const RecordButton({Key? key, required this.dataSource}) : super(key: key); @override _RecordButtonState createState() => _RecordButtonState(); @@ -19,8 +19,8 @@ class RecordButton extends StatefulWidget { class _RecordButtonState extends State { bool _isRecording = false; - StreamSubscription _recorderSubscription; - FlightRecorder _flightRecorder; + StreamSubscription? _recorderSubscription; + FlightRecorder? _flightRecorder; @override void dispose() { @@ -50,42 +50,41 @@ class _RecordButtonState extends State { setState(() { _isRecording = false; }); - _recorderSubscription.cancel(); + _recorderSubscription?.cancel(); _recorderSubscription = null; - _flightRecorder.dispose(); + _flightRecorder?.dispose(); _flightRecorder = null; - FlutterForegroundPlugin.stopForegroundService(); + FlutterBackground.disableBackgroundExecution(); } else { getExternalStorageDirectory().then((value) { - DateTime now = DateTime.now(); - String path = - '${value.path}/${now.year}-${now.month}-${now.day}_${now.hour}-${now.minute}.csv'; - - File outputFile = File(path); - print('Saving trace to $path'); - - if (!outputFile.existsSync()) { - _flightRecorder = FlightRecorder(outputFile); - - setState(() { - _isRecording = true; - }); - - _recorderSubscription = widget.dataSource.data.listen((event) { - _flightRecorder.appendData(event); - }); - } else { - print('File already exists!'); + if (value != null) { + DateTime now = DateTime.now(); + String path = + '${value.path}/${now.year}-${now.month}-${now.day}_${now.hour}-${now.minute}.csv'; + + File outputFile = File(path); + print('Saving trace to $path'); + + if (!outputFile.existsSync()) { + _flightRecorder = FlightRecorder(outputFile); + + setState(() { + _isRecording = true; + }); + + _recorderSubscription = widget.dataSource.data.listen((event) { + _flightRecorder?.appendData(event); + }); + } else { + print('File already exists!'); + } + + // Start a foreground service to keep receiving location updates when + // the app is in the background. + // Turns out I don't need any special background location library :-) + FlutterBackground.enableBackgroundExecution(); } - - // Start a foreground service to keep receiving location updates when - // the app is in the background. - // Turns out I don't need any special background location library :-) - FlutterForegroundPlugin.startForegroundService( - title: "No Fuss PPG location service", - iconName: "ic_launcher", - ); }); } } diff --git a/lib/route_editor.dart b/lib/route_editor.dart index 633fd12..10af99a 100644 --- a/lib/route_editor.dart +++ b/lib/route_editor.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; -import 'package:latlong/latlong.dart'; +import 'package:latlong2/latlong.dart'; class RouteEditor extends StatefulWidget { final List waypoints; - const RouteEditor({Key key, this.waypoints}) : super(key: key); + const RouteEditor({Key? key, required this.waypoints}) : super(key: key); @override _RouteEditorState createState() => _RouteEditorState(); @@ -30,12 +30,12 @@ class _RouteEditorState extends State { double _tripLengthInM() { double length = 0; - LatLng from; + LatLng? from; Distance d = Distance(); widget.waypoints.forEach((waypoint) { if (from != null) { - length += d.distance(from, waypoint); + length += d.distance(from!, waypoint); } from = waypoint; @@ -46,12 +46,12 @@ class _RouteEditorState extends State { List _legDistancesInM() { List distances = [0]; - LatLng from; + LatLng? from; Distance d = Distance(); widget.waypoints.forEach((waypoint) { if (from != null) { - distances.add(d.distance(from, waypoint)); + distances.add(d.distance(from!, waypoint).toDouble()); } from = waypoint; diff --git a/lib/route_manager.dart b/lib/route_manager.dart index 1fb4412..b1c0713 100644 --- a/lib/route_manager.dart +++ b/lib/route_manager.dart @@ -1,19 +1,18 @@ import 'dart:async'; -import 'package:meta/meta.dart'; import 'package:flutter_map/flutter_map.dart'; -import 'package:latlong/latlong.dart'; +import 'package:latlong2/latlong.dart'; import 'package:vfr_light/instruments_data_source.dart'; class RouteManager { List waypoints = []; StreamController _destinationStream = StreamController.broadcast(); final InstrumentsDataSource instrumentsDataSource; - StreamSubscription _instrumentsDataSubscription; + late StreamSubscription _instrumentsDataSubscription; int _destinationIndex = 0; bool _navigationStarted = false; - RouteManager({@required this.instrumentsDataSource}) { + RouteManager({required this.instrumentsDataSource}) { _instrumentsDataSubscription = instrumentsDataSource.data.listen(_onInstrumentsData); } @@ -28,7 +27,7 @@ class RouteManager { Polyline routeRepresentation() { if (instrumentsDataSource.lastPosition != null) { return Polyline( - points: [instrumentsDataSource.lastPosition] + waypoints, + points: [instrumentsDataSource.lastPosition!] + waypoints, strokeWidth: 3); } else { return Polyline(points: waypoints, strokeWidth: 3); @@ -50,10 +49,10 @@ class RouteManager { } void _onInstrumentsData(InstrumentsData data) { - if (_navigationStarted && waypoints.isNotEmpty && data.position != null) { + if (_navigationStarted && waypoints.isNotEmpty) { Distance d = Distance(); double distanceInM = - d.distance(data.position, waypoints[_destinationIndex]); + d.distance(data.position, waypoints[_destinationIndex]).toDouble(); if (distanceInM < 20 && waypoints.length > _destinationIndex + 1) { _destinationStream.add(waypoints[++_destinationIndex]); diff --git a/lib/standard_atmosphere.dart b/lib/standard_atmosphere.dart index adddb2f..c090a0e 100644 --- a/lib/standard_atmosphere.dart +++ b/lib/standard_atmosphere.dart @@ -1,16 +1,16 @@ import 'dart:math'; -import 'package:meta/meta.dart'; - // https://fr.wikipedia.org/wiki/Atmosph%C3%A8re_normalis%C3%A9e +// Pressures in millibar/hPA + class StandardAtmosphere { - static double altitude({@required double pressure, @required double qnh}) { - return (1 - pow((pressure / qnh), 1 / 5.255)) * 288.15 / 0.0065; + static double altitude({required double pressure, required double qnh}) { + return (1 - pow(pressure / qnh, 1 / 5.255)) * 288.15 / 0.0065; } - static double qnh({@required double pressure, @required double altitude}) { - return pressure / pow((1 - 0.0065 * altitude / 288.15), 5.255); + static double qnh({required double pressure, required double altitude}) { + return pressure / pow(1 - 0.0065 * altitude / 288.15, 5.255); } static double feetFromMeters(double meters) { diff --git a/lib/user_agent_tile_provider.dart b/lib/user_agent_tile_provider.dart index a839a53..6ab9c9f 100644 --- a/lib/user_agent_tile_provider.dart +++ b/lib/user_agent_tile_provider.dart @@ -3,16 +3,13 @@ import 'package:flutter_map/flutter_map.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:vfr_light/preset_layers.dart'; -export 'package:flutter_map/src/layer/tile_provider/mbtiles_image_provider.dart'; - class UserAgentTileProvider extends TileProvider { @override ImageProvider getImage(Coords coords, TileLayerOptions options) { - return CachedNetworkImageProvider(getTileUrl(coords, options), - headers: { - 'User-Agent': - 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0' - }, - cacheManager: LongTermCacheManager()); + return CachedNetworkImageProvider(getTileUrl(coords, options), headers: { + 'User-Agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0' + }); + // cacheManager: LongTermCacheManager() } } diff --git a/pubspec.lock b/pubspec.lock index a4ca0e5..bfc1e6c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,539 +1,761 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: - ansicolor: + _fe_analyzer_shared: dependency: transitive description: - name: ansicolor + name: _fe_analyzer_shared url: "https://pub.dartlang.org" source: hosted - version: "1.0.5" + version: "24.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" archive: dependency: transitive description: name: archive url: "https://pub.dartlang.org" source: hosted - version: "2.0.13" + version: "3.1.2" args: dependency: transitive description: name: args url: "https://pub.dartlang.org" source: hosted - version: "1.6.0" + version: "2.2.0" async: dependency: transitive description: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.5.0-nullsafety.1" + version: "2.8.2" boolean_selector: dependency: transitive description: name: boolean_selector url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.1" + version: "2.1.0" cached_network_image: dependency: "direct main" description: name: cached_network_image url: "https://pub.dartlang.org" source: hosted - version: "2.3.2+1" + version: "3.1.0" + cached_network_image_platform_interface: + dependency: transitive + description: + name: cached_network_image_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + cached_network_image_web: + dependency: transitive + description: + name: cached_network_image_web + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" characters: dependency: transitive description: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.3" + version: "1.1.0" charcode: dependency: transitive description: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety.1" + version: "1.3.1" + cli_util: + dependency: transitive + description: + name: cli_util + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.3" clock: dependency: transitive description: name: clock url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.1" + version: "1.1.0" collection: dependency: transitive description: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.15.0-nullsafety.3" - console_log_handler: + version: "1.15.0" + convert: dependency: transitive description: - name: console_log_handler + name: convert url: "https://pub.dartlang.org" source: hosted - version: "1.1.6" - convert: + version: "3.0.1" + coverage: dependency: transitive description: - name: convert + name: coverage url: "https://pub.dartlang.org" source: hosted - version: "2.1.1" + version: "1.0.3" crypto: dependency: transitive description: name: crypto url: "https://pub.dartlang.org" source: hosted - version: "2.1.5" - enviro_sensors: + version: "3.0.1" + environment_sensors: dependency: "direct main" description: - name: enviro_sensors + name: environment_sensors url: "https://pub.dartlang.org" source: hosted - version: "1.0.1" + version: "0.2.0" fake_async: dependency: transitive description: name: fake_async url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety.1" + version: "1.2.0" ffi: dependency: transitive description: name: ffi url: "https://pub.dartlang.org" source: hosted - version: "0.1.3" + version: "1.1.2" file: dependency: transitive description: name: file url: "https://pub.dartlang.org" source: hosted - version: "5.2.1" + version: "6.1.2" flutter: dependency: "direct main" description: flutter source: sdk version: "0.0.0" + flutter_background: + dependency: "direct main" + description: + name: flutter_background + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2+1" flutter_blurhash: dependency: transitive description: name: flutter_blurhash url: "https://pub.dartlang.org" source: hosted - version: "0.5.0" + version: "0.6.0" flutter_cache_manager: dependency: transitive description: name: flutter_cache_manager url: "https://pub.dartlang.org" source: hosted - version: "1.4.2" - flutter_foreground_plugin: - dependency: "direct main" - description: - name: flutter_foreground_plugin - url: "https://pub.dartlang.org" - source: hosted - version: "0.5.0" - flutter_image: - dependency: transitive - description: - name: flutter_image - url: "https://pub.dartlang.org" - source: hosted - version: "3.0.0" + version: "3.1.2" flutter_launcher_icons: dependency: "direct dev" description: name: flutter_launcher_icons url: "https://pub.dartlang.org" source: hosted - version: "0.8.0" + version: "0.9.2" flutter_map: dependency: "direct main" description: name: flutter_map url: "https://pub.dartlang.org" source: hosted - version: "0.10.1+1" + version: "0.13.1" flutter_test: dependency: "direct dev" description: flutter source: sdk version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" font_awesome_flutter: dependency: "direct main" description: name: font_awesome_flutter url: "https://pub.dartlang.org" source: hosted - version: "8.8.1" + version: "9.1.0" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.2" geolocator: dependency: "direct main" description: name: geolocator url: "https://pub.dartlang.org" source: hosted - version: "6.0.0+4" + version: "7.4.0" + geolocator_android: + dependency: transitive + description: + name: geolocator_android + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + geolocator_apple: + dependency: transitive + description: + name: geolocator_apple + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" geolocator_platform_interface: dependency: transitive description: name: geolocator_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "1.0.4" + version: "2.3.2" + geolocator_web: + dependency: transitive + description: + name: geolocator_web + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.6" + glob: + dependency: transitive + description: + name: glob + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" http: dependency: "direct main" description: name: http url: "https://pub.dartlang.org" source: hosted - version: "0.12.2" + version: "0.13.3" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" http_parser: dependency: transitive description: name: http_parser url: "https://pub.dartlang.org" source: hosted - version: "3.1.4" + version: "4.0.0" image: dependency: transitive description: name: image url: "https://pub.dartlang.org" source: hosted - version: "2.1.17" + version: "3.0.2" intl: dependency: transitive description: name: intl url: "https://pub.dartlang.org" source: hosted - version: "0.16.1" - latlong: + version: "0.17.0" + io: + dependency: transitive + description: + name: io + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.3" + js: + dependency: transitive + description: + name: js + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.3" + latlong2: dependency: transitive description: - name: latlong + name: latlong2 url: "https://pub.dartlang.org" source: hosted - version: "0.6.1" + version: "0.8.0" lists: dependency: transitive description: name: lists url: "https://pub.dartlang.org" source: hosted - version: "0.1.6" + version: "1.0.1" logging: dependency: transitive description: name: logging url: "https://pub.dartlang.org" source: hosted - version: "0.11.4" + version: "1.0.1" matcher: dependency: transitive description: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.10-nullsafety.1" + version: "0.12.11" meta: dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.3.0-nullsafety.3" + version: "1.7.0" mgrs_dart: dependency: transitive description: name: mgrs_dart url: "https://pub.dartlang.org" source: hosted - version: "1.0.1" + version: "2.0.0" + mime: + dependency: transitive + description: + name: mime + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + node_preamble: + dependency: transitive + description: + name: node_preamble + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" octo_image: dependency: transitive description: name: octo_image url: "https://pub.dartlang.org" source: hosted - version: "0.3.0" + version: "1.0.0+1" + package_config: + dependency: transitive + description: + name: package_config + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" path: dependency: transitive description: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.8.0-nullsafety.1" + version: "1.8.0" path_provider: dependency: "direct main" description: name: path_provider url: "https://pub.dartlang.org" source: hosted - version: "1.6.18" + version: "2.0.2" path_provider_linux: dependency: transitive description: name: path_provider_linux url: "https://pub.dartlang.org" source: hosted - version: "0.0.1+2" + version: "2.0.2" path_provider_macos: dependency: transitive description: name: path_provider_macos url: "https://pub.dartlang.org" source: hosted - version: "0.0.4+4" + version: "2.0.2" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "1.0.3" + version: "2.0.1" path_provider_windows: dependency: transitive description: name: path_provider_windows url: "https://pub.dartlang.org" source: hosted - version: "0.0.4+1" + version: "2.0.3" pedantic: dependency: transitive description: name: pedantic url: "https://pub.dartlang.org" source: hosted - version: "1.9.2" + version: "1.11.1" petitparser: dependency: transitive description: name: petitparser url: "https://pub.dartlang.org" source: hosted - version: "3.1.0" + version: "4.2.0" platform: dependency: transitive description: name: platform url: "https://pub.dartlang.org" source: hosted - version: "2.2.1" + version: "3.0.2" plugin_platform_interface: dependency: transitive description: name: plugin_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "1.0.2" - positioned_tap_detector: + version: "2.0.1" + pool: dependency: transitive description: - name: positioned_tap_detector + name: pool url: "https://pub.dartlang.org" source: hosted - version: "1.0.3" + version: "1.5.0" + positioned_tap_detector_2: + dependency: transitive + description: + name: positioned_tap_detector_2 + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" process: dependency: transitive description: name: process url: "https://pub.dartlang.org" source: hosted - version: "3.0.13" + version: "4.2.3" proj4dart: dependency: transitive description: name: proj4dart url: "https://pub.dartlang.org" source: hosted - version: "1.0.5" + version: "2.0.0" + pub_semver: + dependency: transitive + description: + name: pub_semver + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" quiver: dependency: transitive description: name: quiver url: "https://pub.dartlang.org" source: hosted - version: "2.1.3" + version: "3.0.1" rxdart: dependency: transitive description: name: rxdart url: "https://pub.dartlang.org" source: hosted - version: "0.24.1" + version: "0.27.1" + shelf: + dependency: transitive + description: + name: shelf + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + shelf_packages_handler: + dependency: transitive + description: + name: shelf_packages_handler + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0" + shelf_static: + dependency: transitive + description: + name: shelf_static + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" sky_engine: dependency: transitive description: flutter source: sdk version: "0.0.99" + source_map_stack_trace: + dependency: transitive + description: + name: source_map_stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + source_maps: + dependency: transitive + description: + name: source_maps + url: "https://pub.dartlang.org" + source: hosted + version: "0.10.10" source_span: dependency: transitive description: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.0-nullsafety.2" + version: "1.8.1" sqflite: dependency: transitive description: name: sqflite url: "https://pub.dartlang.org" source: hosted - version: "1.3.1+1" + version: "2.0.0+4" sqflite_common: dependency: transitive description: name: sqflite_common url: "https://pub.dartlang.org" source: hosted - version: "1.0.2+1" + version: "2.0.1" stack_trace: dependency: transitive description: name: stack_trace url: "https://pub.dartlang.org" source: hosted - version: "1.10.0-nullsafety.1" + version: "1.10.0" stream_channel: dependency: transitive description: name: stream_channel url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.1" + version: "2.1.0" string_scanner: dependency: transitive description: name: string_scanner url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.1" + version: "1.1.0" synchronized: dependency: transitive description: name: synchronized url: "https://pub.dartlang.org" source: hosted - version: "2.2.0+2" + version: "3.0.0" term_glyph: dependency: transitive description: name: term_glyph url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety.1" + version: "1.2.0" + test: + dependency: "direct dev" + description: + name: test + url: "https://pub.dartlang.org" + source: hosted + version: "1.17.11" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.19-nullsafety.2" + version: "0.4.3" + test_core: + dependency: transitive + description: + name: test_core + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.1" transparent_image: dependency: transitive description: name: transparent_image url: "https://pub.dartlang.org" source: hosted - version: "1.0.0" + version: "2.0.0" tuple: dependency: transitive description: name: tuple url: "https://pub.dartlang.org" source: hosted - version: "1.0.3" + version: "2.0.0" typed_data: dependency: transitive description: name: typed_data url: "https://pub.dartlang.org" source: hosted - version: "1.3.0-nullsafety.3" + version: "1.3.0" unicode: dependency: transitive description: name: unicode url: "https://pub.dartlang.org" source: hosted - version: "0.2.4" + version: "0.3.1" uuid: dependency: transitive description: name: uuid url: "https://pub.dartlang.org" source: hosted - version: "2.2.2" - validate: + version: "3.0.4" + vector_math: dependency: transitive description: - name: validate + name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "1.7.0" - vector_math: + version: "2.1.0" + vm_service: dependency: transitive description: - name: vector_math + name: vm_service url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.3" + version: "7.2.0" wakelock: dependency: "direct main" description: name: wakelock url: "https://pub.dartlang.org" source: hosted - version: "0.1.4+2" + version: "0.5.3+3" + wakelock_macos: + dependency: transitive + description: + name: wakelock_macos + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.0+2" + wakelock_platform_interface: + dependency: transitive + description: + name: wakelock_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.1+2" + wakelock_web: + dependency: transitive + description: + name: wakelock_web + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.0+2" + wakelock_windows: + dependency: transitive + description: + name: wakelock_windows + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.0+1" + watcher: + dependency: transitive + description: + name: watcher + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + webkit_inspection_protocol: + dependency: transitive + description: + name: webkit_inspection_protocol + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" win32: dependency: transitive description: name: win32 url: "https://pub.dartlang.org" source: hosted - version: "1.7.3" + version: "2.2.6" wkt_parser: dependency: transitive description: name: wkt_parser url: "https://pub.dartlang.org" source: hosted - version: "1.0.7" + version: "2.0.0" xdg_directories: dependency: transitive description: name: xdg_directories url: "https://pub.dartlang.org" source: hosted - version: "0.1.2" + version: "0.2.0" xml: dependency: transitive description: name: xml url: "https://pub.dartlang.org" source: hosted - version: "4.5.1" + version: "5.2.0" yaml: dependency: transitive description: name: yaml url: "https://pub.dartlang.org" source: hosted - version: "2.2.1" + version: "3.1.0" sdks: - dart: ">=2.10.0-110 <=2.11.0-161.0.dev" - flutter: ">=1.20.0 <2.0.0" + dart: ">=2.13.0 <3.0.0" + flutter: ">=2.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index 37d5074..2f5d3e0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,31 +1,31 @@ name: vfr_light -description: A new Flutter project. +description: A lightweight assistant for pilots flying under VFR. +publish_to: 'none' -publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 environment: - sdk: ">=2.7.0 <3.0.0" + sdk: ">=2.12.0 <3.0.0" dependencies: flutter: sdk: flutter - flutter_map: ^0.10.1 - enviro_sensors: ^1.0.1 - geolocator: ^6.0.0+1 - font_awesome_flutter: ^8.8.1 - path_provider: ^1.6.11 - wakelock: ^0.1.4+2 - flutter_foreground_plugin: ^0.5.0 - cached_network_image: ^2.3.1 - http: ^0.12.2 + flutter_map: ^0.13.1 + environment_sensors: ^0.2.0 + geolocator: ^7.1.0 + font_awesome_flutter: ^9.1.0 + path_provider: ^2.0.1 + wakelock: ^0.5.2 + flutter_background: ^1.0.2+1 + cached_network_image: ^3.0.0 + http: ^0.13.0 dev_dependencies: flutter_test: sdk: flutter - flutter_launcher_icons: "^0.8.0" + flutter_launcher_icons: ^0.9.0 flutter_icons: android: "launcher_icon"