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

Usage widget + GPS Pref

Added:
- GPS Usage pref
- Data footer Widget

Modified:
- PositionHandler is now pref dependent.
parent 262c80ae
This diff is collapsed.
......@@ -21,18 +21,30 @@ class PositionHandler {
/// Retrieve the interval between attempts to acquire GPS signal. (default: 1 second).
PreferencesHandler().getPreferencesInt(PreferenceKeys.GPS__INTERVAL_SECONDS.key).then((value) => this._locationInterval.value = value);
/// Retrieve whether or not the app is allowed to use GPS. (default: false).
PreferencesHandler().getPreferencesBool(PreferenceKeys.GPS__USE_GPS.key).then((value) => this._gpsAllowed.value = value);
/// Checks the [PreferencesHandler] every 20 seconds on Preference updates.
Timer.periodic(Duration(seconds: 20), (Timer t) async {
this._locationAccuracy.value = await PreferencesHandler().getPreferencesInt(PreferenceKeys.GPS__ACCURACY.key);
this._locationInterval.value = await PreferencesHandler().getPreferencesInt(PreferenceKeys.GPS__INTERVAL_SECONDS.key);
this._gpsAllowed.value = await PreferencesHandler().getPreferencesBool(PreferenceKeys.GPS__USE_GPS.key);
});
// Event launcher !
this.getCurrentOrLastPosition().listen((event) => null);
}
/// [BehaviorSubject] concerning the desired location accuracy.
final BehaviorSubject<int> _locationAccuracy = BehaviorSubject<int>();
/// [BehaviorSubject] concerning the time between location acquisition.
final BehaviorSubject<int> _locationInterval = BehaviorSubject<int>();
/// [BehaviorSubject] concerning the authorization to use GPS.
final BehaviorSubject<bool> _gpsAllowed = BehaviorSubject<bool>();
/// Internal static [PositionHandler] reference.
static final PositionHandler _singleton = PositionHandler._internal();
......@@ -99,37 +111,40 @@ class PositionHandler {
Stream<Position> getCurrentOrLastPosition() async* {
// TODO Better Doc
while (true) {
Position currentPosition;
if (!_permissionChecked) {
await this._getGeolocationPermission();
_permissionChecked = true;
}
this._timeSinceLastChange += 1;
if (_gpsAllowed.value ?? false) {
Position currentPosition;
if (!_permissionChecked) {
await this._getGeolocationPermission();
_permissionChecked = true;
}
if (_geolocationStatus == GeolocationStatus.granted) {
currentPosition = await _geolocator.getCurrentPosition(desiredAccuracy: _accuracyFrom(this._locationAccuracy.value ?? 1));
if (currentPosition != this._lastPosition) {
devicePosition.value = currentPosition;
if (_geolocationStatus == GeolocationStatus.granted) {
currentPosition = await _geolocator.getCurrentPosition(desiredAccuracy: _accuracyFrom(this._locationAccuracy.value ?? 1));
if (currentPosition != this._lastPosition) {
devicePosition.value = currentPosition;
}
}
if (currentPosition != null) {
this._lastPosition = currentPosition;
}
if (this._timeSinceLastChange >= 10 && _lastPosition != null && currentPosition == null) {
this._lastPosition = null;
}
if (currentPosition != null) {
this._timeSinceLastChange = 0;
yield currentPosition;
} else if (currentPosition == null && _lastPosition != null) {
yield _lastPosition;
} else {
yield null;
}
}
if (currentPosition != null) {
this._lastPosition = currentPosition;
}
if (this._timeSinceLastChange >= 10 && _lastPosition != null && currentPosition == null) {
this._lastPosition = null;
}
if (currentPosition != null) {
this._timeSinceLastChange = 0;
yield currentPosition;
} else if (currentPosition == null && _lastPosition != null) {
this._timeSinceLastChange += 1;
yield _lastPosition;
} else {
yield null;
}
await Future.delayed(Duration(milliseconds: (this._locationInterval.value ?? 1) * 1000));
}
}
......
......@@ -13,7 +13,7 @@ class Indicator extends StatelessWidget {
const Indicator({
Key key,
this.color,
this.text,
this.text = '',
this.size = 16,
this.textColor = const Color(0xff505050),
}) : super(key: key);
......@@ -36,13 +36,13 @@ class Indicator extends StatelessWidget {
const SizedBox(
width: 4,
),
Text(
(text != null && text != '') ? Text(
text,
style: Theme.of(context).textTheme.bodyText1.copyWith(
fontWeight: FontWeight.bold,
color: textColor
),
)
) : SizedBox()
],
)
);
......
......@@ -32,6 +32,9 @@ dynamic showInitialDialog({@required BuildContext context}) {
// ignore: non_constant_identifier_names
BehaviorSubject<int> _3gToggleObservable = BehaviorSubject<int>();
_3gToggleObservable.value = 0;
BehaviorSubject<int> _gpsToggleObservable = BehaviorSubject<int>();
_gpsToggleObservable.value = 0;
BehaviorSubject<bool> _displayError = BehaviorSubject<bool>();
......@@ -43,8 +46,12 @@ dynamic showInitialDialog({@required BuildContext context}) {
BehaviorSubject<bool> _displayError2 = BehaviorSubject<bool>();
_displayError2.value = false;
_displayError1.stream.listen((bool value) => _displayError.value = value || _displayError2.value);
_displayError2.stream.listen((bool value) => _displayError.value = _displayError1.value || value);
BehaviorSubject<bool> _displayError3 = BehaviorSubject<bool>();
_displayError3.value = false;
_displayError1.stream.listen((bool value) => _displayError.value = value || _displayError2.value || _displayError3.value);
_displayError2.stream.listen((bool value) => _displayError.value = _displayError1.value || value || _displayError3.value);
_displayError3.stream.listen((bool value) => _displayError.value = _displayError1.value || _displayError2.value || value);
return showDialog(
context: context,
......@@ -86,7 +93,7 @@ dynamic showInitialDialog({@required BuildContext context}) {
builder: (context, snapshot) {
if (snapshot.data) {
return Text(
'Both settings need to be set',
'All three settings need to be set',
style: Theme.of(context).textTheme.subtitle1.copyWith(color: Colors.red)
);
} else {
......@@ -159,6 +166,38 @@ dynamic showInitialDialog({@required BuildContext context}) {
);
}
),
Divider(color: Colors.grey[600], indent: 20, endIndent: 20, height: 20,),
StreamBuilder<bool>(
stream: _displayError3.stream,
initialData: false,
builder: (context, snapshot) {
return Container(
alignment: Alignment.center,
padding: EdgeInsets.symmetric(vertical: 5, horizontal: 5),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: Colors.grey[300],
border: Border.all(color: (snapshot.data) ? Colors.red : Colors.transparent)
),
child: Column(
children: [
Text(
'Use GPS for localization',
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.headline6.copyWith(fontSize: 16),
),
Divider(thickness: 0, indent: 20, endIndent: 20, height: 10,),
Container(
padding: EdgeInsets.symmetric(vertical: 5, horizontal: 3),
child: TristateToggleWidget(
observable: _gpsToggleObservable,
)
),
],
),
);
}
),
Divider(height: 30,),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
......@@ -175,16 +214,19 @@ dynamic showInitialDialog({@required BuildContext context}) {
action: () async {
int _wantsToShare = _shareToggleObservable.value;
int _wantsTo3g = _3gToggleObservable.value;
int _wantsToGPS = _gpsToggleObservable.value;
_displayError1.value = _wantsToShare == 0;
_displayError2.value = _wantsTo3g == 0;
_displayError3.value = _wantsToGPS == 0;
if (_wantsToShare == 0 || _wantsTo3g == 0) {
if (_wantsToShare == 0 || _wantsTo3g == 0 || _wantsToGPS == 0) {
return;
}
await PreferencesHandler().setPreferencesBool(PreferenceKeys.GEN__SHARE.key, _wantsToShare == 1);
await PreferencesHandler().setPreferencesBool(PreferenceKeys.NET__USE_MOBILE_NET.key, _wantsTo3g == 1);
await PreferencesHandler().setPreferencesBool(PreferenceKeys.GPS__USE_GPS.key, _wantsToGPS == 1);
Navigator.of(context).pop(true);
},
......@@ -204,9 +246,11 @@ dynamic showInitialDialog({@required BuildContext context}) {
await _shareToggleObservable.close();
await _3gToggleObservable.close();
await _gpsToggleObservable.close();
await _displayError.close();
await _displayError1.close();
await _displayError2.close();
await _displayError3.close();
Navigator.of(context).popUntil((Route<dynamic> route) => route.isFirst);
});
......
import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:intersperse/intersperse.dart';
......@@ -6,10 +8,26 @@ import 'package:logair_application/logic/handlers/position_handler.dart';
import 'package:logair_application/logic/handlers/preference_handler.dart';
import 'package:logair_application/ui/components/body/overview/overview_widgets/common/overview_data_cell.dart';
import 'package:logair_application/utils/decorations.dart';
import 'package:logair_application/utils/enums/preference_keys.dart';
import 'package:rxdart/rxdart.dart';
/// [Widget] recapping the various GPS related statistics for quick lookup.
class MapPositionOverviewWidget extends StatelessWidget {
MapPositionOverviewWidget({Key key}) : super(key: key);
/// [BehaviorSubject] concerning the authorization to use GPS.
final BehaviorSubject<bool> _gpsAllowed = BehaviorSubject<bool>();
MapPositionOverviewWidget({Key key}) : super(key: key) {
_gpsAllowed.value = false;
/// Retrieve whether or not the app is allowed to use GPS. (default: false).
PreferencesHandler().getPreferencesBool(PreferenceKeys.GPS__USE_GPS.key).then((value) => this._gpsAllowed.value = value);
/// Checks the [PreferencesHandler] every 20 seconds on Preference updates.
Timer.periodic(Duration(seconds: 10), (Timer t) async {
this._gpsAllowed.value = await PreferencesHandler().getPreferencesBool(PreferenceKeys.GPS__USE_GPS.key);
});
}
/// Builds a [Text] containing the time at which the position was last acquired.
StreamBuilder<int> _buildPositionLastAcquiredWidget() => StreamBuilder<int>(
......@@ -49,27 +67,38 @@ class MapPositionOverviewWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Column(
children: intersperse(SizedBox(height: 10), [
Container(
width: 40,
height: 40,
decoration: iconContainerNeumorphicDecoration,
child: Icon(
Icons.location_on,
color: Colors.white,
size: IconTheme.of(context).size,
child: StreamBuilder<bool>(
stream: _gpsAllowed.stream,
initialData: false,
builder: (context, snapshot) => Column(
children: intersperse(SizedBox(height: 10), [
Container(
width: 40,
height: 40,
decoration: iconContainerNeumorphicDecoration,
child: Icon(
(snapshot.data) ? Icons.location_on : Icons.location_off,
color: Colors.white,
size: IconTheme.of(context).size,
),
),
),
OverviewDataCell(
label: 'Position last acquired',
builder: _buildPositionLastAcquiredWidget(),
),
OverviewDataCell(
label: 'Location Accuracy',
builder: _buildLocationAccuracyWidget(),
),
]).toList()
(snapshot.data) ? OverviewDataCell(
label: 'Position last acquired',
builder: _buildPositionLastAcquiredWidget(),
) : SizedBox(),
(snapshot.data) ? OverviewDataCell(
label: 'Location Accuracy',
builder: _buildLocationAccuracyWidget(),
) : SizedBox(),
(!snapshot.data) ? OverviewDataCell(
label: 'GPS is disabled in the application preferences',
builder: StreamBuilder(
stream: BehaviorSubject().stream,
builder: (context, snapshot) => SizedBox(),
),
) : SizedBox(),
]).toList()
)
)
);
}
......
......@@ -56,6 +56,7 @@ class _PreferencesWidgetState extends State<PreferencesWidget> {
final TextEditingController _dbMaxAgeController = TextEditingController();
final BehaviorSubject<bool> _gpsUseGPS = BehaviorSubject<bool>();
final BehaviorSubject<int> _gpsAccuracyController = BehaviorSubject<int>();
final TextEditingController _gpsIntervalController = TextEditingController();
......@@ -82,6 +83,7 @@ class _PreferencesWidgetState extends State<PreferencesWidget> {
_dbMaxAgeController.dispose();
_gpsUseGPS.close();
_gpsAccuracyController.close();
_gpsIntervalController.dispose();
......@@ -167,7 +169,7 @@ class _PreferencesWidgetState extends State<PreferencesWidget> {
),
ToggleTile(
controller: this._netPushOnMobile,
labelText: 'Push on 3G/4G',
labelText: 'Push on 3G/4G/5G',
prefsKey: PreferenceKeys.NET__USE_MOBILE_NET.key
)
]
......@@ -198,6 +200,11 @@ class _PreferencesWidgetState extends State<PreferencesWidget> {
title: 'Position',
icon: Icons.location_on,
children: [
ToggleTile(
controller: this._gpsUseGPS,
labelText: 'Use GPS',
prefsKey: PreferenceKeys.GPS__USE_GPS.key
),
LocationSelector(
controller: this._gpsAccuracyController
),
......@@ -395,6 +402,7 @@ class _PreferencesWidgetState extends State<PreferencesWidget> {
await PreferencesHandler().setPreferencesInt(PreferenceKeys.DB__MAX_AGE.key, int.parse(_dbMaxAgeController.text));
await PreferencesHandler().setPreferencesBool(PreferenceKeys.GPS__USE_GPS.key, _gpsUseGPS.value);
await PreferencesHandler().setPreferencesInt(PreferenceKeys.GPS__ACCURACY.key, _gpsAccuracyController.value);
await PreferencesHandler().setPreferencesInt(PreferenceKeys.GPS__INTERVAL_SECONDS.key, int.parse(_gpsIntervalController.text));
......
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:logair_application/ui/components/footer/main_footer_components/data_usage_widget.dart';
/// [Widget] regrouping certain status elements from the home carousel, for quick lookup.
///
......@@ -14,6 +15,7 @@ class MainFooter extends StatelessWidget {
child: child,
);
}
@override
Widget build(BuildContext context) {
return Container(
......@@ -41,7 +43,7 @@ class MainFooter extends StatelessWidget {
child: _container(Icon(Icons.navigation))
),
Expanded(
child: _container(Icon(Icons.data_usage))
child: _container(DataUsageWidget())
),
Expanded(
child: _container(
......
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:logair_application/logic/data_packet.dart';
import 'package:logair_application/logic/handlers/main_database_handler.dart';
import 'package:logair_application/ui/components/body/data/graph/indicator.dart';
import 'package:logair_application/utils/enums/pm_symbol.dart';
class DataUsageWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return StreamBuilder<DataPacket>(
stream: MainDatabaseHandler().getLatestData(),
initialData: DataPacket.fromData(0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, ''),
builder: (context, snapshot) {
DataPacket data = snapshot.data;
return Stack(
alignment: Alignment.center,
children: [
Icon(Icons.data_usage, size: IconTheme.of(context).size, color: Colors.grey[800]),
Column(
children: [
Spacer(),
Row(
children: [
Spacer(),
Indicator(
color: (data != null && data.pm1 != null && data.pm1 != -1) ?
PMSymbol.PM1.colorizeValue(data.pm1) :
Colors.grey[400],
),
Spacer(flex: 2,),
Indicator(
color: (data != null && data.pm2_5 != null && data.pm2_5 != -1) ?
PMSymbol.PM2_5.colorizeValue(data.pm2_5) :
Colors.grey[400],
),
Spacer(),
]
),
Spacer(flex: 2,),
Row(
children: [
Spacer(flex: 2,),
Indicator(
color: (data != null && data.pm4 != null && data.pm4 != -1) ?
PMSymbol.PM4.colorizeValue(data.pm4) :
Colors.grey[400],
),
Spacer(),
Indicator(
color: (data != null && data.pm10 != null && data.pm10 != -1) ?
PMSymbol.PM10.colorizeValue(data.pm10) :
Colors.grey[400],
),
Spacer(flex: 2,),
]
),
Spacer(),
],
)
]
);
}
);
}
}
\ No newline at end of file
......@@ -48,7 +48,9 @@ class PreferenceKeys {
static const DB__MAX_AGE // How long should the database data remain stored (regardless of upload status) ?
= PreferenceKeys._internal('DB.MAX_AGE_DAYS', int, 15);
static const GPS__USE_GPS // How accurate should the GPS signal attempt to be ?
= PreferenceKeys._internal('GPS.USE_GPS', bool, false);
static const GPS__ACCURACY // How accurate should the GPS signal attempt to be ?
= PreferenceKeys._internal('GPS.ACCURACY', int, 2);
static const GPS__INTERVAL_SECONDS // How often should the GPS attempt to localize the device ?
......@@ -87,6 +89,7 @@ class PreferenceKeys {
NET__TIME_TO_PUSH,
NET__USE_MOBILE_NET,
DB__MAX_AGE,
GPS__USE_GPS,
GPS__ACCURACY,
GPS__INTERVAL_SECONDS,
LOI__FOCUS,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment