fix: critical bugs preventing location reporting to server
- Send config to native before starting tracking (SharedPreferences sync) - Register ConnectivityReceiver for network state detection - Fix Settings number fields using TextEditingControllers - Cancel stream subscription on widget dispose (memory leak) - Replace WorkManager with AlarmManager for heartbeat - Add log cleanup for logs older than 24 hours - Change HTTP method from GET to POST These fixes resolve the 'device always offline' issue where: 1. Config was not being sent to native service 2. ConnectivityReceiver was never registered 3. Settings number fields were not saving 4. Heartbeat never fired due to WorkManager process isolation 5. Server expected POST not GET
This commit is contained in:
parent
3ae8bf00c1
commit
bbd51d1c35
10 changed files with 1200 additions and 167 deletions
|
|
@ -12,10 +12,10 @@ class SettingsScreen extends StatefulWidget {
|
|||
class _SettingsScreenState extends State<SettingsScreen> {
|
||||
late TextEditingController _serverUrlController;
|
||||
late TextEditingController _deviceIdController;
|
||||
late TextEditingController _distanceFilterController;
|
||||
late TextEditingController _intervalController;
|
||||
late TextEditingController _heartbeatController;
|
||||
late int _accuracy;
|
||||
late int _distanceFilter;
|
||||
late int _interval;
|
||||
late int _heartbeat;
|
||||
late bool _offlineBuffer;
|
||||
late bool _stopDetection;
|
||||
|
||||
|
|
@ -24,10 +24,10 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||
super.initState();
|
||||
_serverUrlController = TextEditingController(text: Preferences.serverUrl);
|
||||
_deviceIdController = TextEditingController(text: Preferences.deviceId);
|
||||
_distanceFilterController = TextEditingController(text: Preferences.distanceFilter.toString());
|
||||
_intervalController = TextEditingController(text: Preferences.interval.toString());
|
||||
_heartbeatController = TextEditingController(text: Preferences.heartbeat.toString());
|
||||
_accuracy = Preferences.accuracy;
|
||||
_distanceFilter = Preferences.distanceFilter;
|
||||
_interval = Preferences.interval;
|
||||
_heartbeat = Preferences.heartbeat;
|
||||
_offlineBuffer = Preferences.offlineBuffer;
|
||||
_stopDetection = Preferences.stopDetection;
|
||||
}
|
||||
|
|
@ -36,16 +36,23 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||
void dispose() {
|
||||
_serverUrlController.dispose();
|
||||
_deviceIdController.dispose();
|
||||
_distanceFilterController.dispose();
|
||||
_intervalController.dispose();
|
||||
_heartbeatController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _saveSettings() async {
|
||||
final distanceFilter = int.tryParse(_distanceFilterController.text) ?? 75;
|
||||
final interval = int.tryParse(_intervalController.text) ?? 300;
|
||||
final heartbeat = int.tryParse(_heartbeatController.text) ?? 60;
|
||||
|
||||
await Preferences.setServerUrl(_serverUrlController.text);
|
||||
await Preferences.setDeviceId(_deviceIdController.text);
|
||||
await Preferences.setAccuracy(_accuracy);
|
||||
await Preferences.setDistanceFilter(_distanceFilter);
|
||||
await Preferences.setInterval(_interval);
|
||||
await Preferences.setHeartbeat(_heartbeat);
|
||||
await Preferences.setDistanceFilter(distanceFilter);
|
||||
await Preferences.setInterval(interval);
|
||||
await Preferences.setHeartbeat(heartbeat);
|
||||
await Preferences.setOfflineBuffer(_offlineBuffer);
|
||||
await Preferences.setStopDetection(_stopDetection);
|
||||
|
||||
|
|
@ -53,9 +60,9 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||
'serverUrl': _serverUrlController.text,
|
||||
'deviceId': _deviceIdController.text,
|
||||
'accuracy': _accuracy,
|
||||
'distanceFilter': _distanceFilter,
|
||||
'interval': _interval,
|
||||
'heartbeat': _heartbeat,
|
||||
'distanceFilter': distanceFilter,
|
||||
'interval': interval,
|
||||
'heartbeat': heartbeat,
|
||||
'offlineBuffer': _offlineBuffer,
|
||||
'stopDetection': _stopDetection,
|
||||
});
|
||||
|
|
@ -80,9 +87,9 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||
const SizedBox(height: 16),
|
||||
_buildSectionHeader('Location'),
|
||||
_buildAccuracyDropdown(),
|
||||
_buildNumberField('Distance Filter (m)', _distanceFilter, 0, 1000),
|
||||
_buildNumberField('Update Interval (s)', _interval, 30, 3600),
|
||||
_buildNumberField('Heartbeat (s)', _heartbeat, 60, 3600),
|
||||
_buildNumberField('Distance Filter (m)', _distanceFilterController, 0, 1000),
|
||||
_buildNumberField('Update Interval (s)', _intervalController, 30, 3600),
|
||||
_buildNumberField('Heartbeat (s)', _heartbeatController, 60, 3600),
|
||||
const SizedBox(height: 16),
|
||||
_buildSectionHeader('Advanced'),
|
||||
SwitchListTile(
|
||||
|
|
@ -146,30 +153,11 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||
);
|
||||
}
|
||||
|
||||
Widget _buildNumberField(String label, int value, int min, int max) {
|
||||
return TextFormField(
|
||||
initialValue: value.toString(),
|
||||
Widget _buildNumberField(String label, TextEditingController controller, int min, int max) {
|
||||
return TextField(
|
||||
controller: controller,
|
||||
decoration: InputDecoration(labelText: label),
|
||||
keyboardType: TextInputType.number,
|
||||
validator: (v) {
|
||||
final parsed = int.tryParse(v ?? '');
|
||||
if (parsed == null || parsed < min || parsed > max) {
|
||||
return 'Must be between $min and $max';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
onSaved: (v) {
|
||||
final parsed = int.tryParse(v ?? '');
|
||||
if (parsed != null) {
|
||||
if (label.contains('Distance')) {
|
||||
_distanceFilter = parsed;
|
||||
} else if (label.contains('Interval')) {
|
||||
_interval = parsed;
|
||||
} else if (label.contains('Heartbeat')) {
|
||||
_heartbeat = parsed;
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue